static int parse_response(domain_t *domain) { int pret = -1, minor_version, status; size_t msg_len, buflen = domain->data.len, prevbuflen = 0, num_headers; const char *msg; num_headers = sizeof (domain->http.headers) / sizeof (domain->http.headers[0]); while (1) { pret = phr_parse_response(domain->data.buffer, buflen, &minor_version, &status, &msg, &msg_len, domain->http.headers, &num_headers, prevbuflen); domain->http.num_headers = num_headers; domain->http.status = status; if (pret > 0) break; else if (pret == -1) { // ParseError; break; } assert(pret == -2); if (buflen == sizeof (domain->data.buffer)) { // RequestIsTooLongError break; } } return pret; }
static void on_head(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; int minor_version, version, http_status, rlen, is_eos; const char *msg; #define MAX_HEADERS 100 h2o_header_t *headers; h2o_iovec_t *header_names; size_t msg_len, num_headers, i; h2o_socket_cb reader; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { on_error_before_head(client, "I/O error (head)"); return; } headers = h2o_mem_alloc_pool(client->super.pool, *headers, MAX_HEADERS); header_names = h2o_mem_alloc_pool(client->super.pool, *header_names, MAX_HEADERS); /* continue parsing the responses until we see a final one */ while (1) { /* parse response */ struct phr_header src_headers[MAX_HEADERS]; num_headers = MAX_HEADERS; rlen = phr_parse_response(sock->input->bytes, sock->input->size, &minor_version, &http_status, &msg, &msg_len, src_headers, &num_headers, 0); switch (rlen) { case -1: /* error */ on_error_before_head(client, "failed to parse the response"); return; case -2: /* incomplete */ h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } version = 0x100 | (minor_version != 0); /* fill-in the headers */ for (i = 0; i != num_headers; ++i) { const h2o_token_t *token; char *orig_name = h2o_strdup(client->super.pool, src_headers[i].name, src_headers[i].name_len).base; h2o_strtolower((char *)src_headers[i].name, src_headers[i].name_len); token = h2o_lookup_token(src_headers[i].name, src_headers[i].name_len); if (token != NULL) { headers[i].name = (h2o_iovec_t *)&token->buf; } else { header_names[i] = h2o_iovec_init(src_headers[i].name, src_headers[i].name_len); headers[i].name = &header_names[i]; } headers[i].value = h2o_iovec_init(src_headers[i].value, src_headers[i].value_len); headers[i].orig_name = orig_name; headers[i].flags = (h2o_header_flags_t){0}; } if (!(100 <= http_status && http_status <= 199 && http_status != 101)) break; if (client->super.informational_cb != NULL && client->super.informational_cb(&client->super, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers) != 0) { close_client(client); return; } h2o_buffer_consume(&client->sock->input, rlen); if (client->sock->input->size == 0) { h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } } client->super.timings.response_start_at = h2o_gettimeofday(client->super.ctx->loop); /* parse the headers */ reader = on_body_until_close; client->_do_keepalive = minor_version >= 1; for (i = 0; i != num_headers; ++i) { if (headers[i].name == &H2O_TOKEN_CONNECTION->buf) { if (h2o_contains_token(headers[i].value.base, headers[i].value.len, H2O_STRLIT("keep-alive"), ',')) { client->_do_keepalive = 1; } else { client->_do_keepalive = 0; } } else if (headers[i].name == &H2O_TOKEN_TRANSFER_ENCODING->buf) { if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("chunked"))) { /* precond: _body_decoder.chunked is zero-filled */ client->_body_decoder.chunked.decoder.consume_trailer = 1; reader = on_req_chunked; } else if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("identity"))) { /* continue */ } else { on_error_before_head(client, "unexpected type of transfer-encoding"); return; } } else if (headers[i].name == &H2O_TOKEN_CONTENT_LENGTH->buf) { if ((client->_body_decoder.content_length.bytesleft = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == SIZE_MAX) { on_error_before_head(client, "invalid content-length"); return; } if (reader != on_req_chunked) reader = on_body_content_length; } } /* RFC 2616 4.4 */ if (client->_method_is_head || http_status == 101 || http_status == 204 || http_status == 304) { is_eos = 1; client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); } else { is_eos = 0; /* close the connection if impossible to determine the end of the response (RFC 7230 3.3.3) */ if (reader == on_body_until_close) client->_do_keepalive = 0; } /* call the callback. sock may be stealed */ client->bytes_to_consume = rlen; client->super._cb.on_body = client->super._cb.on_head(&client->super, is_eos ? h2o_httpclient_error_is_eos : NULL, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers, 1); if (is_eos) { close_client(client); return; } else if (client->super._cb.on_body == NULL) { client->_do_keepalive = 0; close_client(client); return; } h2o_buffer_consume(&sock->input, client->bytes_to_consume); client->bytes_to_consume = 0; client->sock->bytes_read = client->sock->input->size; client->super._timeout.cb = on_body_timeout; h2o_socket_read_start(sock, reader); reader(client->sock, 0); #undef MAX_HEADERS }