diff options
author | Russ Cox <rsc@swtch.com> | 2008-08-03 07:42:27 -0700 |
---|---|---|
committer | Russ Cox <rsc@swtch.com> | 2008-08-03 07:42:27 -0700 |
commit | 18824b586835525594cde126fbc90b8281d5af8b (patch) | |
tree | e8b69c05eda4543b6d2f3d30777abe6109b48b7f /src/cmd/smugfs/http.c | |
parent | 3d36f4437348227c5bad62587dc12b5fd4a3e95e (diff) | |
download | plan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.gz plan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.bz2 plan9port-18824b586835525594cde126fbc90b8281d5af8b.zip |
smugfs(4): new program
Diffstat (limited to 'src/cmd/smugfs/http.c')
-rw-r--r-- | src/cmd/smugfs/http.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/cmd/smugfs/http.c b/src/cmd/smugfs/http.c new file mode 100644 index 00000000..9cf7f1d0 --- /dev/null +++ b/src/cmd/smugfs/http.c @@ -0,0 +1,237 @@ +#include "a.h" + +static char* +haveheader(char *buf, int n) +{ + int i; + + for(i=0; i<n; i++){ + if(buf[i] == '\n'){ + if(i+2 < n && buf[i+1] == '\r' && buf[i+2] == '\n') + return buf+i+3; + if(i+1 < n && buf[i+1] == '\n') + return buf+i+2; + } + } + return 0; +} + +static int +parseheader(char *buf, int n, HTTPHeader *hdr) +{ + int nline; + char *data, *ebuf, *p, *q, *next; + + memset(hdr, 0, sizeof *hdr); + ebuf = buf+n; + data = haveheader(buf, n); + if(data == nil) + return -1; + + data[-1] = 0; + if(data[-2] == '\r') + data[-2] = 0; + if(chattyhttp > 1){ + fprint(2, "--HTTP Response Header:\n"); + fprint(2, "%s\n", buf); + fprint(2, "--\n"); + } + nline = 0; + for(p=buf; *p; p=next, nline++){ + q = strchr(p, '\n'); + if(q){ + next = q+1; + *q = 0; + if(q > p && q[-1] == '\r') + q[-1] = 0; + }else + next = p+strlen(p); + if(nline == 0){ + if(memcmp(p, "HTTP/", 5) != 0){ + werrstr("invalid HTTP version: %.10s", p); + return -1; + } + q = strchr(p, ' '); + if(q == nil){ + werrstr("invalid HTTP version"); + return -1; + } + *q++ = 0; + strncpy(hdr->proto, p, sizeof hdr->proto); + hdr->proto[sizeof hdr->proto-1] = 0; + while(*q == ' ') + q++; + if(*q < '0' || '9' < *q){ + werrstr("invalid HTTP response code"); + return -1; + } + p = q; + q = strchr(p, ' '); + if(q == nil) + q = p+strlen(p); + else + *q++ = 0; + hdr->code = strtol(p, &p, 10); + if(*p != 0) + return -1; + while(*q == ' ') + q++; + strncpy(hdr->codedesc, q, sizeof hdr->codedesc); + hdr->codedesc[sizeof hdr->codedesc-1] = 0; + continue; + } + q = strchr(p, ':'); + if(q == nil) + continue; + *q++ = 0; + while(*q != 0 && (*q == ' ' || *q == '\t')) + q++; + if(cistrcmp(p, "Content-Type") == 0){ + strncpy(hdr->contenttype, q, sizeof hdr->contenttype); + hdr->contenttype[sizeof hdr->contenttype-1] = 0; + continue; + } + if(cistrcmp(p, "Content-Length") == 0 && '0' <= *q && *q <= '9'){ + hdr->contentlength = strtoll(q, 0, 10); + continue; + } + } + if(nline < 1){ + werrstr("no header"); + return -1; + } + + memmove(buf, data, ebuf - data); + return ebuf - data; +} + +static char* +genhttp(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int wfd, int rfd, vlong rtotal) +{ + int n, m, total, want; + char buf[8192], *data; + Pfd *fd; + + if(chattyhttp > 1){ + fprint(2, "--HTTP Request:\n"); + fprint(2, "%s", req); + fprint(2, "--\n"); + } + fd = proto->connect(host); + if(fd == nil){ + if(chattyhttp > 0) + fprint(2, "connect %s: %r\n", host); + return nil; + } + + n = strlen(req); + if(proto->write(fd, req, n) != n){ + if(chattyhttp > 0) + fprint(2, "write %s: %r\n", host); + proto->close(fd); + return nil; + } + + if(rfd >= 0){ + while(rtotal > 0){ + m = sizeof buf; + if(m > rtotal) + m = rtotal; + if((n = read(rfd, buf, m)) <= 0){ + fprint(2, "read: missing data\n"); + proto->close(fd); + return nil; + } + if(proto->write(fd, buf, n) != n){ + fprint(2, "write data: %r\n"); + proto->close(fd); + return nil; + } + rtotal -= n; + } + } + + total = 0; + while(!haveheader(buf, total)){ + n = proto->read(fd, buf+total, sizeof buf-total); + if(n <= 0){ + if(chattyhttp > 0) + fprint(2, "read missing header\n"); + proto->close(fd); + return nil; + } + total += n; + } + + n = parseheader(buf, total, hdr); + if(n < 0){ + fprint(2, "failed response parse: %r\n"); + proto->close(fd); + return nil; + } + if(hdr->contentlength >= MaxResponse){ + werrstr("response too long"); + proto->close(fd); + return nil; + } + if(hdr->contentlength >= 0 && n > hdr->contentlength) + n = hdr->contentlength; + want = sizeof buf; + data = nil; + total = 0; + goto didread; + + while(want > 0 && (n = proto->read(fd, buf, want)) > 0){ + didread: + if(wfd >= 0){ + if(writen(wfd, buf, n) < 0){ + proto->close(fd); + werrstr("write error"); + return nil; + } + }else{ + data = erealloc(data, total+n); + memmove(data+total, buf, n); + } + total += n; + if(total > MaxResponse){ + proto->close(fd); + werrstr("response too long"); + return nil; + } + if(hdr->contentlength >= 0 && total + want > hdr->contentlength) + want = hdr->contentlength - total; + } + proto->close(fd); + + if(hdr->contentlength >= 0 && total != hdr->contentlength){ + werrstr("got wrong content size %d %d", total, hdr->contentlength); + return nil; + } + hdr->contentlength = total; + if(wfd >= 0) + return (void*)1; + else{ + data = erealloc(data, total+1); + data[total] = 0; + } + return data; +} + +char* +httpreq(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int rfd, vlong rlength) +{ + return genhttp(proto, host, req, hdr, -1, rfd, rlength); +} + +int +httptofile(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int fd) +{ + if(fd < 0){ + werrstr("bad fd"); + return -1; + } + if(genhttp(proto, host, req, hdr, fd, -1, 0) == nil) + return -1; + return 0; +} |