1 /* $OpenBSD: server_http.c,v 1.9 2014/07/17 11:35:26 stsp Exp $ */ 2 3 /* 4 * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/stat.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 #include <sys/tree.h> 26 #include <sys/hash.h> 27 28 #include <net/if.h> 29 #include <netinet/in_systm.h> 30 #include <netinet/in.h> 31 #include <netinet/ip.h> 32 #include <netinet/tcp.h> 33 #include <arpa/inet.h> 34 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <stdio.h> 41 #include <err.h> 42 #include <pwd.h> 43 #include <event.h> 44 #include <fnmatch.h> 45 46 #include <openssl/ssl.h> 47 48 #include "httpd.h" 49 #include "http.h" 50 51 static int server_httpmethod_cmp(const void *, const void *); 52 static int server_httperror_cmp(const void *, const void *); 53 void server_httpdesc_free(struct http_descriptor *); 54 55 static struct httpd *env = NULL; 56 57 static struct http_method http_methods[] = HTTP_METHODS; 58 static struct http_error http_errors[] = HTTP_ERRORS; 59 60 void 61 server_http(struct httpd *x_env) 62 { 63 if (x_env != NULL) 64 env = x_env; 65 66 DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); 67 68 /* Sort the HTTP lookup arrays */ 69 qsort(http_methods, sizeof(http_methods) / 70 sizeof(http_methods[0]) - 1, 71 sizeof(http_methods[0]), server_httpmethod_cmp); 72 qsort(http_errors, sizeof(http_errors) / 73 sizeof(http_errors[0]) - 1, 74 sizeof(http_errors[0]), server_httperror_cmp); 75 } 76 77 void 78 server_http_init(struct server *srv) 79 { 80 /* nothing */ 81 } 82 83 int 84 server_httpdesc_init(struct client *clt) 85 { 86 struct http_descriptor *desc; 87 88 if ((desc = calloc(1, sizeof(*desc))) == NULL) 89 return (-1); 90 91 RB_INIT(&desc->http_headers); 92 clt->clt_desc = desc; 93 94 return (0); 95 } 96 97 void 98 server_httpdesc_free(struct http_descriptor *desc) 99 { 100 if (desc->http_path != NULL) { 101 free(desc->http_path); 102 desc->http_path = NULL; 103 } 104 if (desc->http_query != NULL) { 105 free(desc->http_query); 106 desc->http_query = NULL; 107 } 108 if (desc->http_version != NULL) { 109 free(desc->http_version); 110 desc->http_version = NULL; 111 } 112 kv_purge(&desc->http_headers); 113 } 114 115 void 116 server_read_http(struct bufferevent *bev, void *arg) 117 { 118 struct client *clt = arg; 119 struct http_descriptor *desc = clt->clt_desc; 120 struct evbuffer *src = EVBUFFER_INPUT(bev); 121 char *line = NULL, *key, *value; 122 const char *errstr; 123 size_t size, linelen; 124 struct kv *hdr = NULL; 125 126 getmonotime(&clt->clt_tv_last); 127 128 size = EVBUFFER_LENGTH(src); 129 DPRINTF("%s: session %d: size %lu, to read %lld", 130 __func__, clt->clt_id, size, clt->clt_toread); 131 if (!size) { 132 clt->clt_toread = TOREAD_HTTP_HEADER; 133 goto done; 134 } 135 136 while (!clt->clt_done && (line = evbuffer_readline(src)) != NULL) { 137 linelen = strlen(line); 138 139 /* 140 * An empty line indicates the end of the request. 141 * libevent already stripped the \r\n for us. 142 */ 143 if (!linelen) { 144 clt->clt_done = 1; 145 free(line); 146 break; 147 } 148 key = line; 149 150 /* Limit the total header length minus \r\n */ 151 clt->clt_headerlen += linelen; 152 if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) { 153 free(line); 154 server_abort_http(clt, 413, "request too large"); 155 return; 156 } 157 158 /* 159 * The first line is the GET/POST/PUT/... request, 160 * subsequent lines are HTTP headers. 161 */ 162 if (++clt->clt_line == 1) 163 value = strchr(key, ' '); 164 else if (*key == ' ' || *key == '\t') 165 /* Multiline headers wrap with a space or tab */ 166 value = NULL; 167 else 168 value = strchr(key, ':'); 169 if (value == NULL) { 170 if (clt->clt_line == 1) { 171 free(line); 172 server_abort_http(clt, 400, "malformed"); 173 return; 174 } 175 176 /* Append line to the last header, if present */ 177 if (kv_extend(&desc->http_headers, 178 desc->http_lastheader, line) == NULL) { 179 free(line); 180 goto fail; 181 } 182 183 free(line); 184 continue; 185 } 186 if (*value == ':') { 187 *value++ = '\0'; 188 value += strspn(value, " \t\r\n"); 189 } else { 190 *value++ = '\0'; 191 } 192 193 DPRINTF("%s: session %d: header '%s: %s'", __func__, 194 clt->clt_id, key, value); 195 196 /* 197 * Identify and handle specific HTTP request methods 198 */ 199 if (clt->clt_line == 1) { 200 if ((desc->http_method = server_httpmethod_byname(key)) 201 == HTTP_METHOD_NONE) 202 goto fail; 203 204 /* 205 * Decode request path and query 206 */ 207 desc->http_path = strdup(value); 208 if (desc->http_path == NULL) { 209 free(line); 210 goto fail; 211 } 212 desc->http_version = strchr(desc->http_path, ' '); 213 if (desc->http_version != NULL) 214 *desc->http_version++ = '\0'; 215 desc->http_query = strchr(desc->http_path, '?'); 216 if (desc->http_query != NULL) 217 *desc->http_query++ = '\0'; 218 219 /* 220 * Have to allocate the strings because they could 221 * be changed independetly by the filters later. 222 */ 223 if (desc->http_version != NULL && 224 (desc->http_version = 225 strdup(desc->http_version)) == NULL) { 226 free(line); 227 goto fail; 228 } 229 if (desc->http_query != NULL && 230 (desc->http_query = 231 strdup(desc->http_query)) == NULL) { 232 free(line); 233 goto fail; 234 } 235 } else if (desc->http_method != HTTP_METHOD_NONE && 236 strcasecmp("Content-Length", key) == 0) { 237 if (desc->http_method == HTTP_METHOD_TRACE || 238 desc->http_method == HTTP_METHOD_CONNECT) { 239 /* 240 * These method should not have a body 241 * and thus no Content-Length header. 242 */ 243 server_abort_http(clt, 400, "malformed"); 244 goto abort; 245 } 246 247 /* 248 * Need to read data from the client after the 249 * HTTP header. 250 * XXX What about non-standard clients not using 251 * the carriage return? And some browsers seem to 252 * include the line length in the content-length. 253 */ 254 clt->clt_toread = strtonum(value, 0, LLONG_MAX, 255 &errstr); 256 if (errstr) { 257 server_abort_http(clt, 500, errstr); 258 goto abort; 259 } 260 } 261 262 if (strcasecmp("Transfer-Encoding", key) == 0 && 263 strcasecmp("chunked", value) == 0) 264 desc->http_chunked = 1; 265 266 if (clt->clt_line != 1) { 267 if ((hdr = kv_add(&desc->http_headers, key, 268 value)) == NULL) { 269 free(line); 270 goto fail; 271 } 272 desc->http_lastheader = hdr; 273 } 274 275 free(line); 276 } 277 if (clt->clt_done) { 278 if (desc->http_method == HTTP_METHOD_NONE) { 279 server_abort_http(clt, 406, "no method"); 280 return; 281 } 282 283 switch (desc->http_method) { 284 case HTTP_METHOD_CONNECT: 285 /* Data stream */ 286 clt->clt_toread = TOREAD_UNLIMITED; 287 bev->readcb = server_read; 288 break; 289 case HTTP_METHOD_DELETE: 290 case HTTP_METHOD_GET: 291 case HTTP_METHOD_HEAD: 292 case HTTP_METHOD_OPTIONS: 293 clt->clt_toread = 0; 294 break; 295 case HTTP_METHOD_POST: 296 case HTTP_METHOD_PUT: 297 case HTTP_METHOD_RESPONSE: 298 /* HTTP request payload */ 299 if (clt->clt_toread > 0) 300 bev->readcb = server_read_httpcontent; 301 302 /* Single-pass HTTP body */ 303 if (clt->clt_toread < 0) { 304 clt->clt_toread = TOREAD_UNLIMITED; 305 bev->readcb = server_read; 306 } 307 break; 308 default: 309 /* HTTP handler */ 310 clt->clt_toread = TOREAD_HTTP_HEADER; 311 bev->readcb = server_read_http; 312 break; 313 } 314 if (desc->http_chunked) { 315 /* Chunked transfer encoding */ 316 clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 317 bev->readcb = server_read_httpchunks; 318 } 319 320 done: 321 if (clt->clt_toread <= 0) { 322 server_response(env, clt); 323 return; 324 } 325 } 326 if (clt->clt_done) { 327 server_close(clt, "done"); 328 return; 329 } 330 if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http) 331 bev->readcb(bev, arg); 332 bufferevent_enable(bev, EV_READ); 333 return; 334 fail: 335 server_abort_http(clt, 500, strerror(errno)); 336 return; 337 abort: 338 free(line); 339 } 340 341 void 342 server_read_httpcontent(struct bufferevent *bev, void *arg) 343 { 344 struct client *clt = arg; 345 struct evbuffer *src = EVBUFFER_INPUT(bev); 346 size_t size; 347 348 getmonotime(&clt->clt_tv_last); 349 350 size = EVBUFFER_LENGTH(src); 351 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 352 clt->clt_id, size, clt->clt_toread); 353 if (!size) 354 return; 355 356 if (clt->clt_toread > 0) { 357 /* Read content data */ 358 if ((off_t)size > clt->clt_toread) { 359 size = clt->clt_toread; 360 if (server_bufferevent_write_chunk(clt, 361 src, size) == -1) 362 goto fail; 363 clt->clt_toread = 0; 364 } else { 365 if (server_bufferevent_write_buffer(clt, src) == -1) 366 goto fail; 367 clt->clt_toread -= size; 368 } 369 DPRINTF("%s: done, size %lu, to read %lld", __func__, 370 size, clt->clt_toread); 371 } 372 if (clt->clt_toread == 0) { 373 clt->clt_toread = TOREAD_HTTP_HEADER; 374 bev->readcb = server_read_http; 375 } 376 if (clt->clt_done) 377 goto done; 378 if (bev->readcb != server_read_httpcontent) 379 bev->readcb(bev, arg); 380 bufferevent_enable(bev, EV_READ); 381 return; 382 done: 383 server_close(clt, "last http content read"); 384 return; 385 fail: 386 server_close(clt, strerror(errno)); 387 } 388 389 void 390 server_read_httpchunks(struct bufferevent *bev, void *arg) 391 { 392 struct client *clt = arg; 393 struct evbuffer *src = EVBUFFER_INPUT(bev); 394 char *line; 395 long long llval; 396 size_t size; 397 398 getmonotime(&clt->clt_tv_last); 399 400 size = EVBUFFER_LENGTH(src); 401 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 402 clt->clt_id, size, clt->clt_toread); 403 if (!size) 404 return; 405 406 if (clt->clt_toread > 0) { 407 /* Read chunk data */ 408 if ((off_t)size > clt->clt_toread) { 409 size = clt->clt_toread; 410 if (server_bufferevent_write_chunk(clt, src, size) 411 == -1) 412 goto fail; 413 clt->clt_toread = 0; 414 } else { 415 if (server_bufferevent_write_buffer(clt, src) == -1) 416 goto fail; 417 clt->clt_toread -= size; 418 } 419 DPRINTF("%s: done, size %lu, to read %lld", __func__, 420 size, clt->clt_toread); 421 } 422 switch (clt->clt_toread) { 423 case TOREAD_HTTP_CHUNK_LENGTH: 424 line = evbuffer_readline(src); 425 if (line == NULL) { 426 /* Ignore empty line, continue */ 427 bufferevent_enable(bev, EV_READ); 428 return; 429 } 430 if (strlen(line) == 0) { 431 free(line); 432 goto next; 433 } 434 435 /* 436 * Read prepended chunk size in hex, ignore the trailer. 437 * The returned signed value must not be negative. 438 */ 439 if (sscanf(line, "%llx", &llval) != 1 || llval < 0) { 440 free(line); 441 server_close(clt, "invalid chunk size"); 442 return; 443 } 444 445 if (server_bufferevent_print(clt, line) == -1 || 446 server_bufferevent_print(clt, "\r\n") == -1) { 447 free(line); 448 goto fail; 449 } 450 free(line); 451 452 if ((clt->clt_toread = llval) == 0) { 453 DPRINTF("%s: last chunk", __func__); 454 clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER; 455 } 456 break; 457 case TOREAD_HTTP_CHUNK_TRAILER: 458 /* Last chunk is 0 bytes followed by trailer and empty line */ 459 line = evbuffer_readline(src); 460 if (line == NULL) { 461 /* Ignore empty line, continue */ 462 bufferevent_enable(bev, EV_READ); 463 return; 464 } 465 if (server_bufferevent_print(clt, line) == -1 || 466 server_bufferevent_print(clt, "\r\n") == -1) { 467 free(line); 468 goto fail; 469 } 470 if (strlen(line) == 0) { 471 /* Switch to HTTP header mode */ 472 clt->clt_toread = TOREAD_HTTP_HEADER; 473 bev->readcb = server_read_http; 474 } 475 free(line); 476 break; 477 case 0: 478 /* Chunk is terminated by an empty newline */ 479 line = evbuffer_readline(src); 480 if (line != NULL) 481 free(line); 482 if (server_bufferevent_print(clt, "\r\n") == -1) 483 goto fail; 484 clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 485 break; 486 } 487 488 next: 489 if (clt->clt_done) 490 goto done; 491 if (EVBUFFER_LENGTH(src)) 492 bev->readcb(bev, arg); 493 bufferevent_enable(bev, EV_READ); 494 return; 495 496 done: 497 server_close(clt, "last http chunk read (done)"); 498 return; 499 fail: 500 server_close(clt, strerror(errno)); 501 } 502 503 void 504 server_reset_http(struct client *clt) 505 { 506 struct http_descriptor *desc = clt->clt_desc; 507 508 server_httpdesc_free(desc); 509 desc->http_method = 0; 510 desc->http_chunked = 0; 511 clt->clt_headerlen = 0; 512 clt->clt_line = 0; 513 clt->clt_done = 0; 514 clt->clt_bev->readcb = server_read_http; 515 } 516 517 void 518 server_abort_http(struct client *clt, u_int code, const char *msg) 519 { 520 struct server *srv = clt->clt_server; 521 struct bufferevent *bev = clt->clt_bev; 522 const char *httperr = NULL, *text = ""; 523 char *httpmsg; 524 time_t t; 525 struct tm *lt; 526 char tmbuf[32], hbuf[128]; 527 const char *style; 528 529 if ((httperr = server_httperror_byid(code)) == NULL) 530 httperr = "Unknown Error"; 531 532 if (bev == NULL) 533 goto done; 534 535 /* Some system information */ 536 if (print_host(&srv->srv_conf.ss, hbuf, sizeof(hbuf)) == NULL) 537 goto done; 538 539 /* RFC 2616 "tolerates" asctime() */ 540 time(&t); 541 lt = localtime(&t); 542 tmbuf[0] = '\0'; 543 if (asctime_r(lt, tmbuf) != NULL) 544 tmbuf[strlen(tmbuf) - 1] = '\0'; /* skip final '\n' */ 545 546 /* Do not send details of the Internal Server Error */ 547 if (code != 500) 548 text = msg; 549 550 /* A CSS stylesheet allows minimal customization by the user */ 551 style = "body { background-color: white; color: black; font-family: " 552 "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }" 553 "blink { animation:blink 1s; animation-iteration-count: infinite;" 554 "-webkit-animation:blink 1s;" 555 "-webkit-animation-iteration-count: infinite;}" 556 "@keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}" 557 "50.01%{opacity:1.0;} 100%{opacity:1.0;} }" 558 "@-webkit-keyframes blink { 0%{opacity:0.0;} 50%{opacity:0.0;}" 559 "50.01%{opacity:1.0;} 100%{opacity:1.0;} }"; 560 /* Generate simple HTTP+HTML error document */ 561 if (asprintf(&httpmsg, 562 "HTTP/1.0 %03d %s\r\n" 563 "Date: %s\r\n" 564 "Server: %s\r\n" 565 "Connection: close\r\n" 566 "Content-Type: text/html\r\n" 567 "\r\n" 568 "<!DOCTYPE HTML PUBLIC " 569 "\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" 570 "<html>\n" 571 "<head>\n" 572 "<title>%03d %s</title>\n" 573 "<style type=\"text/css\"><!--\n%s\n--></style>\n" 574 "</head>\n" 575 "<body>\n" 576 "<h1><blink>%s</blink></h1>\n" 577 "<div id='m'>%s</div>\n" 578 "<hr><address>%s at %s port %d</address>\n" 579 "</body>\n" 580 "</html>\n", 581 code, httperr, tmbuf, HTTPD_SERVERNAME, 582 code, httperr, style, httperr, text, 583 HTTPD_SERVERNAME, hbuf, ntohs(srv->srv_conf.port)) == -1) 584 goto done; 585 586 /* Dump the message without checking for success */ 587 server_dump(clt, httpmsg, strlen(httpmsg)); 588 free(httpmsg); 589 590 done: 591 if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) 592 server_close(clt, msg); 593 else { 594 server_close(clt, httpmsg); 595 free(httpmsg); 596 } 597 } 598 599 void 600 server_close_http(struct client *clt) 601 { 602 struct http_descriptor *desc = clt->clt_desc; 603 604 if (desc == NULL) 605 return; 606 server_httpdesc_free(desc); 607 free(desc); 608 } 609 610 int 611 server_response(struct httpd *httpd, struct client *clt) 612 { 613 struct http_descriptor *desc = clt->clt_desc; 614 struct kv *kv, key; 615 int ret; 616 617 if (desc->http_path == NULL) 618 goto fail; 619 620 if (strcmp(desc->http_version, "HTTP/1.1") == 0) { 621 /* Host header is mandatory */ 622 key.kv_key = "Host"; 623 if ((kv = kv_find(&desc->http_headers, &key)) == NULL) 624 goto fail; 625 626 /* Is the connection persistent? */ 627 key.kv_key = "Connection"; 628 if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 629 strcasecmp("close", kv->kv_value) == 0) 630 clt->clt_persist = 0; 631 else 632 clt->clt_persist++; 633 } else { 634 /* Is the connection persistent? */ 635 key.kv_key = "Connection"; 636 if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 637 strcasecmp("keep-alive", kv->kv_value) == 0) 638 clt->clt_persist++; 639 else 640 clt->clt_persist = 0; 641 } 642 643 if ((ret = server_file(httpd, clt)) == -1) 644 return (-1); 645 646 server_reset_http(clt); 647 648 return (0); 649 fail: 650 server_abort_http(clt, 400, "bad request"); 651 return (-1); 652 } 653 654 int 655 server_response_http(struct client *clt, u_int code, 656 struct media_type *media, size_t size) 657 { 658 struct http_descriptor *desc = clt->clt_desc; 659 const char *error; 660 struct kv *ct, *cl; 661 662 if (desc == NULL || (error = server_httperror_byid(code)) == NULL) 663 return (-1); 664 665 kv_purge(&desc->http_headers); 666 667 /* Add error codes */ 668 if (kv_setkey(&desc->http_pathquery, "%lu", code) == -1 || 669 kv_set(&desc->http_pathquery, "%s", error) == -1) 670 return (-1); 671 672 /* Add headers */ 673 if (kv_add(&desc->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 674 return (-1); 675 676 /* Is it a persistent connection? */ 677 if (clt->clt_persist) { 678 if (kv_add(&desc->http_headers, 679 "Connection", "keep-alive") == NULL) 680 return (-1); 681 } else if (kv_add(&desc->http_headers, "Connection", "close") == NULL) 682 return (-1); 683 684 /* Set media type */ 685 if ((ct = kv_add(&desc->http_headers, "Content-Type", NULL)) == NULL || 686 kv_set(ct, "%s/%s", 687 media == NULL ? "application" : media->media_type, 688 media == NULL ? "octet-stream" : media->media_subtype) == -1) 689 return (-1); 690 691 /* Set content length, if specified */ 692 if (size && ((cl = 693 kv_add(&desc->http_headers, "Content-Length", NULL)) == NULL || 694 kv_set(cl, "%ld", size) == -1)) 695 return (-1); 696 697 /* Write completed header */ 698 if (server_writeresponse_http(clt) == -1 || 699 server_bufferevent_print(clt, "\r\n") == -1 || 700 server_writeheader_http(clt) == -1 || 701 server_bufferevent_print(clt, "\r\n") == -1) 702 return (-1); 703 704 if (desc->http_method == HTTP_METHOD_HEAD) { 705 bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); 706 if (clt->clt_persist) 707 clt->clt_toread = TOREAD_HTTP_HEADER; 708 else 709 clt->clt_toread = TOREAD_HTTP_NONE; 710 clt->clt_done = 0; 711 return (0); 712 } 713 714 return (1); 715 } 716 717 int 718 server_writeresponse_http(struct client *clt) 719 { 720 struct http_descriptor *desc = (struct http_descriptor *)clt->clt_desc; 721 722 DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, 723 desc->http_rescode, desc->http_resmesg); 724 725 if (server_bufferevent_print(clt, desc->http_version) == -1 || 726 server_bufferevent_print(clt, " ") == -1 || 727 server_bufferevent_print(clt, desc->http_rescode) == -1 || 728 server_bufferevent_print(clt, " ") == -1 || 729 server_bufferevent_print(clt, desc->http_resmesg) == -1) 730 return (-1); 731 732 return (0); 733 } 734 735 int 736 server_writeheader_kv(struct client *clt, struct kv *hdr) 737 { 738 char *ptr; 739 const char *key; 740 741 if (hdr->kv_flags & KV_FLAG_INVALID) 742 return (0); 743 744 /* The key might have been updated in the parent */ 745 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 746 key = hdr->kv_parent->kv_key; 747 else 748 key = hdr->kv_key; 749 750 ptr = hdr->kv_value; 751 if (server_bufferevent_print(clt, key) == -1 || 752 (ptr != NULL && 753 (server_bufferevent_print(clt, ": ") == -1 || 754 server_bufferevent_print(clt, ptr) == -1 || 755 server_bufferevent_print(clt, "\r\n") == -1))) 756 return (-1); 757 DPRINTF("%s: %s: %s", __func__, key, 758 hdr->kv_value == NULL ? "" : hdr->kv_value); 759 760 return (0); 761 } 762 763 int 764 server_writeheader_http(struct client *clt) 765 { 766 struct kv *hdr, *kv; 767 struct http_descriptor *desc = (struct http_descriptor *)clt->clt_desc; 768 769 RB_FOREACH(hdr, kvtree, &desc->http_headers) { 770 if (server_writeheader_kv(clt, hdr) == -1) 771 return (-1); 772 TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { 773 if (server_writeheader_kv(clt, kv) == -1) 774 return (-1); 775 } 776 } 777 778 return (0); 779 } 780 781 enum httpmethod 782 server_httpmethod_byname(const char *name) 783 { 784 enum httpmethod id = HTTP_METHOD_NONE; 785 struct http_method method, *res = NULL; 786 787 /* Set up key */ 788 method.method_name = name; 789 790 if ((res = bsearch(&method, http_methods, 791 sizeof(http_methods) / sizeof(http_methods[0]) - 1, 792 sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL) 793 id = res->method_id; 794 795 return (id); 796 } 797 798 const char * 799 server_httpmethod_byid(u_int id) 800 { 801 const char *name = NULL; 802 int i; 803 804 for (i = 0; http_methods[i].method_name != NULL; i++) { 805 if (http_methods[i].method_id == id) { 806 name = http_methods[i].method_name; 807 break; 808 } 809 } 810 811 return (name); 812 } 813 814 static int 815 server_httpmethod_cmp(const void *a, const void *b) 816 { 817 const struct http_method *ma = a; 818 const struct http_method *mb = b; 819 820 /* 821 * RFC 2616 section 5.1.1 says that the method is case 822 * sensitive so we don't do a strcasecmp here. 823 */ 824 return (strcmp(ma->method_name, mb->method_name)); 825 } 826 827 const char * 828 server_httperror_byid(u_int id) 829 { 830 struct http_error error, *res = NULL; 831 832 /* Set up key */ 833 error.error_code = (int)id; 834 835 res = bsearch(&error, http_errors, 836 sizeof(http_errors) / sizeof(http_errors[0]) - 1, 837 sizeof(http_errors[0]), server_httperror_cmp); 838 839 return (res->error_name); 840 } 841 842 static int 843 server_httperror_cmp(const void *a, const void *b) 844 { 845 const struct http_error *ea = a; 846 const struct http_error *eb = b; 847 return (ea->error_code - eb->error_code); 848 } 849