|  | #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; | 
|  | } |