static void test_chunked_failure(int line, const char *encoded, ssize_t expected) { struct phr_chunked_decoder dec = {0}; char *buf = strdup(encoded); size_t bufsz, i; ssize_t ret; note("testing failure at-once, source at line %d", line); bufsz = strlen(buf); ret = phr_decode_chunked(&dec, buf, &bufsz); ok(ret == expected); note("testing failure per-byte, source at line %d", line); memset(&dec, 0, sizeof(dec)); for (i = 0; encoded[i] != '\0'; ++i) { buf[0] = encoded[i]; bufsz = 1; ret = phr_decode_chunked(&dec, buf, &bufsz); if (ret == -1) { ok(ret == expected); return; } else if (ret == -2) { /* continue */ } else { ok(0); return; } } ok(ret == expected); free(buf); }
static void handle_chunked_entity_read(struct st_h2o_http1_conn_t *conn) { struct st_h2o_http1_chunked_entity_reader *reader = (void *)conn->_req_entity_reader; h2o_buffer_t *inbuf = conn->sock->input; size_t bufsz; ssize_t ret; /* decode the incoming data */ if ((bufsz = inbuf->size - reader->prev_input_size) == 0) return; ret = phr_decode_chunked(&reader->decoder, inbuf->bytes + reader->prev_input_size, &bufsz); inbuf->size = reader->prev_input_size + bufsz; reader->prev_input_size = inbuf->size; if (ret != -1 && inbuf->size - conn->_reqsize >= conn->super.ctx->globalconf->max_request_entity_size) { entity_read_send_error(conn, 413, "Request Entity Too Large", "request entity is too large"); return; } if (ret < 0) { if (ret == -2) { /* incomplete */ return; } /* error */ entity_read_send_error(conn, 400, "Invalid Request", "broken chunked-encoding"); return; } /* complete */ conn->req.entity = h2o_iovec_init(inbuf->bytes + conn->_reqsize, inbuf->size - conn->_reqsize); conn->_reqsize = inbuf->size; inbuf->size += ret; /* restore the number of extra bytes */ return on_entity_read_complete(conn); }
static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected) { struct phr_chunked_decoder dec = {0}; char *buf; size_t bufsz; ssize_t ret; dec.consume_trailer = consume_trailer; note("testing at-once, source at line %d", line); buf = strdup(encoded); bufsz = strlen(buf); ret = phr_decode_chunked(&dec, buf, &bufsz); ok(ret == expected); ok(bufsz == strlen(decoded)); ok(bufis(buf, bufsz, decoded)); if (expected >= 0) { if (ret == expected) ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret)); else ok(0); } free(buf); }
static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected) { struct phr_chunked_decoder dec = {0}; char *buf = malloc(strlen(encoded) + 1); size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i; ssize_t ret; dec.consume_trailer = consume_trailer; note("testing per-byte, source at line %d", line); for (i = 0; i < bytes_to_consume - 1; ++i) { buf[bytes_ready] = encoded[i]; bufsz = 1; ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz); if (ret != -2) { ok(0); return; } bytes_ready += bufsz; } strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1); bufsz = strlen(buf + bytes_ready); ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz); ok(ret == expected); bytes_ready += bufsz; ok(bytes_ready == strlen(decoded)); ok(bufis(buf, bytes_ready, decoded)); if (expected >= 0) { if (ret == expected) ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume)); else ok(0); } free(buf); }
static void on_req_chunked(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; h2o_buffer_t *inbuf; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { if (err == h2o_socket_error_closed && !phr_decode_chunked_is_in_data(&client->_body_decoder.chunked.decoder)) { /* * if the peer closed after a full chunk, treat this * as if the transfer had complete, browsers appear to ignore * a missing 0\r\n chunk */ client->_do_keepalive = 0; client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); client->super._cb.on_body(&client->super, h2o_httpclient_error_is_eos); close_client(client); } else { on_body_error(client, "I/O error (body; chunked)"); } return; } inbuf = client->sock->input; if (sock->bytes_read != 0) { const char *errstr; int cb_ret; size_t newsz = sock->bytes_read; switch (phr_decode_chunked(&client->_body_decoder.chunked.decoder, inbuf->bytes + inbuf->size - newsz, &newsz)) { case -1: /* error */ newsz = sock->bytes_read; client->_do_keepalive = 0; errstr = "failed to parse the response (chunked)"; break; case -2: /* incomplete */ errstr = NULL; break; default: /* complete, with garbage on tail; should disable keepalive */ client->_do_keepalive = 0; /* fallthru */ case 0: /* complete */ errstr = h2o_httpclient_error_is_eos; client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); break; } inbuf->size -= sock->bytes_read - newsz; cb_ret = client->super._cb.on_body(&client->super, errstr); if (errstr != NULL) { close_client(client); return; } else if (cb_ret != 0) { client->_do_keepalive = 0; close_client(client); return; } do_update_window(&client->super); } h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); }