コード例 #1
0
ファイル: socket.c プロジェクト: Robert-Xie/h2o
int decode_ssl_input(h2o_socket_t *sock)
{
    assert(sock->ssl != NULL);
    assert(sock->ssl->handshake.cb == NULL);

    while (sock->ssl->input.encrypted->size != 0 || SSL_pending(sock->ssl->ssl)) {
        int rlen;
        h2o_iovec_t buf = h2o_buffer_reserve(&sock->input, 4096);
        if (buf.base == NULL)
            return errno;
        { /* call SSL_read (while detecting SSL renegotiation and reporting it as error) */
            int did_write_in_read = 0;
            sock->ssl->did_write_in_read = &did_write_in_read;
            rlen = SSL_read(sock->ssl->ssl, buf.base, (int)buf.len);
            sock->ssl->did_write_in_read = NULL;
            if (did_write_in_read)
                return EIO;
        }
        if (rlen == -1) {
            if (SSL_get_error(sock->ssl->ssl, rlen) != SSL_ERROR_WANT_READ) {
                return EIO;
            }
            break;
        } else if (rlen == 0) {
            break;
        } else {
            sock->input->size += rlen;
        }
    }

    return 0;
}
コード例 #2
0
ファイル: serverutil.c プロジェクト: simudream/h2o
int h2o_read_command(const char *cmd, char **argv, h2o_buffer_t **resp, int *child_status)
{
    int respfds[2] = {-1, -1};
    pid_t pid = -1;
    int mutex_locked = 0, ret = -1;

    h2o_buffer_init(resp, &h2o_socket_buffer_prototype);

    pthread_mutex_lock(&cloexec_mutex);
    mutex_locked = 1;

    /* create pipe for reading the result */
    if (pipe(respfds) != 0)
        goto Exit;
    fcntl(respfds[0], F_SETFD, O_CLOEXEC);

    /* spawn */
    int mapped_fds[] = {respfds[1], 1, /* stdout of the child process is read from the pipe */
                        -1};
    if ((pid = h2o_spawnp(cmd, argv, mapped_fds, 1)) == -1)
        goto Exit;
    close(respfds[1]);
    respfds[1] = -1;

    pthread_mutex_unlock(&cloexec_mutex);
    mutex_locked = 0;

    /* read the response from pipe */
    while (1) {
        h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192);
        ssize_t r;
        while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR)
            ;
        if (r <= 0)
            break;
        (*resp)->size += r;
    }

Exit:
    if (mutex_locked)
        pthread_mutex_unlock(&cloexec_mutex);
    if (pid != -1) {
        /* wait for the child to complete */
        pid_t r;
        while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR)
            ;
        if (r == pid) {
            /* success */
            ret = 0;
        }
    }
    if (respfds[0] != -1)
        close(respfds[0]);
    if (respfds[1] != -1)
        close(respfds[1]);
    if (ret != 0)
        h2o_buffer_dispose(resp);

    return ret;
}
コード例 #3
0
ファイル: chunked.c プロジェクト: LakhanRathi92/h2o
static mrb_value send_chunked_method(mrb_state *mrb, mrb_value self)
{
    h2o_mruby_generator_t *generator = h2o_mruby_current_generator;
    const char *s;
    mrb_int len;

    /* parse args */
    mrb_get_args(mrb, "s", &s, &len);

    { /* precond check */
        mrb_value exc = check_precond(mrb, generator);
        if (!mrb_nil_p(exc))
            mrb_exc_raise(mrb, exc);
    }

    /* append to send buffer, and send out immediately if necessary */
    if (len != 0) {
        h2o_mruby_chunked_t *chunked = generator->chunked;
        if (chunked->bytes_left != SIZE_MAX) {
            if (len > chunked->bytes_left)
                len = chunked->bytes_left;
            chunked->bytes_left -= len;
        }
        if (len != 0) {
            h2o_buffer_reserve(&chunked->callback.receiving, len);
            memcpy(chunked->callback.receiving->bytes + chunked->callback.receiving->size, s, len);
            chunked->callback.receiving->size += len;
            if (chunked->sending.bytes_inflight == 0)
                do_send(generator, &chunked->callback.receiving, 0);
        }
    }

    return mrb_nil_value();
}
コード例 #4
0
ファイル: serverutil.c プロジェクト: zhangjinde/h2o
int h2o_read_command(const char *cmd, char **argv, h2o_buffer_t **resp, int *child_status)
{
    int respfds[2] = {-1, -1};
    posix_spawn_file_actions_t file_actions;
    pid_t pid = -1;
    int ret = -1;
    extern char **environ;

    h2o_buffer_init(resp, &h2o_socket_buffer_prototype);

    /* create pipe for reading the result */
    if (pipe(respfds) != 0)
        goto Exit;

    /* spawn */
    posix_spawn_file_actions_init(&file_actions);
    posix_spawn_file_actions_adddup2(&file_actions, respfds[1], 1);
    if ((errno = posix_spawnp(&pid, cmd, &file_actions, NULL, argv, environ)) != 0) {
        pid = -1;
        goto Exit;
    }
    close(respfds[1]);
    respfds[1] = -1;

    /* read the response from pipe */
    while (1) {
        h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192);
        ssize_t r;
        while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR)
            ;
        if (r <= 0)
            break;
        (*resp)->size += r;
    }

Exit:
    if (pid != -1) {
        /* wait for the child to complete */
        pid_t r;
        while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR)
            ;
        if (r == pid) {
            /* success */
            ret = 0;
        }
    }
    if (respfds[0] != -1)
        close(respfds[0]);
    if (respfds[1] != -1)
        close(respfds[1]);
    if (ret != 0)
        h2o_buffer_dispose(resp);

    return ret;
}
コード例 #5
0
ファイル: stream.c プロジェクト: bowlofstew/h2o
static h2o_iovec_t *send_data_push(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream, h2o_iovec_t *bufs, size_t bufcnt, int is_final)
{
    ssize_t max_payload_size = 0, payload_size = 0;
    size_t data_header_offset = 0;

    for (; bufcnt != 0; ++bufs, --bufcnt) {
        while (bufs->len != 0) {
            size_t fill_size;
            /* encode the header, and allocate space for the next header */
            if (payload_size == max_payload_size) {
                if (payload_size != 0)
                    encode_data_header_and_consume_window(conn, stream, (uint8_t*)conn->_write.buf->bytes + data_header_offset, payload_size, 0);
                if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
                    goto Exit;
                h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE);
                data_header_offset = conn->_write.buf->size;
                conn->_write.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE;
                payload_size = 0;
            }
            /* emit payload */
            fill_size = sz_min(max_payload_size, bufs->len);
            memcpy(h2o_buffer_reserve(&conn->_write.buf, fill_size).base, bufs->base, fill_size);
            conn->_write.buf->size += fill_size;
            bufs->base += fill_size;
            bufs->len -= fill_size;
            payload_size += fill_size;
        }
    }
    /* all data have been emitted */
    if (payload_size != 0) {
        encode_data_header_and_consume_window(conn, stream, (uint8_t*)conn->_write.buf->bytes + data_header_offset, payload_size, is_final);
    } else if (is_final) {
        encode_data_header_and_consume_window(
            conn, stream,
            (void*)h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE).base,
            0, 1);
        conn->_write.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE;
    }

Exit:
    return bufs;
}
コード例 #6
0
ファイル: http_request.c プロジェクト: simudream/h2o
static void post_error(struct st_h2o_mruby_http_request_context_t *ctx, const char *errstr)
{
    static const struct phr_header headers_sorted[] = {{H2O_STRLIT("content-type"), H2O_STRLIT("text/plain; charset=utf-8")}};

    ctx->client = NULL;
    size_t errstr_len = strlen(errstr);
    h2o_buffer_reserve(&ctx->resp.after_closed, errstr_len);
    memcpy(ctx->resp.after_closed->bytes + ctx->resp.after_closed->size, errstr, errstr_len);
    ctx->resp.after_closed->size += errstr_len;
    ctx->resp.has_content = 1;

    post_response(ctx, 500, headers_sorted, sizeof(headers_sorted) / sizeof(headers_sorted[0]));
}
コード例 #7
0
ファイル: stream.c プロジェクト: LakhanRathi92/h2o
static h2o_iovec_t *send_data_push(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream, h2o_iovec_t *bufs, size_t bufcnt,
                                   h2o_send_state_t send_state)
{
    h2o_iovec_t dst;
    size_t max_payload_size;

    if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
        goto Exit;

    /* reserve buffer and point dst to the payload */
    dst.base =
        h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE + max_payload_size).base + H2O_HTTP2_FRAME_HEADER_SIZE;
    dst.len = max_payload_size;

    /* emit data */
    while (bufcnt != 0) {
        if (bufs->len != 0)
            break;
        ++bufs;
        --bufcnt;
    }
    while (bufcnt != 0) {
        size_t fill_size = sz_min(dst.len, bufs->len);
        memcpy(dst.base, bufs->base, fill_size);
        dst.base += fill_size;
        dst.len -= fill_size;
        bufs->base += fill_size;
        bufs->len -= fill_size;
        while (bufs->len == 0) {
            ++bufs;
            --bufcnt;
            if (bufcnt == 0)
                break;
        }
        if (dst.len == 0)
            break;
    }

    /* commit the DATA frame if we have actually emitted payload */
    if (dst.len != max_payload_size || !h2o_send_state_is_in_progress(send_state)) {
        size_t payload_len = max_payload_size - dst.len;
        if (bufcnt != 0) {
            send_state = H2O_SEND_STATE_IN_PROGRESS;
        }
        commit_data_header(conn, stream, &conn->_write.buf, payload_len, send_state);
    }

Exit:
    return bufs;
}
コード例 #8
0
ファイル: stream.c プロジェクト: adam-hanna/h2o
static h2o_iovec_t *send_data_push(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream, h2o_iovec_t *bufs, size_t bufcnt,
                                   int is_final)
{
    h2o_iovec_t dst;
    size_t max_payload_size;

    if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
        goto Exit;

    /* reserve buffer and point dst to the payload */
    dst.base =
        h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE + max_payload_size).base + H2O_HTTP2_FRAME_HEADER_SIZE;
    dst.len = max_payload_size;

    /* emit data */
    while (bufcnt != 0) {
        if (bufs->len != 0)
            break;
        ++bufs;
        --bufcnt;
    }
    while (bufcnt != 0) {
        size_t fill_size = sz_min(dst.len, bufs->len);
        memcpy(dst.base, bufs->base, fill_size);
        dst.base += fill_size;
        dst.len -= fill_size;
        bufs->base += fill_size;
        bufs->len -= fill_size;
        while (bufs->len == 0) {
            ++bufs;
            --bufcnt;
            if (bufcnt == 0)
                break;
        }
        if (dst.len == 0)
            break;
    }

    /* commit the DATA frame if we have actually emitted payload */
    if (dst.len != max_payload_size || is_final) {
        size_t payload_len = max_payload_size - dst.len;
        encode_data_header_and_consume_window(conn, stream, (uint8_t *)conn->_write.buf->bytes + conn->_write.buf->size,
                                              payload_len, is_final && bufcnt == 0);
        conn->_write.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE + payload_len;
    }

Exit:
    return bufs;
}
コード例 #9
0
ファイル: stream.c プロジェクト: LakhanRathi92/h2o
static h2o_send_state_t send_data_pull(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
{
    size_t max_payload_size;
    h2o_iovec_t cbuf;
    h2o_send_state_t send_state = H2O_SEND_STATE_IN_PROGRESS;

    if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
        goto Exit;
    /* reserve buffer */
    h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE + max_payload_size);
    /* obtain content */
    cbuf.base = conn->_write.buf->bytes + conn->_write.buf->size + H2O_HTTP2_FRAME_HEADER_SIZE;
    cbuf.len = max_payload_size;
    send_state = h2o_pull(&stream->req, stream->_pull_cb, &cbuf);
    /* write the header */
    commit_data_header(conn, stream, &conn->_write.buf, cbuf.len, send_state);

Exit:
    return send_state;
}
コード例 #10
0
ファイル: requests.c プロジェクト: LakhanRathi92/h2o
static int collect_req_status(h2o_req_t *req, void *_cbdata)
{
    struct st_collect_req_status_cbdata_t *cbdata = _cbdata;

    /* collect log */
    char buf[4096];
    size_t len = sizeof(buf);
    char *logline = h2o_log_request(cbdata->logconf, req, &len, buf);
    assert(len != 0);
    --len; /* omit trailing LF */

    /* append to buffer */
    h2o_buffer_reserve(&cbdata->buffer, len + 3);
    memcpy(cbdata->buffer->bytes + cbdata->buffer->size, logline, len);
    cbdata->buffer->size += len;

    if (logline != buf)
        free(logline);

    return 0;
}
コード例 #11
0
ファイル: http_request.c プロジェクト: simudream/h2o
static int flatten_request_header(h2o_mruby_context_t *handler_ctx, h2o_iovec_t name, h2o_iovec_t value, void *_ctx)
{
    struct st_h2o_mruby_http_request_context_t *ctx = _ctx;

    /* ignore certain headers */
    if (h2o_lcstris(name.base, name.len, H2O_STRLIT("content-length")) ||
        h2o_lcstris(name.base, name.len, H2O_STRLIT("connection")) || h2o_lcstris(name.base, name.len, H2O_STRLIT("host")))
        return 0;

    /* mark the existence of transfer-encoding in order to prevent us from adding content-length header */
    if (h2o_lcstris(name.base, name.len, H2O_STRLIT("transfer-encoding")))
        ctx->req.has_transfer_encoding = 1;

    h2o_buffer_reserve(&ctx->req.buf, name.len + value.len + sizeof(": \r\n") - 1);
    append_to_buffer(&ctx->req.buf, name.base, name.len);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT(": "));
    append_to_buffer(&ctx->req.buf, value.base, value.len);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT("\r\n"));

    return 0;
}
コード例 #12
0
ファイル: stream.c プロジェクト: bowlofstew/h2o
static int send_data_pull(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
{
    size_t max_payload_size;
    h2o_iovec_t cbuf;
    int is_final = 0;

    do {
        if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
            break;
        /* reserve buffer */
        h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE + max_payload_size);
        /* obtain content */
        cbuf.base = conn->_write.buf->bytes + conn->_write.buf->size + H2O_HTTP2_FRAME_HEADER_SIZE;
        cbuf.len = max_payload_size;
        is_final = h2o_pull(&stream->req, stream->_pull_cb, &cbuf);
        /* write the header */
        encode_data_header_and_consume_window(conn, stream, (void*)(conn->_write.buf->bytes + conn->_write.buf->size), cbuf.len, is_final);
        /* adjust the write buf size */
        conn->_write.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE + cbuf.len;
    } while (! is_final);

    return is_final;
}
コード例 #13
0
ファイル: socket.c プロジェクト: deweerdt/h2o
static void proceed_handshake(h2o_socket_t *sock, const char *err)
{
    h2o_iovec_t first_input = {NULL};
    int ret;

    sock->_cb.write = NULL;

    if (err != NULL) {
        goto Complete;
    }

    if (sock->ssl->handshake.server.async_resumption.state == ASYNC_RESUMPTION_STATE_RECORD) {
        if (sock->ssl->input.encrypted->size <= 1024) {
            /* retain a copy of input if performing async resumption */
            first_input = h2o_iovec_init(alloca(sock->ssl->input.encrypted->size), sock->ssl->input.encrypted->size);
            memcpy(first_input.base, sock->ssl->input.encrypted->bytes, first_input.len);
        } else {
            sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE;
        }
    }

Redo:
    if (SSL_is_server(sock->ssl->ssl)) {
        ret = SSL_accept(sock->ssl->ssl);
    } else {
        ret = SSL_connect(sock->ssl->ssl);
    }

    switch (sock->ssl->handshake.server.async_resumption.state) {
    case ASYNC_RESUMPTION_STATE_RECORD:
        /* async resumption has not been triggered; proceed the state to complete */
        sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE;
        break;
    case ASYNC_RESUMPTION_STATE_REQUEST_SENT: {
        /* sent async request, reset the ssl state, and wait for async response */
        assert(ret < 0);
        SSL_CTX *ssl_ctx = SSL_get_SSL_CTX(sock->ssl->ssl);
        SSL_free(sock->ssl->ssl);
        create_ssl(sock, ssl_ctx);
        clear_output_buffer(sock->ssl);
        h2o_buffer_consume(&sock->ssl->input.encrypted, sock->ssl->input.encrypted->size);
        h2o_buffer_reserve(&sock->ssl->input.encrypted, first_input.len);
        memcpy(sock->ssl->input.encrypted->bytes, first_input.base, first_input.len);
        sock->ssl->input.encrypted->size = first_input.len;
        h2o_socket_read_stop(sock);
        return;
    }
    default:
        break;
    }

    if (ret == 0 || (ret < 0 && SSL_get_error(sock->ssl->ssl, ret) != SSL_ERROR_WANT_READ)) {
        /* failed */
        long verify_result = SSL_get_verify_result(sock->ssl->ssl);
        if (verify_result != X509_V_OK) {
            err = X509_verify_cert_error_string(verify_result);
        } else {
            err = "ssl handshake failure";
        }
        goto Complete;
    }

    if (sock->ssl->output.bufs.size != 0) {
        h2o_socket_read_stop(sock);
        flush_pending_ssl(sock, ret == 1 ? on_handshake_complete : proceed_handshake);
    } else {
        if (ret == 1) {
            if (!SSL_is_server(sock->ssl->ssl)) {
                X509 *cert = SSL_get_peer_certificate(sock->ssl->ssl);
                if (cert != NULL) {
                    switch (validate_hostname(sock->ssl->handshake.client.server_name, cert)) {
                    case MatchFound:
                        /* ok */
                        break;
                    case MatchNotFound:
                        err = h2o_socket_error_ssl_cert_name_mismatch;
                        break;
                    default:
                        err = h2o_socket_error_ssl_cert_invalid;
                        break;
                    }
                    X509_free(cert);
                } else {
                    err = h2o_socket_error_ssl_no_cert;
                }
            }
            goto Complete;
        }
        if (sock->ssl->input.encrypted->size != 0)
            goto Redo;
        h2o_socket_read_start(sock, proceed_handshake);
    }
    return;

Complete:
    h2o_socket_read_stop(sock);
    on_handshake_complete(sock, err);
}
コード例 #14
0
ファイル: serverutil.c プロジェクト: LakhanRathi92/h2o
int h2o_read_command(const char *cmd, char **argv, h2o_buffer_t **resp, int *child_status)
{
    int respfds[2] = {-1, -1};
    pid_t pid = -1;
    int mutex_locked = 0, ret = -1;

    h2o_buffer_init(resp, &h2o_socket_buffer_prototype);
#ifndef _MSC_VER
    pthread_mutex_lock(&cloexec_mutex);
    mutex_locked = 1;
#else
	uv_mutex_lock(&cloexec_mutex);
	mutex_locked = 1;
#endif
    /* create pipe for reading the result */
#ifdef _MSC_VER
	if (_pipe(respfds, 4096, O_BINARY) == -1) //on fail
											  //-- : Debug{
											  //printf("Pipe Failed \n");
		goto Exit;
#else
	if (pipe(respfds) != 0) //on fail
		goto Exit;
#endif

#ifndef _MSC_VER
    fcntl(respfds[0], F_SETFD, O_CLOEXEC);
#endif
    /* spawn */
    int mapped_fds[] = {respfds[1], 1, /* stdout of the child process is read from the pipe */
                        -1};
    if ((pid = h2o_spawnp(cmd, argv, mapped_fds, 1)) == -1)
        goto Exit;
    close(respfds[1]);
    respfds[1] = -1;
#ifndef _MSC_VER
    pthread_mutex_unlock(&cloexec_mutex);
#else
	uv_mutex_unlock(&cloexec_mutex);
#endif
    mutex_locked = 0;

    /* read the response from pipe */
    while (1) {
        h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192);
        ssize_t r;
#ifndef _MSC_VER
        while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR)
            ;
#else
		while ((r = _read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR)
			;
#endif
        if (r <= 0)
            break;
        (*resp)->size += r;
    }

Exit:
    if (mutex_locked)
#ifndef _MSC_VER
        pthread_mutex_unlock(&cloexec_mutex);
#else
		uv_mutex_unlock(&cloexec_mutex);
#endif
	//Child _ waiting doesn't work same way in Windows so
#ifndef _MSC_VER
    if (pid != -1) {
        /* wait for the child to complete */
        pid_t r;
        while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR)
            ;
        if (r == pid) {
            /* success */
            ret = 0;
        }
    }
#else
	//If you can come up with a way to wait for a child to exist in Windows put here.s
#endif
#ifndef _MSC_VER
    if (respfds[0] != -1)
        close(respfds[0]);
    if (respfds[1] != -1)
        close(respfds[1]);
#else
	if (respfds[0] != -1)
		_close(respfds[0]);
	if (respfds[1] != -1)
		_close(respfds[1]);
#endif
    if (ret != 0)
        h2o_buffer_dispose(resp);

    return ret;
}
コード例 #15
0
ファイル: frame.c プロジェクト: Wanderfalke/h2o
static uint8_t *allocate_frame(h2o_buffer_t **buf, size_t length, uint8_t type, uint8_t flags, int32_t stream_id)
{
    h2o_iovec_t alloced = h2o_buffer_reserve(buf, H2O_HTTP2_FRAME_HEADER_SIZE + length);
    (*buf)->size += H2O_HTTP2_FRAME_HEADER_SIZE + length;
    return h2o_http2_encode_frame_header((uint8_t*)alloced.base, length, type, flags, stream_id);
}
コード例 #16
0
ファイル: http_request.c プロジェクト: simudream/h2o
static mrb_value http_request_method(mrb_state *mrb, mrb_value self)
{
    h2o_mruby_generator_t *generator;
    struct st_h2o_mruby_http_request_context_t *ctx;
    const char *arg_url;
    mrb_int arg_url_len;
    mrb_value arg_hash;
    h2o_iovec_t method;
    h2o_url_t url;

    /* parse args */
    arg_hash = mrb_nil_value();
    mrb_get_args(mrb, "s|H", &arg_url, &arg_url_len, &arg_hash);

    /* precond check */
    if ((generator = h2o_mruby_current_generator) == NULL || generator->req == NULL)
        mrb_exc_raise(mrb, create_downstream_closed_exception(mrb));

    /* allocate context and initialize */
    ctx = h2o_mem_alloc_shared(&generator->req->pool, sizeof(*ctx), on_dispose);
    memset(ctx, 0, sizeof(*ctx));
    ctx->generator = generator;
    ctx->receiver = mrb_nil_value();
    h2o_buffer_init(&ctx->req.buf, &h2o_socket_buffer_prototype);
    h2o_buffer_init(&ctx->resp.after_closed, &h2o_socket_buffer_prototype);
    ctx->refs.request = mrb_nil_value();
    ctx->refs.input_stream = mrb_nil_value();

    /* uri */
    if (h2o_url_parse(arg_url, arg_url_len, &url) != 0)
        mrb_raise(mrb, E_ARGUMENT_ERROR, "invaild URL");

    /* method */
    method = h2o_iovec_init(H2O_STRLIT("GET"));
    if (mrb_hash_p(arg_hash)) {
        mrb_value t = mrb_hash_get(mrb, arg_hash, mrb_symbol_value(generator->ctx->symbols.sym_method));
        if (!mrb_nil_p(t)) {
            t = mrb_str_to_str(mrb, t);
            method = h2o_iovec_init(RSTRING_PTR(t), RSTRING_LEN(t));
        }
    }

    /* start building the request */
    h2o_buffer_reserve(&ctx->req.buf, method.len + 1);
    append_to_buffer(&ctx->req.buf, method.base, method.len);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT(" "));
    h2o_buffer_reserve(&ctx->req.buf,
                       url.path.len + url.authority.len + sizeof(" HTTP/1.1\r\nConnection: close\r\nHost: \r\n") - 1);
    append_to_buffer(&ctx->req.buf, url.path.base, url.path.len);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT(" HTTP/1.1\r\nConnection: close\r\nHost: "));
    append_to_buffer(&ctx->req.buf, url.authority.base, url.authority.len);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT("\r\n"));

    /* headers */
    if (mrb_hash_p(arg_hash)) {
        mrb_value headers = mrb_hash_get(mrb, arg_hash, mrb_symbol_value(generator->ctx->symbols.sym_headers));
        if (!mrb_nil_p(headers)) {
            if (h2o_mruby_iterate_headers(generator->ctx, headers, flatten_request_header, ctx) != 0) {
                mrb_value exc = mrb_obj_value(mrb->exc);
                mrb->exc = NULL;
                mrb_exc_raise(mrb, exc);
            }
        }
    }
    /* body */
    if (mrb_hash_p(arg_hash)) {
        mrb_value body = mrb_hash_get(mrb, arg_hash, mrb_symbol_value(generator->ctx->symbols.sym_body));
        if (!mrb_nil_p(body)) {
            if (mrb_obj_eq(mrb, body, generator->rack_input)) {
                /* fast path */
                mrb_int pos;
                mrb_input_stream_get_data(mrb, body, NULL, NULL, &pos, NULL, NULL);
                ctx->req.body = generator->req->entity;
                ctx->req.body.base += pos;
                ctx->req.body.len -= pos;
            } else {
                if (!mrb_string_p(body)) {
                    body = mrb_funcall(mrb, body, "read", 0);
                    if (!mrb_string_p(body))
                        mrb_raise(mrb, E_ARGUMENT_ERROR, "body.read did not return string");
                }
                ctx->req.body = h2o_strdup(&ctx->generator->req->pool, RSTRING_PTR(body), RSTRING_LEN(body));
            }
            if (!ctx->req.has_transfer_encoding) {
                char buf[64];
                size_t l = (size_t)sprintf(buf, "content-length: %zu\r\n", ctx->req.body.len);
                h2o_buffer_reserve(&ctx->req.buf, l);
                append_to_buffer(&ctx->req.buf, buf, l);
            }
        }
    }

    h2o_buffer_reserve(&ctx->req.buf, 2);
    append_to_buffer(&ctx->req.buf, H2O_STRLIT("\r\n"));

    /* build request and connect */
    h2o_http1client_connect(&ctx->client, ctx, &generator->req->conn->ctx->proxy.client_ctx, url.host, h2o_url_get_port(&url),
                            on_connect);

    ctx->refs.request = h2o_mruby_create_data_instance(mrb, mrb_ary_entry(generator->ctx->constants, H2O_MRUBY_HTTP_REQUEST_CLASS),
                                                       ctx, &request_type);
    return ctx->refs.request;
}