static void on_connection_ready(struct st_h2o_http1client_t *client) { h2o_iovec_t proxy_protocol = h2o_iovec_init(NULL, 0); int chunked = 0; h2o_iovec_t connection_header = h2o_iovec_init(NULL, 0); h2o_httpclient_properties_t props = { &proxy_protocol, &chunked, &connection_header, }; h2o_iovec_t method; h2o_url_t url; h2o_header_t *headers; size_t num_headers; h2o_iovec_t body; client->super._cb.on_head = client->super._cb.on_connect(&client->super, NULL, &method, &url, (const h2o_header_t **)&headers, &num_headers, &body, &client->proceed_req, &props, client->_origin); if (client->super._cb.on_head == NULL) { close_client(client); return; } h2o_iovec_t reqbufs[3]; size_t reqbufcnt = 0; if (props.proxy_protocol->base != NULL) reqbufs[reqbufcnt++] = *props.proxy_protocol; reqbufs[reqbufcnt++] = build_request(client, method, url, *props.connection_header, headers, num_headers); client->_is_chunked = *props.chunked; client->_method_is_head = h2o_memis(method.base, method.len, H2O_STRLIT("HEAD")); if (client->proceed_req != NULL) { if (body.base != NULL) { h2o_buffer_init(&client->_body_buf, &h2o_socket_buffer_prototype); if (h2o_buffer_append(&client->_body_buf, body.base, body.len) == 0) { on_send_request(client->sock, "Internal error"); return; } } h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_req_body_done); } else { if (client->_is_chunked) { assert(body.base != NULL); reqbufcnt += encode_chunk(client, reqbufs + reqbufcnt, body); } else if (body.base != NULL) { reqbufs[reqbufcnt++] = body; } h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_send_request); } /* TODO no need to set the timeout if all data has been written into TCP sendbuf */ client->super._timeout.cb = on_send_timeout; h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); client->super.timings.request_begin_at = h2o_gettimeofday(client->super.ctx->loop); }
void finalostream_send(h2o_ostream_t *_self, h2o_req_t *req, h2o_iovec_t *inbufs, size_t inbufcnt, int is_final) { struct st_h2o_http1_finalostream_t *self = (void *)_self; struct st_h2o_http1_conn_t *conn = (struct st_h2o_http1_conn_t *)req->conn; h2o_iovec_t *bufs = alloca(sizeof(h2o_iovec_t) * (inbufcnt + 1)); int bufcnt = 0; assert(self == &conn->_ostr_final); if (!self->sent_headers) { conn->req.timestamps.response_start_at = *h2o_get_timestamp(conn->super.ctx, NULL, NULL); /* build headers and send */ const char *connection = req->http1_is_persistent ? "keep-alive" : "close"; bufs[bufcnt].base = h2o_mem_alloc_pool( &req->pool, flatten_headers_estimate_size(req, conn->super.ctx->globalconf->server_name.len + strlen(connection))); bufs[bufcnt].len = flatten_headers(bufs[bufcnt].base, req, connection); ++bufcnt; self->sent_headers = 1; } memcpy(bufs + bufcnt, inbufs, sizeof(h2o_iovec_t) * inbufcnt); bufcnt += inbufcnt; if (bufcnt != 0) { h2o_socket_write(conn->sock, bufs, bufcnt, is_final ? on_send_complete : on_send_next_push); } else { on_send_complete(conn->sock, 0); } }
static void on_read(h2o_socket_t *sock, const char *err) { struct st_h2o_tunnel_t *tunnel = sock->data; h2o_socket_t *dst; assert(tunnel != NULL); assert(tunnel->sock[0] == sock || tunnel->sock[1] == sock); if (err != NULL) { close_connection(tunnel); return; } if (sock->bytes_read == 0) return; h2o_socket_read_stop(sock); reset_timeout(tunnel); if (tunnel->sock[0] == sock) dst = tunnel->sock[1]; else dst = tunnel->sock[0]; h2o_iovec_t buf; buf.base = sock->input->bytes; buf.len = sock->input->size; h2o_socket_write(dst, &buf, 1, on_write_complete); }
static void send_bad_request(struct st_h2o_http1_conn_t *conn) { const static h2o_iovec_t resp = {H2O_STRLIT("HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Connection: close\r\n" "Content-Length: 11\r\n" "\r\n" "Bad Request")}; assert(conn->req.version == 0 && "request has not been parsed successfully"); h2o_socket_write(conn->sock, (h2o_iovec_t *)&resp, 1, send_bad_request_on_complete); }
static int do_write_req(h2o_httpclient_t *_client, h2o_iovec_t chunk, int is_end_stream) { struct st_h2o_http1client_t *client = (struct st_h2o_http1client_t *)_client; client->_body_buf_is_done = is_end_stream; if (client->_body_buf == NULL) h2o_buffer_init(&client->_body_buf, &h2o_socket_buffer_prototype); if (chunk.len != 0) { if (h2o_buffer_append(&client->_body_buf, chunk.base, chunk.len) == 0) return -1; } if (client->sock->_cb.write != NULL) return 0; assert(client->_body_buf_in_flight == NULL || client->_body_buf_in_flight->size == 0); swap_buffers(&client->_body_buf, &client->_body_buf_in_flight); if (client->_body_buf_in_flight->size == 0) { /* return immediately if the chunk is empty */ on_req_body_done(client->sock, NULL); return 0; } h2o_iovec_t iov = h2o_iovec_init(client->_body_buf_in_flight->bytes, client->_body_buf_in_flight->size); if (client->_is_chunked) { h2o_iovec_t bufs[3]; size_t bufcnt = encode_chunk(client, bufs, iov); h2o_socket_write(client->sock, bufs, bufcnt, on_req_body_done); } else { h2o_socket_write(client->sock, &iov, 1, on_req_body_done); } return 0; }
void h2o_http1_upgrade(h2o_http1_conn_t *conn, h2o_iovec_t *inbufs, size_t inbufcnt, h2o_http1_upgrade_cb on_complete, void *user_data) { h2o_iovec_t *bufs = alloca(sizeof(h2o_iovec_t) * (inbufcnt + 1)); conn->upgrade.data = user_data; conn->upgrade.cb = on_complete; bufs[0].base = h2o_mem_alloc_pool( &conn->req.pool, flatten_headers_estimate_size(&conn->req, conn->super.ctx->globalconf->server_name.len + sizeof("upgrade") - 1)); bufs[0].len = flatten_headers(bufs[0].base, &conn->req, "upgrade"); memcpy(bufs + 1, inbufs, sizeof(h2o_iovec_t) * inbufcnt); h2o_socket_write(conn->sock, bufs, (int)(inbufcnt + 1), on_upgrade_complete); }
static void proceed_pull(struct st_h2o_http1_conn_t *conn, size_t nfilled) { h2o_iovec_t buf = {conn->_ostr_final.pull.buf, nfilled}; int is_final; if (buf.len < MAX_PULL_BUF_SZ) { h2o_iovec_t cbuf = {buf.base + buf.len, MAX_PULL_BUF_SZ - buf.len}; is_final = h2o_pull(&conn->req, conn->_ostr_final.pull.cb, &cbuf); buf.len += cbuf.len; } else { is_final = 0; } /* write */ h2o_socket_write(conn->sock, &buf, 1, is_final ? on_send_complete : on_send_next_pull); }
void h2o_http1_upgrade(h2o_req_t *req, h2o_iovec_t *inbufs, size_t inbufcnt, h2o_http1_upgrade_cb on_complete, void *user_data) { struct st_h2o_http1_conn_t *conn = (void *)req->conn; assert(req->version <= 0x200); /* TODO find a better way to assert instanceof(req->conn) == struct st_h2o_http1_conn_t */ h2o_iovec_t *bufs = alloca(sizeof(h2o_iovec_t) * (inbufcnt + 1)); conn->upgrade.data = user_data; conn->upgrade.cb = on_complete; bufs[0].base = h2o_mem_alloc_pool(&conn->req.pool, flatten_headers_estimate_size(&conn->req, conn->super.ctx->globalconf->server_name.len + sizeof("upgrade") - 1)); bufs[0].len = flatten_headers(bufs[0].base, &conn->req, "upgrade"); memcpy(bufs + 1, inbufs, sizeof(h2o_iovec_t) * inbufcnt); h2o_socket_write(conn->sock, bufs, inbufcnt + 1, on_upgrade_complete); }
void h2o_websocket_proceed(h2o_websocket_conn_t *conn) { int handled; /* run the loop until getting to a point where no more progress can be achieved */ do { handled = 0; if (!h2o_socket_is_writing(conn->sock) && wslay_event_want_write(conn->ws_ctx)) { if (wslay_event_send(conn->ws_ctx) != 0) { goto Close; } /* avoid infinite loop when user want send more bufers count than ours in on_msg_callback() */ if (conn->_write_buf.cnt < sizeof(conn->_write_buf.bufs) / sizeof(conn->_write_buf.bufs[0])) { handled = 1; } } if (conn->sock->input->size != 0 && wslay_event_want_read(conn->ws_ctx)) { if (wslay_event_recv(conn->ws_ctx) != 0) { goto Close; } handled = 1; } } while (handled); if (!h2o_socket_is_writing(conn->sock) && conn->_write_buf.cnt > 0) { /* write */ h2o_socket_write(conn->sock, conn->_write_buf.bufs, conn->_write_buf.cnt, on_write_complete); } if (wslay_event_want_read(conn->ws_ctx)) { h2o_socket_read_start(conn->sock, on_recv); } else if (h2o_socket_is_writing(conn->sock) || wslay_event_want_write(conn->ws_ctx)) { h2o_socket_read_stop(conn->sock); } else { /* nothing is going on... close the socket */ goto Close; } return; Close: on_close(conn); }
static void proceed_pull(struct st_h2o_http1_conn_t *conn, size_t nfilled) { h2o_iovec_t buf = {conn->_ostr_final.pull.buf, nfilled}; h2o_send_state_t send_state; if (buf.len < MAX_PULL_BUF_SZ) { h2o_iovec_t cbuf = {buf.base + buf.len, MAX_PULL_BUF_SZ - buf.len}; send_state = h2o_pull(&conn->req, conn->_ostr_final.pull.cb, &cbuf); if (send_state == H2O_SEND_STATE_ERROR) { conn->req.http1_is_persistent = 0; } buf.len += cbuf.len; } else { send_state = H2O_SEND_STATE_IN_PROGRESS; } /* write */ h2o_socket_write(conn->sock, &buf, 1, h2o_send_state_is_in_progress(send_state) ? on_send_next_pull : on_send_complete); }
static ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *_conn) { h2o_websocket_conn_t *conn = _conn; h2o_iovec_t buf; /* return WOULDBLOCK if pending (TODO: queue fixed number of chunks, instead of only one) */ if (h2o_socket_is_writing(conn->sock)) { wslay_event_set_error(conn->ws_ctx, WSLAY_ERR_WOULDBLOCK); return -1; } /* copy data */ conn->_write_buf = h2o_mem_realloc(conn->_write_buf, len); memcpy(conn->_write_buf, data, len); /* write */ buf.base = conn->_write_buf; buf.len = len; h2o_socket_write(conn->sock, &buf, 1, on_write_complete); return len; }
static void on_send_request(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { on_error_before_head(client, "I/O error (send request)"); return; } if (client->_is_chunked) { client->_is_chunked = 0; h2o_iovec_t last = h2o_iovec_init(H2O_STRLIT("0\r\n")); h2o_socket_write(client->sock, &last, 1, on_send_request); return; } client->super.timings.request_end_at = h2o_gettimeofday(client->super.ctx->loop); h2o_socket_read_start(client->sock, on_head); client->super._timeout.cb = on_head_timeout; h2o_timer_link(client->super.ctx->loop, client->super.ctx->first_byte_timeout, &client->super._timeout); }
static void handle_incoming_request(struct st_h2o_http1_conn_t *conn) { size_t inreqlen = conn->sock->input->size < H2O_MAX_REQLEN ? conn->sock->input->size : H2O_MAX_REQLEN; int reqlen, minor_version; struct phr_header headers[H2O_MAX_HEADERS]; size_t num_headers = H2O_MAX_HEADERS; ssize_t entity_body_header_index; h2o_iovec_t expect; /* need to set request_begin_at here for keep-alive connection */ if (conn->req.timestamps.request_begin_at.tv_sec == 0) conn->req.timestamps.request_begin_at = *h2o_get_timestamp(conn->super.ctx, NULL, NULL); reqlen = phr_parse_request(conn->sock->input->bytes, inreqlen, (const char **)&conn->req.input.method.base, &conn->req.input.method.len, (const char **)&conn->req.input.path.base, &conn->req.input.path.len, &minor_version, headers, &num_headers, conn->_prevreqlen); conn->_prevreqlen = inreqlen; switch (reqlen) { default: // parse complete conn->_reqsize = reqlen; if ((entity_body_header_index = fixup_request(conn, headers, num_headers, minor_version, &expect)) != -1) { conn->req.timestamps.request_body_begin_at = *h2o_get_timestamp(conn->super.ctx, NULL, NULL); if (expect.base != NULL) { if (!h2o_lcstris(expect.base, expect.len, H2O_STRLIT("100-continue"))) { set_timeout(conn, NULL, NULL); h2o_socket_read_stop(conn->sock); h2o_send_error(&conn->req, 417, "Expectation Failed", "unknown expectation", H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION); return; } static const h2o_iovec_t res = {H2O_STRLIT("HTTP/1.1 100 Continue\r\n\r\n")}; h2o_socket_write(conn->sock, (void *)&res, 1, on_continue_sent); } if (create_entity_reader(conn, headers + entity_body_header_index) != 0) { return; } if (expect.base != NULL) { /* processing of the incoming entity is postponed until the 100 response is sent */ h2o_socket_read_stop(conn->sock); return; } conn->_req_entity_reader->handle_incoming_entity(conn); } else { set_timeout(conn, NULL, NULL); h2o_socket_read_stop(conn->sock); process_request(conn); } return; case -2: // incomplete if (inreqlen == H2O_MAX_REQLEN) { // request is too long (TODO notify) close_connection(conn, 1); } return; case -1: // error /* upgrade to HTTP/2 if the request starts with: PRI * HTTP/2 */ if (conn->super.ctx->globalconf->http1.upgrade_to_http2) { /* should check up to the first octet that phr_parse_request returns an error */ static const h2o_iovec_t HTTP2_SIG = {H2O_STRLIT("PRI * HTTP/2")}; if (conn->sock->input->size >= HTTP2_SIG.len && memcmp(conn->sock->input->bytes, HTTP2_SIG.base, HTTP2_SIG.len) == 0) { h2o_accept_ctx_t accept_ctx = {conn->super.ctx, conn->super.hosts}; h2o_socket_t *sock = conn->sock; struct timeval connected_at = conn->super.connected_at; /* destruct the connection after detatching the socket */ conn->sock = NULL; close_connection(conn, 1); /* and accept as http2 connection */ h2o_http2_accept(&accept_ctx, sock, connected_at); return; } } close_connection(conn, 1); return; } }