http.c (4762B)
1 #include "a.h" 2 3 static char* 4 haveheader(char *buf, int n) 5 { 6 int i; 7 8 for(i=0; i<n; i++){ 9 if(buf[i] == '\n'){ 10 if(i+2 < n && buf[i+1] == '\r' && buf[i+2] == '\n') 11 return buf+i+3; 12 if(i+1 < n && buf[i+1] == '\n') 13 return buf+i+2; 14 } 15 } 16 return 0; 17 } 18 19 static int 20 parseheader(char *buf, int n, HTTPHeader *hdr) 21 { 22 int nline; 23 char *data, *ebuf, *p, *q, *next; 24 25 memset(hdr, 0, sizeof *hdr); 26 ebuf = buf+n; 27 data = haveheader(buf, n); 28 if(data == nil) 29 return -1; 30 31 data[-1] = 0; 32 if(data[-2] == '\r') 33 data[-2] = 0; 34 if(chattyhttp > 1){ 35 fprint(2, "--HTTP Response Header:\n"); 36 fprint(2, "%s\n", buf); 37 fprint(2, "--\n"); 38 } 39 nline = 0; 40 for(p=buf; *p; p=next, nline++){ 41 q = strchr(p, '\n'); 42 if(q){ 43 next = q+1; 44 *q = 0; 45 if(q > p && q[-1] == '\r') 46 q[-1] = 0; 47 }else 48 next = p+strlen(p); 49 if(nline == 0){ 50 if(memcmp(p, "HTTP/", 5) != 0){ 51 werrstr("invalid HTTP version: %.10s", p); 52 return -1; 53 } 54 q = strchr(p, ' '); 55 if(q == nil){ 56 werrstr("invalid HTTP version"); 57 return -1; 58 } 59 *q++ = 0; 60 strncpy(hdr->proto, p, sizeof hdr->proto); 61 hdr->proto[sizeof hdr->proto-1] = 0; 62 while(*q == ' ') 63 q++; 64 if(*q < '0' || '9' < *q){ 65 werrstr("invalid HTTP response code"); 66 return -1; 67 } 68 p = q; 69 q = strchr(p, ' '); 70 if(q == nil) 71 q = p+strlen(p); 72 else 73 *q++ = 0; 74 hdr->code = strtol(p, &p, 10); 75 if(*p != 0) 76 return -1; 77 while(*q == ' ') 78 q++; 79 strncpy(hdr->codedesc, q, sizeof hdr->codedesc); 80 hdr->codedesc[sizeof hdr->codedesc-1] = 0; 81 continue; 82 } 83 q = strchr(p, ':'); 84 if(q == nil) 85 continue; 86 *q++ = 0; 87 while(*q != 0 && (*q == ' ' || *q == '\t')) 88 q++; 89 if(cistrcmp(p, "Content-Type") == 0){ 90 strncpy(hdr->contenttype, q, sizeof hdr->contenttype); 91 hdr->contenttype[sizeof hdr->contenttype-1] = 0; 92 continue; 93 } 94 if(cistrcmp(p, "Content-Length") == 0 && '0' <= *q && *q <= '9'){ 95 hdr->contentlength = strtoll(q, 0, 10); 96 continue; 97 } 98 } 99 if(nline < 1){ 100 werrstr("no header"); 101 return -1; 102 } 103 104 memmove(buf, data, ebuf - data); 105 return ebuf - data; 106 } 107 108 static char* 109 genhttp(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int wfd, int rfd, vlong rtotal) 110 { 111 int n, m, total, want; 112 char buf[8192], *data; 113 Pfd *fd; 114 115 if(chattyhttp > 1){ 116 fprint(2, "--HTTP Request:\n"); 117 fprint(2, "%s", req); 118 fprint(2, "--\n"); 119 } 120 fd = proto->connect(host); 121 if(fd == nil){ 122 if(chattyhttp > 0) 123 fprint(2, "connect %s: %r\n", host); 124 return nil; 125 } 126 127 n = strlen(req); 128 if(proto->write(fd, req, n) != n){ 129 if(chattyhttp > 0) 130 fprint(2, "write %s: %r\n", host); 131 proto->close(fd); 132 return nil; 133 } 134 135 if(rfd >= 0){ 136 while(rtotal > 0){ 137 m = sizeof buf; 138 if(m > rtotal) 139 m = rtotal; 140 if((n = read(rfd, buf, m)) <= 0){ 141 fprint(2, "read: missing data\n"); 142 proto->close(fd); 143 return nil; 144 } 145 if(proto->write(fd, buf, n) != n){ 146 fprint(2, "write data: %r\n"); 147 proto->close(fd); 148 return nil; 149 } 150 rtotal -= n; 151 } 152 } 153 154 total = 0; 155 while(!haveheader(buf, total)){ 156 n = proto->read(fd, buf+total, sizeof buf-total); 157 if(n <= 0){ 158 if(chattyhttp > 0) 159 fprint(2, "read missing header\n"); 160 proto->close(fd); 161 return nil; 162 } 163 total += n; 164 } 165 166 n = parseheader(buf, total, hdr); 167 if(n < 0){ 168 fprint(2, "failed response parse: %r\n"); 169 proto->close(fd); 170 return nil; 171 } 172 if(hdr->contentlength >= MaxResponse){ 173 werrstr("response too long"); 174 proto->close(fd); 175 return nil; 176 } 177 if(hdr->contentlength >= 0 && n > hdr->contentlength) 178 n = hdr->contentlength; 179 want = sizeof buf; 180 data = nil; 181 total = 0; 182 goto didread; 183 184 while(want > 0 && (n = proto->read(fd, buf, want)) > 0){ 185 didread: 186 if(wfd >= 0){ 187 if(writen(wfd, buf, n) < 0){ 188 proto->close(fd); 189 werrstr("write error"); 190 return nil; 191 } 192 }else{ 193 data = erealloc(data, total+n); 194 memmove(data+total, buf, n); 195 } 196 total += n; 197 if(total > MaxResponse){ 198 proto->close(fd); 199 werrstr("response too long"); 200 return nil; 201 } 202 if(hdr->contentlength >= 0 && total + want > hdr->contentlength) 203 want = hdr->contentlength - total; 204 } 205 proto->close(fd); 206 207 if(hdr->contentlength >= 0 && total != hdr->contentlength){ 208 werrstr("got wrong content size %d %d", total, hdr->contentlength); 209 return nil; 210 } 211 hdr->contentlength = total; 212 if(wfd >= 0) 213 return (void*)1; 214 else{ 215 data = erealloc(data, total+1); 216 data[total] = 0; 217 } 218 return data; 219 } 220 221 char* 222 httpreq(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int rfd, vlong rlength) 223 { 224 return genhttp(proto, host, req, hdr, -1, rfd, rlength); 225 } 226 227 int 228 httptofile(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int fd) 229 { 230 if(fd < 0){ 231 werrstr("bad fd"); 232 return -1; 233 } 234 if(genhttp(proto, host, req, hdr, fd, -1, 0) == nil) 235 return -1; 236 return 0; 237 }