1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ip.h> 5 #include <plumb.h> 6 #include <thread.h> 7 #include <fcall.h> 8 #include <9p.h> 9 #include "dat.h" 10 #include "fns.h" 11 12 char PostContentType[] = "application/octet-stream"; 13 int httpdebug; 14 15 typedef struct HttpState HttpState; 16 struct HttpState 17 { 18 int fd; 19 Client *c; 20 char *location; 21 char *setcookie; 22 char *netaddr; 23 Ibuf b; 24 }; 25 26 static void 27 location(HttpState *hs, char *value) 28 { 29 if(hs->location == nil) 30 hs->location = estrdup(value); 31 } 32 33 static void 34 contenttype(HttpState *hs, char *value) 35 { 36 if(hs->c->contenttype == nil) 37 hs->c->contenttype = estrdup(value); 38 } 39 40 static void 41 setcookie(HttpState *hs, char *value) 42 { 43 char *s, *t; 44 Fmt f; 45 46 s = hs->setcookie; 47 fmtstrinit(&f); 48 if(s) 49 fmtprint(&f, "%s", s); 50 fmtprint(&f, "set-cookie: "); 51 fmtprint(&f, "%s", value); 52 fmtprint(&f, "\n"); 53 t = fmtstrflush(&f); 54 if(t){ 55 free(s); 56 hs->setcookie = t; 57 } 58 } 59 60 struct { 61 char *name; /* Case-insensitive */ 62 void (*fn)(HttpState *hs, char *value); 63 } hdrtab[] = { 64 { "location:", location }, 65 { "content-type:", contenttype }, 66 { "set-cookie:", setcookie }, 67 }; 68 69 static int 70 httprcode(HttpState *hs) 71 { 72 int n; 73 char *p; 74 char buf[256]; 75 76 n = readline(&hs->b, buf, sizeof(buf)-1); 77 if(n <= 0) 78 return n; 79 if(httpdebug) 80 fprint(2, "-> %s\n", buf); 81 p = strchr(buf, ' '); 82 if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){ 83 werrstr("bad response from server"); 84 return -1; 85 } 86 buf[n] = 0; 87 return atoi(p+1); 88 } 89 90 /* 91 * read a single mime header, collect continuations. 92 * 93 * this routine assumes that there is a blank line twixt 94 * the header and the message body, otherwise bytes will 95 * be lost. 96 */ 97 static int 98 getheader(HttpState *hs, char *buf, int n) 99 { 100 char *p, *e; 101 int i; 102 103 n--; 104 p = buf; 105 for(e = p + n; ; p += i){ 106 i = readline(&hs->b, p, e-p); 107 if(i < 0) 108 return i; 109 110 if(p == buf){ 111 /* first line */ 112 if(strchr(buf, ':') == nil) 113 break; /* end of headers */ 114 } else { 115 /* continuation line */ 116 if(*p != ' ' && *p != '\t'){ 117 unreadline(&hs->b, p); 118 *p = 0; 119 break; /* end of this header */ 120 } 121 } 122 } 123 124 if(httpdebug) 125 fprint(2, "-> %s\n", buf); 126 return p-buf; 127 } 128 129 static int 130 httpheaders(HttpState *hs) 131 { 132 char buf[2048]; 133 char *p; 134 int i, n; 135 136 for(;;){ 137 n = getheader(hs, buf, sizeof(buf)); 138 if(n < 0) 139 return -1; 140 if (n == 0) 141 return 0; 142 //print("http header: '%.*s'\n", n, buf); 143 for(i = 0; i < nelem(hdrtab); i++){ 144 n = strlen(hdrtab[i].name); 145 if(cistrncmp(buf, hdrtab[i].name, n) == 0){ 146 /* skip field name and leading white */ 147 p = buf + n; 148 while(*p == ' ' || *p == '\t') 149 p++; 150 (*hdrtab[i].fn)(hs, p); 151 break; 152 } 153 } 154 } 155 return 0; 156 } 157 158 int 159 httpopen(Client *c, Url *url) 160 { 161 int fd, code, redirect; 162 char *cookies; 163 Ioproc *io; 164 HttpState *hs; 165 166 if(httpdebug) 167 fprint(2, "httpopen\n"); 168 io = c->io; 169 hs = emalloc(sizeof(*hs)); 170 hs->c = c; 171 hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme)); 172 c->aux = hs; 173 if(httpdebug) 174 fprint(2, "dial %s\n", hs->netaddr); 175 fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps); 176 if(fd < 0){ 177 Error: 178 if(httpdebug) 179 fprint(2, "iodial: %r\n"); 180 free(hs->netaddr); 181 close(hs->fd); 182 hs->fd = -1; 183 free(hs); 184 c->aux = nil; 185 return -1; 186 } 187 hs->fd = fd; 188 if(httpdebug) 189 fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n", 190 c->havepostbody? "POST": " GET", url->http.page_spec, url->host); 191 ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n", 192 c->havepostbody? "POST" : "GET", url->http.page_spec, url->host); 193 if(httpdebug) 194 fprint(2, "<- User-Agent: %s\n", c->ctl.useragent); 195 if(c->ctl.useragent) 196 ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent); 197 if(c->ctl.sendcookies){ 198 /* should we use url->page here? sometimes it is nil. */ 199 cookies = httpcookies(url->host, url->http.page_spec, 0); 200 if(cookies && cookies[0]) 201 ioprint(io, fd, "%s", cookies); 202 if(httpdebug) 203 fprint(2, "<- %s", cookies); 204 free(cookies); 205 } 206 if(c->havepostbody){ 207 ioprint(io, fd, "Content-type: %s\r\n", PostContentType); 208 ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody); 209 if(httpdebug){ 210 fprint(2, "<- Content-type: %s\n", PostContentType); 211 fprint(2, "<- Content-length: %ud\n", c->npostbody); 212 } 213 } 214 ioprint(io, fd, "\r\n"); 215 if(c->havepostbody) 216 if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody) 217 goto Error; 218 219 c->havepostbody = 0; 220 redirect = 0; 221 initibuf(&hs->b, io, fd); 222 code = httprcode(hs); 223 224 switch(code){ 225 case -1: /* connection timed out */ 226 goto Error; 227 228 /* 229 case Eof: 230 werrstr("EOF from HTTP server"); 231 goto Error; 232 */ 233 234 case 200: /* OK */ 235 case 201: /* Created */ 236 case 202: /* Accepted */ 237 case 204: /* No Content */ 238 #ifdef NOT_DEFINED 239 if(ofile == nil && r->start != 0) 240 sysfatal("page changed underfoot"); 241 #endif 242 break; 243 244 case 206: /* Partial Content */ 245 werrstr("Partial Content (206)"); 246 goto Error; 247 248 case 301: /* Moved Permanently */ 249 case 302: /* Moved Temporarily */ 250 redirect = 1; 251 break; 252 253 case 304: /* Not Modified */ 254 break; 255 256 case 400: /* Bad Request */ 257 werrstr("Bad Request (400)"); 258 goto Error; 259 260 case 401: /* Unauthorized */ 261 case 402: /* ??? */ 262 werrstr("Unauthorized (401,402)"); 263 goto Error; 264 265 case 403: /* Forbidden */ 266 werrstr("Forbidden by server (403)"); 267 goto Error; 268 269 case 404: /* Not Found */ 270 werrstr("Not found on server (404)"); 271 goto Error; 272 273 case 500: /* Internal server error */ 274 werrstr("Server choked (500)"); 275 goto Error; 276 277 case 501: /* Not implemented */ 278 werrstr("Server can't do it (501)"); 279 goto Error; 280 281 case 502: /* Bad gateway */ 282 werrstr("Bad gateway (502)"); 283 goto Error; 284 285 case 503: /* Service unavailable */ 286 werrstr("Service unavailable (503)"); 287 goto Error; 288 289 default: 290 /* Bogus: we should treat unknown code XYZ as code X00 */ 291 werrstr("Unknown response code %d", code); 292 goto Error; 293 } 294 295 if(httpheaders(hs) < 0) 296 goto Error; 297 if(c->ctl.acceptcookies && hs->setcookie) 298 httpsetcookie(hs->setcookie, url->host, url->path); 299 if(redirect){ 300 if(!hs->location){ 301 werrstr("redirection without Location: header"); 302 return -1; 303 } 304 c->redirect = hs->location; 305 hs->location = nil; 306 } 307 return 0; 308 } 309 310 int 311 httpread(Client *c, Req *r) 312 { 313 char *dst; 314 HttpState *hs; 315 int n; 316 long rlen, tot, len; 317 318 hs = c->aux; 319 dst = r->ofcall.data; 320 len = r->ifcall.count; 321 tot = 0; 322 while (tot < len){ 323 rlen = len - tot; 324 n = readibuf(&hs->b, dst + tot, rlen); 325 if(n == 0) 326 break; 327 else if(n < 0){ 328 if(tot == 0) 329 return -1; 330 else 331 return tot; 332 } 333 tot += n; 334 } 335 r->ofcall.count = tot; 336 return 0; 337 } 338 339 void 340 httpclose(Client *c) 341 { 342 HttpState *hs; 343 344 hs = c->aux; 345 if(hs == nil) 346 return; 347 ioclose(c->io, hs->fd); 348 hs->fd = -1; 349 free(hs->location); 350 free(hs->setcookie); 351 free(hs->netaddr); 352 free(hs); 353 c->aux = nil; 354 } 355 356