int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { data_string *ds; UNUSED(srv); if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, keylen); buffer_copy_string_len(ds->value, value, vallen); array_insert_unique(con->response.headers, (data_unset *)ds); return 0; }
static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *ns; const char *s; int line = 0; UNUSED(srv); buffer_copy_buffer(p->parse_response, in); for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1, line++) { const char *key, *value; int key_len; data_string *ds; /* strip the \n */ ns[0] = '\0'; if (ns > s && ns[-1] == '\r') ns[-1] = '\0'; if (line == 0 && 0 == strncmp(s, "HTTP/1.", 7)) { /* non-parsed header ... we parse them anyway */ if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') { int status; /* after the space should be a status code for us */ status = strtol(s+9, NULL, 10); if (status >= 100 && status < 1000) { /* we expected 3 digits and didn't got them */ con->parsed_response |= HTTP_STATUS; con->http_status = status; } } } else { /* parse the headers */ key = s; if (NULL == (value = strchr(s, ':'))) { /* we expect: "<key>: <value>\r\n" */ continue; } key_len = value - key; value += 1; /* skip LWS */ while (*value == ' ' || *value == '\t') value++; if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); array_insert_unique(con->response.headers, (data_unset *)ds); switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { con->parsed_response |= HTTP_DATE; } break; case 6: if (0 == strncasecmp(key, "Status", key_len)) { int status = strtol(value, NULL, 10); if (status >= 100 && status < 1000) { con->http_status = status; con->parsed_response |= HTTP_STATUS; } else { con->http_status = 502; } } break; case 8: if (0 == strncasecmp(key, "Location", key_len)) { con->parsed_response |= HTTP_LOCATION; } break; case 10: if (0 == strncasecmp(key, "Connection", key_len)) { con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0; con->parsed_response |= HTTP_CONNECTION; } break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { con->response.content_length = strtoul(value, NULL, 10); con->parsed_response |= HTTP_CONTENT_LENGTH; } break; default: break; } } } /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { con->http_status = 302; } return 0; }
static int cgi_demux_response(server *srv, connection *con, plugin_data *p) { cgi_session *sess = con->plugin_ctx[p->id]; switch(srv->network_backend_read(srv, con, sess->sock, sess->rb)) { case NETWORK_STATUS_CONNECTION_CLOSE: fdevent_event_del(srv->ev, sess->sock); /* connection closed. close the read chunkqueue. */ sess->rb->is_closed = 1; case NETWORK_STATUS_SUCCESS: /* we got content */ break; case NETWORK_STATUS_WAIT_FOR_EVENT: return 0; default: /* oops */ ERROR("%s", "oops, read-pipe-read failed and I don't know why"); return -1; } /* looks like we got some content * * split off the header from the incoming stream */ if (con->file_started == 0) { size_t i; int have_content_length = 0; http_response_reset(p->resp); /* the response header is not fully received yet, * * extract the http-response header from the rb-cq */ switch (http_response_parse_cq(sess->rb, p->resp)) { case PARSE_UNSET: case PARSE_ERROR: /* parsing failed */ TRACE("%s", "response parser failed"); con->http_status = 502; /* Bad Gateway */ return -1; case PARSE_NEED_MORE: if (sess->rb->is_closed) { /* backend died before sending a header */ con->http_status = 502; /* Bad Gateway */ return -1; } return 0; case PARSE_SUCCESS: con->http_status = p->resp->status; chunkqueue_remove_finished_chunks(sess->rb); /* copy the http-headers */ for (i = 0; i < p->resp->headers->used; i++) { const char *ign[] = { "Status", "Connection", NULL }; size_t j; data_string *ds; data_string *header = (data_string *)p->resp->headers->data[i]; /* some headers are ignored by default */ for (j = 0; ign[j]; j++) { if (0 == strcasecmp(ign[j], header->key->ptr)) break; } if (ign[j]) continue; if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Location"))) { /* CGI/1.1 rev 03 - 7.2.1.2 */ if (con->http_status == 0) con->http_status = 302; } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Content-Length"))) { have_content_length = 1; } if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_buffer(ds->key, header->key); buffer_copy_string_buffer(ds->value, header->value); array_insert_unique(con->response.headers, (data_unset *)ds); } con->file_started = 1; /* if Status: ... is not set, 200 is our default status-code */ if (con->http_status == 0) con->http_status = 200; sess->state = CGI_STATE_READ_RESPONSE_CONTENT; if (con->request.http_version == HTTP_VERSION_1_1 && !have_content_length) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } break; } } /* FIXME: pass the response-header to the other plugins to * setup the filter-queue * * - use next-queue instead of con->write_queue */ /* copy the resopnse content */ cgi_copy_response(srv, con, sess); joblist_append(srv, con); return 0; }
static int proxy_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *s, *ns; int http_response_status = -1; UNUSED(srv); /* \r\n -> \0\0 */ buffer_copy_string_buffer(p->parse_response, in); for (s = p->parse_response->ptr; NULL != (ns = strstr(s, "\r\n")); s = ns + 2) { char *key, *value; int key_len; data_string *ds; int copy_header; ns[0] = '\0'; ns[1] = '\0'; if (-1 == http_response_status) { /* The first line of a Response message is the Status-Line */ for (key=s; *key && *key != ' '; key++); if (*key) { http_response_status = (int) strtol(key, NULL, 10); if (http_response_status <= 0) http_response_status = 502; } else { http_response_status = 502; } con->http_status = http_response_status; con->parsed_response |= HTTP_STATUS; continue; } if (NULL == (value = strchr(s, ':'))) { /* now we expect: "<key>: <value>\n" */ continue; } key = s; key_len = value - key; value++; /* strip WS */ while (*value == ' ' || *value == '\t') value++; copy_header = 1; switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { con->parsed_response |= HTTP_DATE; } break; case 8: if (0 == strncasecmp(key, "Location", key_len)) { con->parsed_response |= HTTP_LOCATION; } break; case 10: if (0 == strncasecmp(key, "Connection", key_len)) { copy_header = 0; } break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { con->response.content_length = strtol(value, NULL, 10); con->parsed_response |= HTTP_CONTENT_LENGTH; } break; default: break; } if (copy_header) { if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); array_insert_unique(con->response.headers, (data_unset *)ds); } } return 0; }