server_fcgi.c revision 1.51
1/* $OpenBSD: server_fcgi.c,v 1.51 2015/01/21 22:23:24 reyk Exp $ */ 2 3/* 4 * Copyright (c) 2014 Florian Obser <florian@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/time.h> 21#include <sys/socket.h> 22#include <sys/un.h> 23 24#include <netinet/in.h> 25#include <arpa/inet.h> 26 27#include <limits.h> 28#include <errno.h> 29#include <stdlib.h> 30#include <string.h> 31#include <stdio.h> 32#include <time.h> 33#include <ctype.h> 34#include <event.h> 35 36#include "httpd.h" 37#include "http.h" 38 39#define FCGI_PADDING_SIZE 255 40#define FCGI_RECORD_SIZE \ 41 (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 42 43#define FCGI_BEGIN_REQUEST 1 44#define FCGI_ABORT_REQUEST 2 45#define FCGI_END_REQUEST 3 46#define FCGI_PARAMS 4 47#define FCGI_STDIN 5 48#define FCGI_STDOUT 6 49#define FCGI_STDERR 7 50#define FCGI_DATA 8 51#define FCGI_GET_VALUES 9 52#define FCGI_GET_VALUES_RESULT 10 53#define FCGI_UNKNOWN_TYPE 11 54#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 55 56#define FCGI_RESPONDER 1 57 58struct fcgi_record_header { 59 uint8_t version; 60 uint8_t type; 61 uint16_t id; 62 uint16_t content_len; 63 uint8_t padding_len; 64 uint8_t reserved; 65} __packed; 66 67struct fcgi_begin_request_body { 68 uint16_t role; 69 uint8_t flags; 70 uint8_t reserved[5]; 71} __packed; 72 73struct server_fcgi_param { 74 int total_len; 75 uint8_t buf[FCGI_RECORD_SIZE]; 76}; 77 78int server_fcgi_header(struct client *, u_int); 79void server_fcgi_read(struct bufferevent *, void *); 80int server_fcgi_writeheader(struct client *, struct kv *, void *); 81int server_fcgi_writechunk(struct client *); 82int server_fcgi_getheaders(struct client *); 83int fcgi_add_param(struct server_fcgi_param *, const char *, const char *, 84 struct client *); 85 86int 87server_fcgi(struct httpd *env, struct client *clt) 88{ 89 struct server_fcgi_param param; 90 struct server_config *srv_conf = clt->clt_srv_conf; 91 struct http_descriptor *desc = clt->clt_descreq; 92 struct fcgi_record_header *h; 93 struct fcgi_begin_request_body *begin; 94 char hbuf[HOST_NAME_MAX+1]; 95 size_t scriptlen; 96 int pathlen; 97 int fd = -1, ret; 98 const char *stripped, *p, *alias, *errstr = NULL; 99 char *str, *script = NULL; 100 101 if (srv_conf->socket[0] == ':') { 102 struct sockaddr_storage ss; 103 in_port_t port; 104 105 p = srv_conf->socket + 1; 106 107 port = strtonum(p, 0, 0xffff, &errstr); 108 if (errstr != NULL) { 109 log_warn("%s: strtonum %s, %s", __func__, p, errstr); 110 goto fail; 111 } 112 memset(&ss, 0, sizeof(ss)); 113 ss.ss_family = AF_INET; 114 ((struct sockaddr_in *) 115 &ss)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 116 port = htons(port); 117 118 if ((fd = server_socket_connect(&ss, port, srv_conf)) == -1) 119 goto fail; 120 } else { 121 struct sockaddr_un sun; 122 size_t len; 123 124 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 125 goto fail; 126 127 memset(&sun, 0, sizeof(sun)); 128 sun.sun_family = AF_UNIX; 129 len = strlcpy(sun.sun_path, 130 srv_conf->socket, sizeof(sun.sun_path)); 131 if (len >= sizeof(sun.sun_path)) { 132 errstr = "socket path to long"; 133 goto fail; 134 } 135 sun.sun_len = len; 136 137 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 138 goto fail; 139 } 140 141 socket_set_blockmode(fd, BM_NONBLOCK); 142 143 memset(hbuf, 0, sizeof(hbuf)); 144 clt->clt_fcgi_state = FCGI_READ_HEADER; 145 clt->clt_fcgi_toread = sizeof(struct fcgi_record_header); 146 147 if (clt->clt_srvevb != NULL) 148 evbuffer_free(clt->clt_srvevb); 149 150 clt->clt_srvevb = evbuffer_new(); 151 if (clt->clt_srvevb == NULL) { 152 errstr = "failed to allocate evbuffer"; 153 goto fail; 154 } 155 156 clt->clt_fd = fd; 157 if (clt->clt_srvbev != NULL) 158 bufferevent_free(clt->clt_srvbev); 159 160 clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read, 161 NULL, server_file_error, clt); 162 if (clt->clt_srvbev == NULL) { 163 errstr = "failed to allocate fcgi buffer event"; 164 goto fail; 165 } 166 167 memset(¶m, 0, sizeof(param)); 168 169 h = (struct fcgi_record_header *)¶m.buf; 170 h->version = 1; 171 h->type = FCGI_BEGIN_REQUEST; 172 h->id = htons(1); 173 h->content_len = htons(sizeof(struct fcgi_begin_request_body)); 174 h->padding_len = 0; 175 176 begin = (struct fcgi_begin_request_body *)¶m.buf[sizeof(struct 177 fcgi_record_header)]; 178 begin->role = htons(FCGI_RESPONDER); 179 180 bufferevent_write(clt->clt_srvbev, ¶m.buf, 181 sizeof(struct fcgi_record_header) + 182 sizeof(struct fcgi_begin_request_body)); 183 184 h->type = FCGI_PARAMS; 185 h->content_len = param.total_len = 0; 186 187 alias = desc->http_path_alias != NULL 188 ? desc->http_path_alias 189 : desc->http_path; 190 191 stripped = server_root_strip(alias, srv_conf->strip); 192 if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) 193 == -1) { 194 errstr = "failed to get script name"; 195 goto fail; 196 } 197 198 scriptlen = path_info(script); 199 /* 200 * no part of root should show up in PATH_INFO. 201 * therefore scriptlen should be >= strlen(root) 202 */ 203 if (scriptlen < strlen(srv_conf->root)) 204 scriptlen = strlen(srv_conf->root); 205 if ((int)scriptlen < pathlen) { 206 if (fcgi_add_param(¶m, "PATH_INFO", 207 script + scriptlen, clt) == -1) { 208 errstr = "failed to encode param"; 209 goto fail; 210 } 211 script[scriptlen] = '\0'; 212 } 213 214 /* 215 * calculate length of http SCRIPT_NAME: 216 * add length of stripped prefix, 217 * subtract length of prepended local root 218 */ 219 scriptlen += (stripped - alias) - strlen(srv_conf->root); 220 if ((str = strndup(alias, scriptlen)) == NULL) 221 goto fail; 222 ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); 223 free(str); 224 if (ret == -1) { 225 errstr = "failed to encode param"; 226 goto fail; 227 } 228 if (fcgi_add_param(¶m, "SCRIPT_FILENAME", script, clt) == -1) { 229 errstr = "failed to encode param"; 230 goto fail; 231 } 232 233 if (desc->http_query) 234 if (fcgi_add_param(¶m, "QUERY_STRING", desc->http_query, 235 clt) == -1) { 236 errstr = "failed to encode param"; 237 goto fail; 238 } 239 240 if (fcgi_add_param(¶m, "DOCUMENT_ROOT", srv_conf->root, 241 clt) == -1) { 242 errstr = "failed to encode param"; 243 goto fail; 244 } 245 if (fcgi_add_param(¶m, "DOCUMENT_URI", desc->http_path, 246 clt) == -1) { 247 errstr = "failed to encode param"; 248 goto fail; 249 } 250 if (fcgi_add_param(¶m, "GATEWAY_INTERFACE", "CGI/1.1", 251 clt) == -1) { 252 errstr = "failed to encode param"; 253 goto fail; 254 } 255 256 if (srv_conf->flags & SRVFLAG_AUTH) { 257 if (fcgi_add_param(¶m, "REMOTE_USER", 258 clt->clt_remote_user, clt) == -1) { 259 errstr = "failed to encode param"; 260 goto fail; 261 } 262 } 263 264 /* Add HTTP_* headers */ 265 if (server_headers(clt, desc, server_fcgi_writeheader, ¶m) == -1) { 266 errstr = "failed to encode param"; 267 goto fail; 268 } 269 270 if (srv_conf->flags & SRVFLAG_TLS) 271 if (fcgi_add_param(¶m, "HTTPS", "on", clt) == -1) { 272 errstr = "failed to encode param"; 273 goto fail; 274 } 275 276 (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf)); 277 if (fcgi_add_param(¶m, "REMOTE_ADDR", hbuf, clt) == -1) { 278 errstr = "failed to encode param"; 279 goto fail; 280 } 281 282 (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port)); 283 if (fcgi_add_param(¶m, "REMOTE_PORT", hbuf, clt) == -1) { 284 errstr = "failed to encode param"; 285 goto fail; 286 } 287 288 if (fcgi_add_param(¶m, "REQUEST_METHOD", 289 server_httpmethod_byid(desc->http_method), clt) == -1) { 290 errstr = "failed to encode param"; 291 goto fail; 292 } 293 294 if (!desc->http_query) { 295 if (fcgi_add_param(¶m, "REQUEST_URI", desc->http_path, 296 clt) == -1) { 297 errstr = "failed to encode param"; 298 goto fail; 299 } 300 } else if (asprintf(&str, "%s?%s", desc->http_path, 301 desc->http_query) != -1) { 302 ret = fcgi_add_param(¶m, "REQUEST_URI", str, clt); 303 free(str); 304 if (ret == -1) { 305 errstr = "failed to encode param"; 306 goto fail; 307 } 308 } 309 310 (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf)); 311 if (fcgi_add_param(¶m, "SERVER_ADDR", hbuf, clt) == -1) { 312 errstr = "failed to encode param"; 313 goto fail; 314 } 315 316 (void)snprintf(hbuf, sizeof(hbuf), "%d", 317 ntohs(server_socket_getport(&clt->clt_srv_ss))); 318 if (fcgi_add_param(¶m, "SERVER_PORT", hbuf, clt) == -1) { 319 errstr = "failed to encode param"; 320 goto fail; 321 } 322 323 if (fcgi_add_param(¶m, "SERVER_NAME", srv_conf->name, 324 clt) == -1) { 325 errstr = "failed to encode param"; 326 goto fail; 327 } 328 329 if (fcgi_add_param(¶m, "SERVER_PROTOCOL", desc->http_version, 330 clt) == -1) { 331 errstr = "failed to encode param"; 332 goto fail; 333 } 334 335 if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, 336 clt) == -1) { 337 errstr = "failed to encode param"; 338 goto fail; 339 } 340 341 if (param.total_len != 0) { /* send last params record */ 342 bufferevent_write(clt->clt_srvbev, ¶m.buf, 343 sizeof(struct fcgi_record_header) + 344 ntohs(h->content_len)); 345 } 346 347 /* send "no more params" message */ 348 h->content_len = 0; 349 bufferevent_write(clt->clt_srvbev, ¶m.buf, 350 sizeof(struct fcgi_record_header)); 351 352 bufferevent_settimeout(clt->clt_srvbev, 353 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); 354 bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE); 355 if (clt->clt_toread != 0) { 356 server_read_httpcontent(clt->clt_bev, clt); 357 bufferevent_enable(clt->clt_bev, EV_READ); 358 } else { 359 bufferevent_disable(clt->clt_bev, EV_READ); 360 fcgi_add_stdin(clt, NULL); 361 } 362 363 if (strcmp(desc->http_version, "HTTP/1.1") == 0) { 364 clt->clt_fcgi_chunked = 1; 365 } else { 366 /* HTTP/1.0 does not support chunked encoding */ 367 clt->clt_fcgi_chunked = 0; 368 clt->clt_persist = 0; 369 } 370 clt->clt_fcgi_end = 0; 371 clt->clt_done = 0; 372 373 free(script); 374 return (0); 375 fail: 376 free(script); 377 if (errstr == NULL) 378 errstr = strerror(errno); 379 server_abort_http(clt, 500, errstr); 380 return (-1); 381} 382 383int 384fcgi_add_stdin(struct client *clt, struct evbuffer *evbuf) 385{ 386 struct fcgi_record_header h; 387 388 memset(&h, 0, sizeof(h)); 389 h.version = 1; 390 h.type = FCGI_STDIN; 391 h.id = htons(1); 392 h.padding_len = 0; 393 394 if (evbuf == NULL) { 395 h.content_len = 0; 396 return bufferevent_write(clt->clt_srvbev, &h, 397 sizeof(struct fcgi_record_header)); 398 } else { 399 h.content_len = htons(EVBUFFER_LENGTH(evbuf)); 400 if (bufferevent_write(clt->clt_srvbev, &h, 401 sizeof(struct fcgi_record_header)) == -1) 402 return -1; 403 return bufferevent_write_buffer(clt->clt_srvbev, evbuf); 404 } 405 return (0); 406} 407 408int 409fcgi_add_param(struct server_fcgi_param *p, const char *key, 410 const char *val, struct client *clt) 411{ 412 struct fcgi_record_header *h; 413 int len = 0; 414 int key_len = strlen(key); 415 int val_len = strlen(val); 416 uint8_t *param; 417 418 len += key_len + val_len; 419 len += key_len > 127 ? 4 : 1; 420 len += val_len > 127 ? 4 : 1; 421 422 DPRINTF("%s: %s[%d] => %s[%d], total_len: %d", __func__, key, key_len, 423 val, val_len, p->total_len); 424 425 if (len > FCGI_CONTENT_SIZE) 426 return (-1); 427 428 if (p->total_len + len > FCGI_CONTENT_SIZE) { 429 bufferevent_write(clt->clt_srvbev, p->buf, 430 sizeof(struct fcgi_record_header) + p->total_len); 431 p->total_len = 0; 432 } 433 434 h = (struct fcgi_record_header *)p->buf; 435 param = p->buf + sizeof(*h) + p->total_len; 436 437 if (key_len > 127) { 438 *param++ = ((key_len >> 24) & 0xff) | 0x80; 439 *param++ = ((key_len >> 16) & 0xff); 440 *param++ = ((key_len >> 8) & 0xff); 441 *param++ = (key_len & 0xff); 442 } else 443 *param++ = key_len; 444 445 if (val_len > 127) { 446 *param++ = ((val_len >> 24) & 0xff) | 0x80; 447 *param++ = ((val_len >> 16) & 0xff); 448 *param++ = ((val_len >> 8) & 0xff); 449 *param++ = (val_len & 0xff); 450 } else 451 *param++ = val_len; 452 453 memcpy(param, key, key_len); 454 param += key_len; 455 memcpy(param, val, val_len); 456 457 p->total_len += len; 458 459 h->content_len = htons(p->total_len); 460 return (0); 461} 462 463void 464server_fcgi_read(struct bufferevent *bev, void *arg) 465{ 466 uint8_t buf[FCGI_RECORD_SIZE]; 467 struct client *clt = (struct client *) arg; 468 struct fcgi_record_header *h; 469 size_t len; 470 char *ptr; 471 472 do { 473 len = bufferevent_read(bev, buf, clt->clt_fcgi_toread); 474 /* XXX error handling */ 475 evbuffer_add(clt->clt_srvevb, buf, len); 476 clt->clt_fcgi_toread -= len; 477 DPRINTF("%s: len: %lu toread: %d state: %d type: %d", 478 __func__, len, clt->clt_fcgi_toread, 479 clt->clt_fcgi_state, clt->clt_fcgi_type); 480 481 if (clt->clt_fcgi_toread != 0) 482 return; 483 484 switch (clt->clt_fcgi_state) { 485 case FCGI_READ_HEADER: 486 clt->clt_fcgi_state = FCGI_READ_CONTENT; 487 h = (struct fcgi_record_header *) 488 EVBUFFER_DATA(clt->clt_srvevb); 489 DPRINTF("%s: record header: version %d type %d id %d " 490 "content len %d padding %d", __func__, 491 h->version, h->type, ntohs(h->id), 492 ntohs(h->content_len), h->padding_len); 493 clt->clt_fcgi_type = h->type; 494 clt->clt_fcgi_toread = ntohs(h->content_len); 495 clt->clt_fcgi_padding_len = h->padding_len; 496 evbuffer_drain(clt->clt_srvevb, 497 EVBUFFER_LENGTH(clt->clt_srvevb)); 498 if (clt->clt_fcgi_toread != 0) 499 break; 500 else if (clt->clt_fcgi_type == FCGI_STDOUT && 501 !clt->clt_chunk) { 502 server_abort_http(clt, 500, "empty stdout"); 503 return; 504 } 505 506 /* fallthrough if content_len == 0 */ 507 case FCGI_READ_CONTENT: 508 switch (clt->clt_fcgi_type) { 509 case FCGI_STDERR: 510 if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 && 511 (ptr = get_string( 512 EVBUFFER_DATA(clt->clt_srvevb), 513 EVBUFFER_LENGTH(clt->clt_srvevb))) 514 != NULL) { 515 server_sendlog(clt->clt_srv_conf, 516 IMSG_LOG_ERROR, "%s", ptr); 517 free(ptr); 518 } 519 break; 520 case FCGI_STDOUT: 521 if (++clt->clt_chunk == 1) { 522 if (server_fcgi_header(clt, 523 server_fcgi_getheaders(clt)) 524 == -1) { 525 server_abort_http(clt, 500, 526 "malformed fcgi headers"); 527 return; 528 } 529 if (!EVBUFFER_LENGTH(clt->clt_srvevb)) 530 break; 531 } 532 /* FALLTHROUGH */ 533 case FCGI_END_REQUEST: 534 if (server_fcgi_writechunk(clt) == -1) { 535 server_abort_http(clt, 500, 536 "encoding error"); 537 return; 538 } 539 break; 540 } 541 evbuffer_drain(clt->clt_srvevb, 542 EVBUFFER_LENGTH(clt->clt_srvevb)); 543 if (!clt->clt_fcgi_padding_len) { 544 clt->clt_fcgi_state = FCGI_READ_HEADER; 545 clt->clt_fcgi_toread = 546 sizeof(struct fcgi_record_header); 547 } else { 548 clt->clt_fcgi_state = FCGI_READ_PADDING; 549 clt->clt_fcgi_toread = 550 clt->clt_fcgi_padding_len; 551 } 552 break; 553 case FCGI_READ_PADDING: 554 evbuffer_drain(clt->clt_srvevb, 555 EVBUFFER_LENGTH(clt->clt_srvevb)); 556 clt->clt_fcgi_state = FCGI_READ_HEADER; 557 clt->clt_fcgi_toread = 558 sizeof(struct fcgi_record_header); 559 break; 560 } 561 } while (len > 0); 562} 563 564int 565server_fcgi_header(struct client *clt, u_int code) 566{ 567 struct http_descriptor *desc = clt->clt_descreq; 568 struct http_descriptor *resp = clt->clt_descresp; 569 const char *error; 570 char tmbuf[32]; 571 struct kv *kv, key; 572 573 if (desc == NULL || (error = server_httperror_byid(code)) == NULL) 574 return (-1); 575 576 if (server_log_http(clt, code, 0) == -1) 577 return (-1); 578 579 /* Add error codes */ 580 if (kv_setkey(&resp->http_pathquery, "%lu", code) == -1 || 581 kv_set(&resp->http_pathquery, "%s", error) == -1) 582 return (-1); 583 584 /* Add headers */ 585 if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 586 return (-1); 587 588 /* Set chunked encoding */ 589 if (clt->clt_fcgi_chunked) { 590 /* XXX Should we keep and handle Content-Length instead? */ 591 key.kv_key = "Content-Length"; 592 if ((kv = kv_find(&resp->http_headers, &key)) != NULL) 593 kv_delete(&resp->http_headers, kv); 594 595 /* 596 * XXX What if the FastCGI added some kind of Transfer-Encoding? 597 * XXX like gzip, deflate or even "chunked"? 598 */ 599 if (kv_add(&resp->http_headers, 600 "Transfer-Encoding", "chunked") == NULL) 601 return (-1); 602 } 603 604 /* Is it a persistent connection? */ 605 if (clt->clt_persist) { 606 if (kv_add(&resp->http_headers, 607 "Connection", "keep-alive") == NULL) 608 return (-1); 609 } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) 610 return (-1); 611 612 /* Date header is mandatory and should be added as late as possible */ 613 if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || 614 kv_add(&resp->http_headers, "Date", tmbuf) == NULL) 615 return (-1); 616 617 /* Write initial header (fcgi might append more) */ 618 if (server_writeresponse_http(clt) == -1 || 619 server_bufferevent_print(clt, "\r\n") == -1 || 620 server_headers(clt, resp, server_writeheader_http, NULL) == -1 || 621 server_bufferevent_print(clt, "\r\n") == -1) 622 return (-1); 623 624 return (0); 625} 626 627int 628server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg) 629{ 630 struct server_fcgi_param *param = arg; 631 char *val, *name, *p; 632 const char *key; 633 int ret; 634 635 if (hdr->kv_flags & KV_FLAG_INVALID) 636 return (0); 637 638 /* The key might have been updated in the parent */ 639 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 640 key = hdr->kv_parent->kv_key; 641 else 642 key = hdr->kv_key; 643 644 val = hdr->kv_value; 645 646 if (strcasecmp(key, "Content-Length") == 0 || 647 strcasecmp(key, "Content-Type") == 0) { 648 if ((name = strdup(key)) == NULL) 649 return (-1); 650 } else { 651 if (asprintf(&name, "HTTP_%s", key) == -1) 652 return (-1); 653 } 654 655 for (p = name; *p != '\0'; p++) { 656 if (isalpha((unsigned char)*p)) 657 *p = toupper((unsigned char)*p); 658 else 659 *p = '_'; 660 } 661 662 ret = fcgi_add_param(param, name, val, clt); 663 free(name); 664 665 return (ret); 666} 667 668int 669server_fcgi_writechunk(struct client *clt) 670{ 671 struct evbuffer *evb = clt->clt_srvevb; 672 size_t len; 673 674 if (clt->clt_fcgi_type == FCGI_END_REQUEST) { 675 len = 0; 676 } else 677 len = EVBUFFER_LENGTH(evb); 678 679 if (clt->clt_fcgi_chunked) { 680 /* If len is 0, make sure to write the end marker only once */ 681 if (len == 0 && clt->clt_fcgi_end++) 682 return (0); 683 if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 || 684 server_bufferevent_write_chunk(clt, evb, len) == -1 || 685 server_bufferevent_print(clt, "\r\n") == -1) 686 return (-1); 687 } else if (len) 688 return (server_bufferevent_write_buffer(clt, evb)); 689 690 return (0); 691} 692 693int 694server_fcgi_getheaders(struct client *clt) 695{ 696 struct http_descriptor *resp = clt->clt_descresp; 697 struct evbuffer *evb = clt->clt_srvevb; 698 int code = 200; 699 char *line, *key, *value; 700 const char *errstr; 701 702 while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') { 703 key = line; 704 705 if ((value = strchr(key, ':')) == NULL) 706 break; 707 if (*value == ':') { 708 *value++ = '\0'; 709 value += strspn(value, " \t"); 710 } else { 711 *value++ = '\0'; 712 } 713 714 DPRINTF("%s: %s: %s", __func__, key, value); 715 716 if (strcasecmp("Status", key) == 0) { 717 value[strcspn(value, " \t")] = '\0'; 718 code = (int)strtonum(value, 100, 600, &errstr); 719 if (errstr != NULL || server_httperror_byid( 720 code) == NULL) 721 code = 200; 722 } else { 723 (void)kv_add(&resp->http_headers, key, value); 724 } 725 free(line); 726 } 727 728 return (code); 729} 730