Blob


1 #include "a.h"
3 static char*
4 haveheader(char *buf, int n)
5 {
6 int i;
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 }
19 static int
20 parseheader(char *buf, int n, HTTPHeader *hdr)
21 {
22 int nline;
23 char *data, *ebuf, *p, *q, *next;
25 memset(hdr, 0, sizeof *hdr);
26 ebuf = buf+n;
27 data = haveheader(buf, n);
28 if(data == nil)
29 return -1;
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;
104 memmove(buf, data, ebuf - data);
105 return ebuf - data;
108 static char*
109 genhttp(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int wfd, int rfd, vlong rtotal)
111 int n, m, total, want;
112 char buf[8192], *data;
113 Pfd *fd;
115 if(chattyhttp > 1){
116 fprint(2, "--HTTP Request:\n");
117 fprint(2, "%s", req);
118 fprint(2, "--\n");
120 fd = proto->connect(host);
121 if(fd == nil){
122 if(chattyhttp > 0)
123 fprint(2, "connect %s: %r\n", host);
124 return nil;
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;
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;
145 if(proto->write(fd, buf, n) != n){
146 fprint(2, "write data: %r\n");
147 proto->close(fd);
148 return nil;
150 rtotal -= n;
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;
163 total += n;
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;
172 if(hdr->contentlength >= MaxResponse){
173 werrstr("response too long");
174 proto->close(fd);
175 return nil;
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;
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;
192 }else{
193 data = erealloc(data, total+n);
194 memmove(data+total, buf, n);
196 total += n;
197 if(total > MaxResponse){
198 proto->close(fd);
199 werrstr("response too long");
200 return nil;
202 if(hdr->contentlength >= 0 && total + want > hdr->contentlength)
203 want = hdr->contentlength - total;
205 proto->close(fd);
207 if(hdr->contentlength >= 0 && total != hdr->contentlength){
208 werrstr("got wrong content size %d %d", total, hdr->contentlength);
209 return nil;
211 hdr->contentlength = total;
212 if(wfd >= 0)
213 return (void*)1;
214 else{
215 data = erealloc(data, total+1);
216 data[total] = 0;
218 return data;
221 char*
222 httpreq(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int rfd, vlong rlength)
224 return genhttp(proto, host, req, hdr, -1, rfd, rlength);
227 int
228 httptofile(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int fd)
230 if(fd < 0){
231 werrstr("bad fd");
232 return -1;
234 if(genhttp(proto, host, req, hdr, fd, -1, 0) == nil)
235 return -1;
236 return 0;