Exemple #1
0
static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason, h2o_buf_t mime_type)
{
    /* link the request */
    self->req = req;

    /* setup response */
    req->res.status = status;
    req->res.reason = reason;
    req->res.content_length = self->bytesleft;
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len);
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->last_modified_buf, H2O_TIMESTR_RFC1123_LEN);
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ETAG, self->etag_buf, self->etag_len);

    /* send data */
    h2o_start_response(req, &self->super);

    if (req->_ostr_top->start_pull != NULL) {
        req->_ostr_top->start_pull(req->_ostr_top, do_pull);
    } else {
        size_t bufsz = MAX_BUF_SIZE;
        if (self->bytesleft < bufsz)
            bufsz = self->bytesleft;
        self->buf = h2o_mempool_alloc(&req->pool, bufsz);
        do_proceed(&self->super, req);
    }
}
Exemple #2
0
h2o_websocket_conn_t *h2o_upgrade_to_websocket(h2o_req_t *req, const char *client_key, void *data, h2o_websocket_msg_callback cb)
{
    h2o_websocket_conn_t *conn = h2o_mem_alloc(sizeof(*conn));
    char accept_key[29];

    /* only for http1 connection */
    assert(req->version < 0x200);

    /* setup the context */
    memset(conn, 0, sizeof(*conn));
    // conn->sock = sock; set by on_complete
    conn->ws_callbacks.recv_callback = recv_callback;
    conn->ws_callbacks.send_callback = send_callback;
    conn->ws_callbacks.on_msg_recv_callback = on_msg_callback;
    conn->data = data;
    conn->cb = cb;

    wslay_event_context_server_init(&conn->ws_ctx, &conn->ws_callbacks, conn);

    /* build response */
    create_accept_key(accept_key, client_key);
    req->res.status = 101;
    req->res.reason = "Switching Protocols";
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT("websocket"));
    h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("sec-websocket-accept"), 0, NULL, accept_key,
                          strlen(accept_key));

    /* send */
    h2o_http1_upgrade(req, NULL, 0, on_complete, conn);

    return conn;
}
Exemple #3
0
static int send_dir_listing(h2o_req_t *req, const char *path, size_t path_len, int is_get)
{
    static h2o_generator_t generator = {NULL, NULL};
    DIR *dp;
    h2o_buffer_t *body;
    h2o_iovec_t bodyvec;

    /* build html */
    if ((dp = opendir(path)) == NULL)
        return -1;
    body = build_dir_listing_html(&req->pool, req->path_normalized, dp);
    closedir(dp);

    bodyvec = h2o_iovec_init(body->bytes, body->size);
    h2o_buffer_link_to_pool(body, &req->pool);

    /* send response */
    req->res.status = 200;
    req->res.reason = "OK";
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8"));

    /* send headers */
    if (!is_get) {
        h2o_send_inline(req, NULL, 0);
        return 0;
    }

    /* send data */
    h2o_start_response(req, &generator);
    h2o_send(req, &bodyvec, 1, 1);
    return 0;
}
Exemple #4
0
static void on_setup_ostream(h2o_filter_t *self, h2o_req_t *req, h2o_ostream_t **slot)
{
    chunked_encoder_t *encoder;

    /* do nothing if content-length is known */
    if (req->res.content_length != SIZE_MAX)
        goto Next;
    /* RFC 2616 4.4 states that the following status codes (and response to a HEAD method) should not include message body */
    if ((100 <= req->res.status && req->res.status <= 199) || req->res.status == 204 || req->res.status == 304)
        goto Next;
    else if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD")) == 0)
        goto Next;
    /* we cannot handle certain responses (like 101 switching protocols) */
    if (req->res.status != 200) {
        req->http1_is_persistent = 0;
        goto Next;
    }
    /* skip if content-encoding header is being set */
    if (h2o_find_header(&req->res.headers, H2O_TOKEN_TRANSFER_ENCODING, -1) != -1)
        goto Next;

    /* set content-encoding header */
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_TRANSFER_ENCODING, H2O_STRLIT("chunked"));

    /* setup filter */
    encoder = (void*)h2o_add_ostream(req, sizeof(chunked_encoder_t), slot);
    encoder->super.do_send = send_chunk;
    slot = &encoder->super.next;

Next:
    h2o_setup_next_ostream(self, req, slot);
}
Exemple #5
0
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status, h2o_iovec_t msg, struct phr_header *headers, size_t num_headers)
{
    struct rp_generator_t *self = client->data;
    size_t i;

    if (errstr != NULL && errstr != h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_send_error(self->src_req, 502, "Gateway Error", errstr, 0);
        return NULL;
    }

    /* copy the response */
    self->src_req->res.status = status;
    self->src_req->res.reason = h2o_strdup(&self->src_req->pool, msg.base, msg.len).base;
    for (i = 0; i != num_headers; ++i) {
        const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len);
        h2o_iovec_t value;
        if (token != NULL) {
            if (token->is_connection_specific) {
                goto Skip;
            }
            if (token == H2O_TOKEN_CONTENT_LENGTH) {
                if (self->src_req->res.content_length != SIZE_MAX
                    || (self->src_req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) {
                    self->client = NULL;
                    h2o_send_error(self->src_req, 502, "Gateway Error", "invalid response from upstream", 0);
                    return NULL;
                }
                goto Skip;
            } else if (token == H2O_TOKEN_LOCATION) {
                value = rewrite_location(&self->src_req->pool, headers[i].value, headers[i].value_len, self->upstream, self->src_req->scheme, self->src_req->authority, self->src_req->pathconf->path);
                goto AddHeader;
            }
            /* default behaviour, transfer the header downstream */
            value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len);
        AddHeader:
            h2o_add_header(&self->src_req->pool, &self->src_req->res.headers, token, value.base, value.len);
        Skip:
            ;
        } else {
            h2o_iovec_t name = h2o_strdup(&self->src_req->pool, headers[i].name, headers[i].name_len);
            h2o_iovec_t value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len);
            h2o_add_header_by_str(&self->src_req->pool, &self->src_req->res.headers, name.base, name.len, 0, value.base, value.len);
        }
    }

    /* declare the start of the response */
    h2o_start_response(self->src_req, &self->super);

    if (errstr == h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_send(self->src_req, NULL, 0, 1);
        return NULL;
    }

    return on_body;
}
Exemple #6
0
static int reproxy_test(h2o_handler_t *self, h2o_req_t *req)
{
    if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
        return -1;

    req->res.status = 200;
    req->res.reason = "OK";
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_X_REPROXY_URL, H2O_STRLIT("http://example.com:81/bar"));
    h2o_send_inline(req, H2O_STRLIT("you should never see this!\n"));

    return 0;
}
Exemple #7
0
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body)
{
    /* FIXME setup host_config, since this function may get called before h2o_process_request is invoked */
    req->http1_is_persistent = 0;

    req->res.status = status;
    req->res.reason = reason;
    memset(&req->res.headers, 0, sizeof(req->res.headers));
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"));

    h2o_send_inline(req, body, SIZE_MAX);
}
Exemple #8
0
static int redirect_to_dir(h2o_req_t *req, const char *path, size_t path_len)
{
    static h2o_generator_t generator = { NULL, NULL };
    static const h2o_buf_t body_prefix = {
        H2O_STRLIT("<!DOCTYPE html><TITLE>301 Moved Permanently</TITLE><P>The document has moved <A HREF=\"")
    };
    static const h2o_buf_t body_suffix = {
        H2O_STRLIT("\">here</A>")
    };

    h2o_buf_t url;
    size_t alloc_size;
    h2o_buf_t bufs[3];

    /* determine the size of the memory needed */
    alloc_size = sizeof(":///") + req->scheme.len + req->authority.len + path_len;

    /* allocate and build url */
    url.base = h2o_mempool_alloc(&req->pool, alloc_size);
    url.len = sprintf(url.base, "%.*s://%.*s%.*s/", (int)req->scheme.len, req->scheme.base, (int)req->authority.len, req->authority.base, (int)path_len, path);
    assert(url.len + 1 == alloc_size);

    /* build response header */
    req->res.status = 301;
    req->res.reason = "Moved Permanently";
    memset(&req->res.headers, 0, sizeof(req->res.headers));
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, url.base, url.len);
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8"));

    /* build response */
    bufs[0] = body_prefix;
    bufs[1] = h2o_htmlescape(&req->pool, url.base, url.len);
    bufs[2] = body_suffix;

    /* send */
    h2o_start_response(req, &generator);
    h2o_send(req, bufs, 3, 1);

    return 0;
}
Exemple #9
0
static void add_headers_unconditional(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req)
{
    /* RFC 7232 4.1: The server generating a 304 response MUST generate any of the following header fields that would have been sent
     * in a 200 (OK) response to the same request: Cache-Control, Content-Location, Date, ETag, Expires, and Vary (snip) a sender
     * SHOULD NOT generate representation metadata other than the above listed fields unless said metadata exists for the purpose of
     * guiding cache updates. */
    if (self->send_etag) {
        size_t etag_len = h2o_filecache_get_etag(self->file.ref, self->header_bufs.etag);
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ETAG, self->header_bufs.etag, etag_len);
    }
    if (self->send_vary)
        h2o_set_header_token(&req->pool, &req->res.headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding"));
}
Exemple #10
0
void h2o_set_header(h2o_mempool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value, size_t value_len, int overwrite_if_exists)
{
    ssize_t cursor = h2o_find_header(headers, token, -1);
    if (cursor != -1) {
        if (overwrite_if_exists) {
            h2o_buf_t *slot = &headers->entries[cursor].value;
            slot->base = (char*)value;
            slot->len = value_len;
        }
    } else {
        h2o_add_header(pool, headers, token, value, value_len);
    }
}
Exemple #11
0
static ssize_t init_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers, const struct phr_header *src, size_t len,
                            h2o_iovec_t *connection, h2o_iovec_t *host, h2o_iovec_t *upgrade, h2o_iovec_t *expect)
{
    ssize_t entity_header_index = -1;

    assert(headers->size == 0);

    /* setup */
    if (len != 0) {
        size_t i;
        h2o_vector_reserve(pool, headers, len);
        for (i = 0; i != len; ++i) {
            const h2o_token_t *name_token;
            char orig_case[src[i].name_len];

            /* preserve the original case */
            memcpy(orig_case, src[i].name, src[i].name_len);
            /* convert to lower-case in-place */
            h2o_strtolower((char *)src[i].name, src[i].name_len);
            if ((name_token = h2o_lookup_token(src[i].name, src[i].name_len)) != NULL) {
                if (name_token->is_init_header_special) {
                    if (name_token == H2O_TOKEN_HOST) {
                        host->base = (char *)src[i].value;
                        host->len = src[i].value_len;
                    } else if (name_token == H2O_TOKEN_CONTENT_LENGTH) {
                        if (entity_header_index == -1)
                            entity_header_index = i;
                    } else if (name_token == H2O_TOKEN_TRANSFER_ENCODING) {
                        entity_header_index = i;
                    } else if (name_token == H2O_TOKEN_EXPECT) {
                        expect->base = (char *)src[i].value;
                        expect->len = src[i].value_len;
                    } else if (name_token == H2O_TOKEN_UPGRADE) {
                        upgrade->base = (char *)src[i].value;
                        upgrade->len = src[i].value_len;
                    } else {
                        assert(!"logic flaw");
                    }
                } else {
                    h2o_add_header(pool, headers, name_token, orig_case, src[i].value, src[i].value_len);
                    if (name_token == H2O_TOKEN_CONNECTION)
                        *connection = headers->entries[headers->size - 1].value;
                }
            } else {
                h2o_add_header_by_str(pool, headers, src[i].name, src[i].name_len, 0, orig_case, src[i].value, src[i].value_len);
            }
        }
    }

    return entity_header_index;
}
Exemple #12
0
void h2o_rewrite_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers, h2o_headers_command_t *cmd)
{
    h2o_header_t *target;

    switch (cmd->cmd) {
    case H2O_HEADERS_CMD_ADD:
        goto AddHeader;
    case H2O_HEADERS_CMD_APPEND:
        if ((target = find_header(headers, cmd)) == NULL)
            goto AddHeader;
        goto AppendToken;
    case H2O_HEADERS_CMD_MERGE:
        if ((target = find_header(headers, cmd)) == NULL)
            goto AddHeader;
        if (h2o_contains_token(target->value.base, target->value.len, cmd->value.base, cmd->value.len, ','))
            return;
        goto AppendToken;
    case H2O_HEADERS_CMD_SET:
        remove_header(headers, cmd);
        goto AddHeader;
    case H2O_HEADERS_CMD_SETIFEMPTY:
        if (find_header(headers, cmd) != NULL)
            return;
        goto AddHeader;
    case H2O_HEADERS_CMD_UNSET:
        remove_header(headers, cmd);
        return;
    }

    assert(!"FIXME");
    return;

AddHeader:
    if (h2o_iovec_is_token(cmd->name)) {
        h2o_add_header(pool, headers, (void *)cmd->name, NULL, cmd->value.base, cmd->value.len);
    } else {
        h2o_add_header_by_str(pool, headers, cmd->name->base, cmd->name->len, 0, NULL, cmd->value.base, cmd->value.len);
    }
    return;

AppendToken:
    if (target->value.len != 0) {
        h2o_iovec_t v;
        v.len = target->value.len + 2 + cmd->value.len;
        v.base = h2o_mem_alloc_pool(pool, char, v.len);
        memcpy(v.base, target->value.base, target->value.len);
        v.base[target->value.len] = ',';
        v.base[target->value.len + 1] = ' ';
        memcpy(v.base + target->value.len + 2, cmd->value.base, cmd->value.len);
        target->value = v;
    } else {
Exemple #13
0
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
{
    bind_conf(req);

    if ((flags & H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION) != 0)
        req->http1_is_persistent = 0;

    req->res.status = status;
    req->res.reason = reason;
    req->res.content_length = strlen(body);
    memset(&req->res.headers, 0, sizeof(req->res.headers));
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"));

    h2o_send_inline(req, body, SIZE_MAX);
}
Exemple #14
0
static int post_test(h2o_handler_t *self, h2o_req_t *req)
{
    if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST"))
        && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/post-test"))) {
        static h2o_generator_t generator = { NULL, NULL };
        req->res.status = 200;
        req->res.reason = "OK";
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"));
        h2o_start_response(req, &generator);
        h2o_send(req, &req->entity, 1, 1);
        return 0;
    }

    return -1;
}
Exemple #15
0
static int chunked_test(h2o_handler_t *self, h2o_req_t *req)
{
    if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))
        && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/chunked-test"))) {
        static h2o_generator_t generator = { NULL, NULL };
        h2o_buf_t body = h2o_strdup(&req->pool, "hello world\n", SIZE_MAX);
        req->res.status = 200;
        req->res.reason = "OK";
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain"));
        h2o_start_response(req, &generator);
        h2o_send(req, &body, 1, 1);
        return 0;
    }

    return -1;
}
Exemple #16
0
static mrb_value h2o_mrb_req_send_file(mrb_state *mrb, mrb_value self)
{
    h2o_mruby_internal_context_t *mruby_ctx = (h2o_mruby_internal_context_t *)mrb->ud;
    char *fn;
    int status;
    h2o_iovec_t content_type;
    int content_type_header_removed = 0;

    if (mruby_ctx->state != H2O_MRUBY_STATE_UNDETERMINED)
        mrb_raise(mrb, E_RUNTIME_ERROR, "response already sent");

    mrb_get_args(mrb, "z", &fn);

    /* determine status and reason to be used */
    if ((status = mruby_ctx->req->res.status) == 0)
        status = 200;

    { /* determine content-type (removing existing header, since it is added by h2o_file_send) */
        ssize_t header_index;
        if ((header_index = h2o_find_header(&mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) != -1) {
            content_type = mruby_ctx->req->res.headers.entries[header_index].value;
            h2o_delete_header(&mruby_ctx->req->res.headers, header_index);
            content_type_header_removed = 1;
        } else {
            const char *ext = h2o_get_filext(fn, strlen(fn));
            h2o_mimemap_type_t *m = h2o_mimemap_get_type_by_extension(mruby_ctx->req->pathconf->mimemap, ext);
            if (m == NULL || m->type != H2O_MIMEMAP_TYPE_MIMETYPE) {
                m = h2o_mimemap_get_default_type(mruby_ctx->req->pathconf->mimemap);
                assert(m->type == H2O_MIMEMAP_TYPE_MIMETYPE);
            }
            content_type = m->data.mimetype;
        }
    }

    if (h2o_file_send(mruby_ctx->req, status, mruby_ctx->req->res.reason, fn, content_type, H2O_FILE_FLAG_SEND_GZIP) == 0) {
        /* succeeded, return true */
        mruby_ctx->state = H2O_MRUBY_STATE_RESPONSE_SENT;
        return mrb_true_value();
    } else {
        /* failed, restore content-type header and return false */
        if (content_type_header_removed)
            h2o_add_header(&mruby_ctx->req->pool, &mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, content_type.base,
                           content_type.len);
        return mrb_false_value();
    }
}
Exemple #17
0
ssize_t h2o_set_header_token(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value,
                             size_t value_len)
{
    size_t found = -1;
    size_t i;
    for (i = 0; i != headers->size; ++i) {
        if (headers->entries[i].name == &token->buf) {
            if (h2o_contains_token(headers->entries[i].value.base, headers->entries[i].value.len, value, value_len, ','))
                return -1;
            found = i;
        }
    }
    if (found != -1) {
        h2o_header_t *dest = headers->entries + found;
        dest->value = h2o_concat(pool, dest->value, h2o_iovec_init(H2O_STRLIT(", ")), h2o_iovec_init(value, value_len));
        return found;
    } else {
        return h2o_add_header(pool, headers, token, NULL, value, value_len);
    }
}
Exemple #18
0
static void on_setup_ostream(h2o_filter_t *_self, h2o_req_t *req, h2o_ostream_t **slot)
{
    struct st_compress_filter_t *self = (void *)_self;
    struct st_compress_encoder_t *encoder;
    int compressible_types;
    h2o_compress_context_t *compressor;
    ssize_t i;

    if (req->version < 0x101)
        goto Next;
    if (req->res.status != 200)
        goto Next;
    if (h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")))
        goto Next;
    if (req->res.mime_attr == NULL)
        h2o_req_fill_mime_attributes(req);
    if (!req->res.mime_attr->is_compressible)
        goto Next;
    if (req->res.content_length < self->args.min_size)
        goto Next;
    /* skip if failed to gather the list of compressible types */
    if ((compressible_types = h2o_get_compressible_types(&req->headers)) == 0)
        goto Next;

    /* skip if content-encoding header is being set (as well as obtain the location of accept-ranges) */
    size_t content_encoding_header_index = -1, accept_ranges_header_index = -1;
    for (i = 0; i != req->res.headers.size; ++i) {
        if (req->res.headers.entries[i].name == &H2O_TOKEN_CONTENT_ENCODING->buf)
            content_encoding_header_index = i;
        else if (req->res.headers.entries[i].name == &H2O_TOKEN_ACCEPT_RANGES->buf)
            accept_ranges_header_index = i;
        else
            continue;
    }
    if (content_encoding_header_index != -1)
        goto Next;

/* open the compressor */
#ifndef _MSC_VER
#else
#define H2O_USE_BROTLI 0
#endif

#if H2O_USE_BROTLI
    if (self->args.brotli.quality != -1 && (compressible_types & H2O_COMPRESSIBLE_BROTLI) != 0) {
        compressor = h2o_compress_brotli_open(&req->pool, self->args.brotli.quality, req->res.content_length);
    } else
#endif
        if (self->args.gzip.quality != -1 && (compressible_types & H2O_COMPRESSIBLE_GZIP) != 0) {
        compressor = h2o_compress_gzip_open(&req->pool, self->args.gzip.quality);
    } else {
        goto Next;
    }

    /* adjust the response headers */
    req->res.content_length = SIZE_MAX;
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, compressor->name.base, compressor->name.len);
    h2o_set_header_token(&req->pool, &req->res.headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding"));
    if (accept_ranges_header_index != -1) {
        req->res.headers.entries[accept_ranges_header_index].value = h2o_iovec_init(H2O_STRLIT("none"));
    } else {
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("none"));
    }

    /* setup filter */
    encoder = (void *)h2o_add_ostream(req, sizeof(*encoder), slot);
    encoder->super.do_send = do_send;
    slot = &encoder->super.next;
    encoder->compressor = compressor;

    /* adjust preferred chunk size (compress by 8192 bytes) */
    if (req->preferred_chunk_size > BUF_SIZE)
        req->preferred_chunk_size = BUF_SIZE;

Next:
    h2o_setup_next_ostream(req, slot);
}
Exemple #19
0
static void on_setup_ostream(h2o_filter_t *self, h2o_req_t *req, h2o_ostream_t **slot)
{
    gzip_encoder_t *encoder;
    ssize_t i;

    if (req->version < 0x101)
        goto Next;
    if (req->res.status != 200)
        goto Next;
    if (h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")))
        goto Next;
    if (req->res.mime_attr == NULL)
        h2o_req_fill_mime_attributes(req);
    if (!req->res.mime_attr->is_compressible)
        goto Next;
    /* 100 is a rough estimate */
    if (req->res.content_length <= 100)
        goto Next;
    /* skip if no accept-encoding is set */
    if ((i = h2o_find_header(&req->headers, H2O_TOKEN_ACCEPT_ENCODING, -1)) == -1)
        goto Next;
    if (!h2o_contains_token(req->headers.entries[i].value.base, req->headers.entries[i].value.len, H2O_STRLIT("gzip"), ','))
        goto Next;

    /* skip if content-encoding header is being set (as well as obtain the location of accept-ranges */
    size_t content_encoding_header_index = -1, accept_ranges_header_index = -1;
    for (i = 0; i != req->res.headers.size; ++i) {
        if (req->res.headers.entries[i].name == &H2O_TOKEN_CONTENT_ENCODING->buf)
            content_encoding_header_index = i;
        else if (req->res.headers.entries[i].name == &H2O_TOKEN_ACCEPT_RANGES->buf)
            accept_ranges_header_index = i;
        else
            continue;
    }
    if (content_encoding_header_index != -1)
        goto Next;

    /* adjust the response headers */
    req->res.content_length = SIZE_MAX;
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, H2O_STRLIT("gzip"));
    h2o_add_header_token(&req->pool, &req->res.headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding"));
    if (accept_ranges_header_index != -1) {
        req->res.headers.entries[accept_ranges_header_index].value = h2o_iovec_init(H2O_STRLIT("none"));
    } else {
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("none"));
    }

    /* setup filter */
    encoder = (void *)h2o_add_ostream(req, sizeof(gzip_encoder_t), slot);
    encoder->super.do_send = send_gzip;
    encoder->super.stop = stop_gzip;
    slot = &encoder->super.next;

    encoder->bufs.capacity = 0;
    encoder->bufs.size = 0;
    encoder->zstream.zalloc = gzip_encoder_alloc;
    encoder->zstream.zfree = gzip_encoder_free;
    encoder->zstream.opaque = encoder;

    /* adjust preferred chunk size (compress by 8192 bytes) */
    if (req->preferred_chunk_size > BUF_SIZE)
        req->preferred_chunk_size = BUF_SIZE;

Next:
    h2o_setup_next_ostream(req, slot);
}
Exemple #20
0
static int serve_with_generator(struct st_h2o_sendfile_generator_t *generator, h2o_req_t *req, const char *rpath, size_t rpath_len,
                                h2o_mimemap_type_t *mime_type)
{
    enum { METHOD_IS_GET, METHOD_IS_HEAD, METHOD_IS_OTHER } method_type;
    size_t if_modified_since_header_index, if_none_match_header_index;
    size_t range_header_index;

    /* determine the method */
    if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) {
        method_type = METHOD_IS_GET;
    } else if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD"))) {
        method_type = METHOD_IS_HEAD;
    } else {
        method_type = METHOD_IS_OTHER;
    }

    /* if-non-match and if-modified-since */
    if ((if_none_match_header_index = h2o_find_header(&req->headers, H2O_TOKEN_IF_NONE_MATCH, SIZE_MAX)) != -1) {
        h2o_iovec_t *if_none_match = &req->headers.entries[if_none_match_header_index].value;
        char etag[H2O_FILECACHE_ETAG_MAXLEN + 1];
        size_t etag_len = h2o_filecache_get_etag(generator->file.ref, etag);
        if (h2o_memis(if_none_match->base, if_none_match->len, etag, etag_len))
            goto NotModified;
    } else if ((if_modified_since_header_index = h2o_find_header(&req->headers, H2O_TOKEN_IF_MODIFIED_SINCE, SIZE_MAX)) != -1) {
        h2o_iovec_t *ims_vec = &req->headers.entries[if_modified_since_header_index].value;
        struct tm ims_tm, *last_modified_tm;
        if (h2o_time_parse_rfc1123(ims_vec->base, ims_vec->len, &ims_tm) == 0) {
            last_modified_tm = h2o_filecache_get_last_modified(generator->file.ref, NULL);
            if (!tm_is_lessthan(&ims_tm, last_modified_tm))
                goto NotModified;
        }
    }

    /* obtain mime type */
    if (mime_type->type == H2O_MIMEMAP_TYPE_DYNAMIC) {
        do_close(&generator->super, req);
        return delegate_dynamic_request(req, req->path_normalized.len, rpath, rpath_len, mime_type);
    }
    assert(mime_type->type == H2O_MIMEMAP_TYPE_MIMETYPE);

    /* only allow GET or POST for static files */
    if (method_type == METHOD_IS_OTHER) {
        do_close(&generator->super, req);
        send_method_not_allowed(req);
        return 0;
    }

    /* if-range */
    if ((range_header_index = h2o_find_header(&req->headers, H2O_TOKEN_RANGE, SIZE_MAX)) != -1) {
        h2o_iovec_t *range = &req->headers.entries[range_header_index].value;
        size_t *range_infos, range_count;
        range_infos = process_range(&req->pool, range, generator->bytesleft, &range_count);
        if (range_infos == NULL) {
            h2o_iovec_t content_range;
            content_range.base = h2o_mem_alloc_pool(&req->pool, 32);
            content_range.len = sprintf(content_range.base, "bytes */%zu", generator->bytesleft);
            h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len);
            h2o_send_error(req, 416, "Request Range Not Satisfiable", "requested range not satisfiable",
                           H2O_SEND_ERROR_KEEP_HEADERS);
            goto Close;
        }
        generator->ranged.range_count = range_count;
        generator->ranged.range_infos = range_infos;
        generator->ranged.current_range = 0;
        generator->ranged.filesize = generator->bytesleft;

        /* set content-length according to range */
        if (range_count == 1)
            generator->bytesleft = range_infos[1];
        else {
            generator->ranged.mimetype = h2o_strdup(&req->pool, mime_type->data.mimetype.base, mime_type->data.mimetype.len);
            size_t final_content_len = 0, size_tmp = 0, size_fixed_each_part, i;
            generator->ranged.boundary.base = h2o_mem_alloc_pool(&req->pool, BOUNDARY_SIZE + 1);
            generator->ranged.boundary.len = BOUNDARY_SIZE;
            gen_rand_string(&generator->ranged.boundary);
            i = generator->bytesleft;
            while (i) {
                i /= 10;
                size_tmp++;
            }
            size_fixed_each_part = FIXED_PART_SIZE + mime_type->data.mimetype.len + size_tmp;
            for (i = 0; i < range_count; i++) {
                size_tmp = *range_infos++;
                if (size_tmp == 0)
                    final_content_len++;
                while (size_tmp) {
                    size_tmp /= 10;
                    final_content_len++;
                }

                size_tmp = *(range_infos - 1);
                final_content_len += *range_infos;

                size_tmp += *range_infos++ - 1;
                if (size_tmp == 0)
                    final_content_len++;
                while (size_tmp) {
                    size_tmp /= 10;
                    final_content_len++;
                }
            }
            final_content_len += sizeof("\r\n--") - 1 + BOUNDARY_SIZE + sizeof("--\r\n") - 1 + size_fixed_each_part * range_count -
                                 (sizeof("\r\n") - 1);
            generator->bytesleft = final_content_len;
        }
        do_send_file(generator, req, 206, "Partial Content", mime_type->data.mimetype, &h2o_mime_attributes_as_is,
                     method_type == METHOD_IS_GET);
        return 0;
    }

    /* return file */
    do_send_file(generator, req, 200, "OK", mime_type->data.mimetype, &mime_type->data.attr, method_type == METHOD_IS_GET);
    return 0;

NotModified:
    req->res.status = 304;
    req->res.reason = "Not Modified";
    add_headers_unconditional(generator, req);
    h2o_send_inline(req, NULL, 0);
Close:
    do_close(&generator->super, req);
    return 0;
}
Exemple #21
0
static void send_method_not_allowed(h2o_req_t *req)
{
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ALLOW, H2O_STRLIT("GET, HEAD"));
    h2o_send_error(req, 405, "Method Not Allowed", "method not allowed", H2O_SEND_ERROR_KEEP_HEADERS);
}
Exemple #22
0
static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason,
                         h2o_iovec_t mime_type, h2o_mime_attributes_t *mime_attr, int is_get)
{
    /* link the request */
    self->req = req;

    /* setup response */
    req->res.status = status;
    req->res.reason = reason;
    req->res.content_length = self->bytesleft;
    req->res.mime_attr = mime_attr;

    if (self->ranged.range_count > 1) {
        mime_type.base = h2o_mem_alloc_pool(&req->pool, 52);
        mime_type.len = sprintf(mime_type.base, "multipart/byteranges; boundary=%s", self->ranged.boundary.base);
    }
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len);
    h2o_filecache_get_last_modified(self->file.ref, self->header_bufs.last_modified);
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->header_bufs.last_modified,
                   H2O_TIMESTR_RFC1123_LEN);
    add_headers_unconditional(self, req);
    if (self->content_encoding.base != NULL)
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, self->content_encoding.base,
                       self->content_encoding.len);
    if (self->ranged.range_count == 0)
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("bytes"));
    else if (self->ranged.range_count == 1) {
        h2o_iovec_t content_range;
        content_range.base = h2o_mem_alloc_pool(&req->pool, 128);
        content_range.len = sprintf(content_range.base, "bytes %zd-%zd/%zd", self->ranged.range_infos[0],
                                    self->ranged.range_infos[0] + self->ranged.range_infos[1] - 1, self->ranged.filesize);
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len);
    }

    /* special path for cases where we do not need to send any data */
    if (!is_get || self->bytesleft == 0) {
        static h2o_generator_t generator = {NULL, NULL};
        h2o_start_response(req, &generator);
        h2o_send(req, NULL, 0, 1);
        do_close(&self->super, req);
        return;
    }

    /* send data */
    h2o_start_response(req, &self->super);

    if (self->ranged.range_count == 1)
        self->file.off = self->ranged.range_infos[0];
    if (req->_ostr_top->start_pull != NULL && self->ranged.range_count < 2) {
        req->_ostr_top->start_pull(req->_ostr_top, do_pull);
    } else {
        size_t bufsz = MAX_BUF_SIZE;
        if (self->bytesleft < bufsz)
            bufsz = self->bytesleft;
        self->buf = h2o_mem_alloc_pool(&req->pool, bufsz);
        if (self->ranged.range_count < 2)
            do_proceed(&self->super, req);
        else {
            self->bytesleft = 0;
            self->super.proceed = do_multirange_proceed;
            do_multirange_proceed(&self->super, req);
        }
    }
}
Exemple #23
0
static int send_headers(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
{
    h2o_timestamp_t ts;
    size_t num_casper_entries_before_push = 0;

    h2o_get_timestamp(conn->super.ctx, &stream->req.pool, &ts);

    /* cancel push with an error response */
    if (h2o_http2_stream_is_push(stream->stream_id)) {
        if (400 <= stream->req.res.status)
            goto CancelPush;
        h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-pushed"), 0, H2O_STRLIT("1"));
    }

    if (stream->req.hostconf->http2.casper.capacity_bits != 0) {
        /* extract the client-side cache fingerprint */
        if (conn->casper == NULL)
            h2o_http2_conn_init_casper(conn, stream->req.hostconf->http2.casper.capacity_bits);
        size_t header_index = -1;
        while ((header_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_COOKIE, header_index)) != -1) {
            h2o_header_t *header = stream->req.headers.entries + header_index;
            h2o_http2_casper_consume_cookie(conn->casper, header->value.base, header->value.len);
        }
        num_casper_entries_before_push = h2o_http2_casper_num_entries(conn->casper);
        /* update casper if necessary */
        if (stream->req.hostconf->http2.casper.track_all_types || is_blocking_asset(&stream->req)) {
            ssize_t etag_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_ETAG, -1);
            h2o_iovec_t etag = etag_index != -1 ? stream->req.headers.entries[etag_index].value : (h2o_iovec_t){};
            if (h2o_http2_casper_lookup(conn->casper, stream->req.path.base, stream->req.path.len, etag.base, etag.len, 1)) {
                /* cancel if the pushed resource is already marked as cached */
                if (h2o_http2_stream_is_push(stream->stream_id))
                    goto CancelPush;
            }
        }
    }

    if (h2o_http2_stream_is_push(stream->stream_id)) {
        /* for push, send the push promise */
        if (!stream->push.promise_sent)
            h2o_http2_stream_send_push_promise(conn, stream);
        /* send ASAP if it is a blocking asset (even in case of Firefox we can't wait 1RTT for it to reprioritize the asset) */
        if (is_blocking_asset(&stream->req))
            h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0);
    } else {
        /* for pull, push things requested, as well as send the casper cookie if modified */
        if (conn->peer_settings.enable_push) {
            size_t i;
            for (i = 0; i != stream->req.http2_push_paths.size; ++i)
                h2o_http2_conn_push_path(conn, stream->req.http2_push_paths.entries[i], stream);
            /* send casper cookie if it has been altered (due to the __stream itself__ or by some of the pushes) */
            if (conn->casper != NULL && num_casper_entries_before_push != h2o_http2_casper_num_entries(conn->casper)) {
                h2o_iovec_t cookie = h2o_http2_casper_build_cookie(conn->casper, &stream->req.pool);
                h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, cookie.base, cookie.len);
            }
        }
        /* raise the priority of asset files that block rendering to highest if the user-agent is _not_ using dependency-based
         * prioritization (e.g. that of Firefox)
         */
        if (conn->num_streams.open_priority == 0 && stream->req.hostconf->http2.reprioritize_blocking_assets &&
            h2o_http2_scheduler_get_parent(&stream->_refs.scheduler) == &conn->scheduler && is_blocking_asset(&stream->req))
            h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0);
    }

    /* send HEADERS, as well as start sending body */
    h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, stream->stream_id,
                               conn->peer_settings.max_frame_size, &stream->req.res, &ts,
                               &conn->super.ctx->globalconf->server_name);
    h2o_http2_conn_request_write(conn);
    h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY);

    return 0;

CancelPush:
    h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
    h2o_linklist_insert(&conn->_write.streams_to_proceed, &stream->_refs.link);
    if (stream->push.promise_sent) {
        h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, H2O_HTTP2_ERROR_INTERNAL);
        h2o_http2_conn_request_write(conn);
    }
    return -1;
}
Exemple #24
0
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status,
                                       h2o_iovec_t msg, struct phr_header *headers, size_t num_headers)
{
    struct rp_generator_t *self = client->data;
    h2o_req_t *req = self->src_req;
    size_t i;

    if (errstr != NULL && errstr != h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr);
        h2o_send_error(req, 502, "Gateway Error", errstr, 0);
        return NULL;
    }

    /* copy the response (note: all the headers must be copied; http1client discards the input once we return from this callback) */
    req->res.status = status;
    req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base;
    for (i = 0; i != num_headers; ++i) {
        const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len);
        h2o_iovec_t value;
        if (token != NULL) {
            if (token->proxy_should_drop) {
                goto Skip;
            }
            if (token == H2O_TOKEN_CONTENT_LENGTH) {
                if (req->res.content_length != SIZE_MAX ||
                    (req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) {
                    self->client = NULL;
                    h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)");
                    h2o_send_error(req, 502, "Gateway Error", "invalid response from upstream", 0);
                    return NULL;
                }
                goto Skip;
            } else if (token == H2O_TOKEN_LOCATION) {
                if (req->res_is_delegated && (300 <= status && status <= 399) && status != 304) {
                    self->client = NULL;
                    h2o_iovec_t method = h2o_get_redirect_method(req->method, status);
                    h2o_send_redirect_internal(req, method, headers[i].value, headers[i].value_len, 1);
                    return NULL;
                }
                if (req->overrides != NULL && req->overrides->location_rewrite.match != NULL) {
                    value =
                        rewrite_location(&req->pool, headers[i].value, headers[i].value_len, req->overrides->location_rewrite.match,
                                         req->input.scheme, req->input.authority, req->overrides->location_rewrite.path_prefix);
                    if (value.base != NULL)
                        goto AddHeader;
                }
                goto AddHeaderDuped;
            } else if (token == H2O_TOKEN_LINK) {
                h2o_puth_path_in_link_header(req, headers[i].value, headers[i].value_len);
            }
        /* default behaviour, transfer the header downstream */
        AddHeaderDuped:
            value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len);
        AddHeader:
            h2o_add_header(&req->pool, &req->res.headers, token, value.base, value.len);
        Skip:
            ;
        } else {
            h2o_iovec_t name = h2o_strdup(&req->pool, headers[i].name, headers[i].name_len);
            h2o_iovec_t value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len);
            h2o_add_header_by_str(&req->pool, &req->res.headers, name.base, name.len, 0, value.base, value.len);
        }
    }

    if (self->is_websocket_handshake && req->res.status == 101) {
        h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
        assert(client_ctx->websocket_timeout != NULL);
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, H2O_STRLIT("websocket"));
        on_websocket_upgrade(self, client_ctx->websocket_timeout);
        self->client = NULL;
        return NULL;
    }
    /* declare the start of the response */
    h2o_start_response(req, &self->super);

    if (errstr == h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_send(req, NULL, 0, 1);
        return NULL;
    }

    return on_body;
}
Exemple #25
0
static void test_build_request(void)
{
    h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
    h2o_fastcgi_config_vars_t config = {5000, 0, {}};
    iovec_vector_t vecs;
    size_t vec_index;

    conn->req.input.method = conn->req.method = h2o_iovec_init(H2O_STRLIT("GET"));
    conn->req.input.scheme = conn->req.scheme = &H2O_URL_SCHEME_HTTP;
    conn->req.input.authority = conn->req.authority = h2o_iovec_init(H2O_STRLIT("localhost"));
    conn->req.input.path = conn->req.path = h2o_iovec_init(H2O_STRLIT("/"));
    conn->req.path_normalized = conn->req.path;
    conn->req.query_at = SIZE_MAX;
    conn->req.version = 0x101;
    conn->req.hostconf = *ctx.globalconf->hosts;
    conn->req.pathconf = conn->req.hostconf->paths.entries;
    h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, H2O_STRLIT("foo=bar"));
    h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_USER_AGENT,
                   H2O_STRLIT("Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9"));

    /* build with max_record_size=65535 */
    build_request(&conn->req, &vecs, 0x1234, 65535, &config);
    ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00"
                                                                       "\x00\x01\0\0\0\0\0\0")));
    vec_index = 1;
    ok(check_params(vecs.entries, &vec_index, 0x1234,
                    H2O_STRLIT("\x0b\x00SCRIPT_NAME"                                                                    /* */
                               "\x09\x01PATH_INFO/"                                                                     /* */
                               "\x0c\x00QUERY_STRING"                                                                   /* */
                               "\x0b\x09REMOTE_ADDR127.0.0.1"                                                           /* */
                               "\x0b\x05REMOTE_PORT55555"                                                               /* */
                               "\x0e\x03REQUEST_METHODGET"                                                              /* */
                               "\x09\x09HTTP_HOSTlocalhost"                                                             /* */
                               "\x0b\x01REQUEST_URI/"                                                                   /* */
                               "\x0b\x09SERVER_ADDR127.0.0.1"                                                           /* */
                               "\x0b\x02SERVER_PORT80"                                                                  /* */
                               "\x0b\x07SERVER_NAMEdefault"                                                             /* */
                               "\x0f\x08SERVER_PROTOCOLHTTP/1.1"                                                        /* */
                               "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1"                                                /* */
                               "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */
                               "\x0b\x07HTTP_COOKIEfoo=bar"                                                             /* */
                               )));
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00")));
    ++vec_index;
    ok(vec_index == vecs.size);

    /* build with max_record_size=64, DOCUMENT_ROOT, additional cookie, and content */
    config.document_root = h2o_iovec_init(H2O_STRLIT("/var/www/htdocs"));
    h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, H2O_STRLIT("hoge=fuga"));
    conn->req.entity = h2o_iovec_init(H2O_STRLIT("The above copyright notice and this permission notice shall be included in all "
                                                 "copies or substantial portions of the Software."));
    build_request(&conn->req, &vecs, 0x1234, 64, &config);
    ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00"
                                                                       "\x00\x01\0\0\0\0\0\0")));
    vec_index = 1;
    ok(check_params(vecs.entries, &vec_index, 0x1234,
                    H2O_STRLIT("\x0e\x03"
                               "CONTENT_LENGTH126"   /* */
                               "\x0b\x00SCRIPT_NAME" /* */
                               "\x09\x01PATH_INFO/"  /* */
                               "\x0d\x0f"
                               "DOCUMENT_ROOT/var/www/htdocs"                                                           /* */
                               "\x0f\x10PATH_TRANSLATED/var/www/htdocs/"                                                /* */
                               "\x0c\x00QUERY_STRING"                                                                   /* */
                               "\x0b\x09REMOTE_ADDR127.0.0.1"                                                           /* */
                               "\x0b\x05REMOTE_PORT55555"                                                               /* */
                               "\x0e\x03REQUEST_METHODGET"                                                              /* */
                               "\x09\x09HTTP_HOSTlocalhost"                                                             /* */
                               "\x0b\x01REQUEST_URI/"                                                                   /* */
                               "\x0b\x09SERVER_ADDR127.0.0.1"                                                           /* */
                               "\x0b\x02SERVER_PORT80"                                                                  /* */
                               "\x0b\x07SERVER_NAMEdefault"                                                             /* */
                               "\x0f\x08SERVER_PROTOCOLHTTP/1.1"                                                        /* */
                               "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1"                                                /* */
                               "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */
                               "\x0b\x11HTTP_COOKIEfoo=bar;hoge=fuga"                                                   /* */
                               )));
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x40\x00\x00")));
    ++vec_index;
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len,
                 H2O_STRLIT("The above copyright notice and this permission notice shall be i")));
    ++vec_index;
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x3e\x00\x00")));
    ++vec_index;
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len,
                 H2O_STRLIT("ncluded in all copies or substantial portions of the Software.")));
    ++vec_index;
    ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00")));
    ++vec_index;
    ok(vec_index == vecs.size);

    h2o_loopback_destroy(conn);
}
Exemple #26
0
void test_lib__http2__hpack(void)
{
    h2o_mem_pool_t pool;
    h2o_mem_init_pool(&pool);

    note("decode_int");
    {
        h2o_iovec_t in;
        const uint8_t *p;
        int32_t out;
#define TEST(input, output) \
    in = h2o_iovec_init(H2O_STRLIT(input)); \
    p = (const uint8_t*)in.base; \
    out = decode_int(&p, p + in.len, 7); \
    ok(out == output); \
    ok(p == (const uint8_t*)in.base + in.len);
        TEST("\x00", 0);
        TEST("\x03", 3);
        TEST("\x81", 1);
        TEST("\x7f\x00", 127);
        TEST("\x7f\x01", 128);
        TEST("\x7f\x7f", 254);
        TEST("\x7f\x81\x00", 128);
        TEST("\x7f\x80\x01", 255);
        TEST("\x7f\xff\xff\xff\x7f", 0xfffffff + 127);
        /* failures */
        TEST("", -1);
        TEST("\x7f", -1);
        TEST("\x7f\xff", -1);
        TEST("\x7f\xff\xff\xff\xff", -1);
#undef TEST
    }

    note("decode_huffman");
    {
        h2o_iovec_t huffcode = { H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff") };
        h2o_iovec_t *decoded = decode_huffman(&pool, (const uint8_t*)huffcode.base, huffcode.len);
        ok(decoded->len == sizeof("www.example.com") -1);
        ok(strcmp(decoded->base, "www.example.com") == 0);
    }
    h2o_mem_clear_pool(&pool);

    note("decode_header (literal header field with indexing)");
    {
        struct st_h2o_decode_header_result_t result;
        h2o_hpack_header_table_t header_table;
        h2o_iovec_t in;
        int r;

        memset(&header_table, 0, sizeof(header_table));
        header_table.hpack_capacity = 4096;
        in = h2o_iovec_init(H2O_STRLIT("\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0d\x63\x75\x73\x74\x6f\x6d\x2d\x68\x65\x61\x64\x65\x72"));
        const uint8_t *p = (const uint8_t*)in.base;
        r = decode_header(&pool, &result, &header_table, &p, p + in.len);
        ok(r == 0);
        ok(result.name->len == 10);
        ok(strcmp(result.name->base, "custom-key") == 0);
        ok(result.value->len == 13);
        ok(strcmp(result.value->base, "custom-header") == 0);
        ok(header_table.hpack_size == 55);
    }
    h2o_mem_clear_pool(&pool);

    note("decode_header (literal header field without indexing)");
    {
        struct st_h2o_decode_header_result_t result;
        h2o_hpack_header_table_t header_table;
        h2o_iovec_t in;
        int r;

        memset(&header_table, 0, sizeof(header_table));
        header_table.hpack_capacity = 4096;
        in = h2o_iovec_init(H2O_STRLIT("\x04\x0c\x2f\x73\x61\x6d\x70\x6c\x65\x2f\x70\x61\x74\x68"));
        const uint8_t *p = (const uint8_t*)in.base;
        r = decode_header(&pool, &result, &header_table, &p, p + in.len);
        ok(r == 0);
        ok(result.name == &H2O_TOKEN_PATH->buf);
        ok(result.value->len == 12);
        ok(strcmp(result.value->base, "/sample/path") == 0);
        ok(header_table.hpack_size == 0);
    }
    h2o_mem_clear_pool(&pool);

    note("decode_header (literal header field never indexed)");
    {
        struct st_h2o_decode_header_result_t result;
        h2o_hpack_header_table_t header_table;
        h2o_iovec_t in;
        int r;

        memset(&header_table, 0, sizeof(header_table));
        header_table.hpack_capacity = 4096;
        in = h2o_iovec_init(H2O_STRLIT("\x10\x08\x70\x61\x73\x73\x77\x6f\x72\x64\x06\x73\x65\x63\x72\x65\x74"));
        const uint8_t *p = (const uint8_t*)in.base;
        r = decode_header(&pool, &result, &header_table, &p, p + in.len);
        ok(r == 0);
        ok(result.name->len == 8);
        ok(strcmp(result.name->base, "password") == 0);
        ok(result.value->len == 6);
        ok(strcmp(result.value->base, "secret") == 0);
        ok(header_table.hpack_size == 0);
    }
    h2o_mem_clear_pool(&pool);

    note("decode_header (indexed header field)");
    {
        struct st_h2o_decode_header_result_t result;
        h2o_hpack_header_table_t header_table;
        h2o_iovec_t in;
        int r;

        memset(&header_table, 0, sizeof(header_table));
        header_table.hpack_capacity = 4096;
        in = h2o_iovec_init(H2O_STRLIT("\x82"));
        const uint8_t *p = (const uint8_t*)in.base;
        r = decode_header(&pool, &result, &header_table, &p, p + in.len);
        ok(r == 0);
        ok(result.name == &H2O_TOKEN_METHOD->buf);
        ok(result.value->len == 3);
        ok(strcmp(result.value->base, "GET") == 0);
        ok(header_table.hpack_size == 0);
    }
    h2o_mem_clear_pool(&pool);

    note("request examples without huffman coding");
    test_request(
        h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x0f\x77\x77\x77\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d")),
        h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x08\x6e\x6f\x2d\x63\x61\x63\x68\x65")),
        h2o_iovec_init(H2O_STRLIT("\x82\x87\x85\xbf\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0c\x63\x75\x73\x74\x6f\x6d\x2d\x76\x61\x6c\x75\x65")));

    note("request examples with huffman coding");
    test_request(
        h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff")),
        h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x86\xa8\xeb\x10\x64\x9c\xbf")),
        h2o_iovec_init(H2O_STRLIT("\x82\x87\x85\xbf\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5b\xb8\xe8\xb4\xbf")));

    note("encode_huffman");
    {
        h2o_iovec_t huffcode = { H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff") };
        char buf[sizeof("www.example.com")];
        size_t l = encode_huffman((uint8_t*)buf, (uint8_t*)H2O_STRLIT("www.example.com"));
        ok(l == huffcode.len);
        ok(memcmp(buf, huffcode.base, huffcode.len) == 0);
    }

    note("response examples with huffmann coding");
    {
        h2o_hpack_header_table_t header_table;
        h2o_res_t res;

        memset(&header_table, 0, sizeof(header_table));
        header_table.hpack_capacity = 256;

        memset(&res, 0, sizeof(res));
        res.status = 302;
        res.reason = "Found";
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("private"));
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT"));
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, H2O_STRLIT("https://www.example.com"));
        check_flatten(&header_table, &res,
            H2O_STRLIT("\x08\x03\x33\x30\x32\x58\x85\xae\xc3\x77\x1a\x4b\x61\x96\xd0\x7a\xbe\x94\x10\x54\xd4\x44\xa8\x20\x05\x95\x04\x0b\x81\x66\xe0\x82\xa6\x2d\x1b\xff\x6e\x91\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3"));

        memset(&res, 0, sizeof(res));
        res.status = 307;
        res.reason = "Temporary Redirect";
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("private"));
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT"));
        h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, H2O_STRLIT("https://www.example.com"));
        check_flatten(&header_table, &res,
            H2O_STRLIT("\x08\x03\x33\x30\x37\xc0\xbf\xbe"));
#if 0
        h2o_iovec_init(H2O_STRLIT("\x48\x03\x33\x30\x37\xc1\xc0\xbf")),
        h2o_iovec_init(H2O_STRLIT("\x88\xc1\x61\x1d\x4d\x6f\x6e\x2c\x20\x32\x31\x20\x4f\x63\x74\x20\x32\x30\x31\x33\x20\x32\x30\x3a\x31\x33\x3a\x32\x32\x20\x47\x4d\x54\xc0\x5a\x04\x67\x7a\x69\x70\x77\x38\x66\x6f\x6f\x3d\x41\x53\x44\x4a\x4b\x48\x51\x4b\x42\x5a\x58\x4f\x51\x57\x45\x4f\x50\x49\x55\x41\x58\x51\x57\x45\x4f\x49\x55\x3b\x20\x6d\x61\x78\x2d\x61\x67\x65\x3d\x33\x36\x30\x30\x3b\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x31")));
#endif
    }

    h2o_mem_clear_pool(&pool);
}
Exemple #27
0
/** HTTP POST entry point for receiving entries from client
 * Provide the user with an ID */
static int __http_get_id(h2o_handler_t *self, h2o_req_t *req)
{
    static h2o_generator_t generator = { NULL, NULL };

    if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST")))
        return -1;

    /* redirect to leader if needed */
    int leader = raft_get_current_leader(sv->raft);
    if (-1 == leader)
    {
        return h2oh_respond_with_error(req, 503, "Leader unavailable");
    }
    else if (leader != sv->node_idx)
    {
        raft_node_t* node = raft_get_node(sv->raft, leader);
        peer_connection_t* leader_conn = raft_node_get_udata(node);
        char leader_url[LEADER_URL_LEN];

        static h2o_generator_t generator = { NULL, NULL };
        static h2o_iovec_t body = { .base = "", .len = 0 };
        req->res.status = 301;
        req->res.reason = "Moved Permanently";
        h2o_start_response(req, &generator);
        snprintf(leader_url, LEADER_URL_LEN, "http://%s:%d/",
                 inet_ntoa(leader_conn->addr.sin_addr),
                 leader_conn->http_port);
        h2o_add_header(&req->pool,
                       &req->res.headers,
                       H2O_TOKEN_LOCATION,
                       leader_url,
                       strlen(leader_url));
        h2o_send(req, &body, 1, 1);
        return 0;
    }

    int e;

    unsigned int ticket = __generate_ticket();

    msg_entry_t entry;
    entry.id = rand();
    entry.data.buf = (void*)&ticket;
    entry.data.len = sizeof(ticket);

    uv_mutex_lock(&sv->raft_lock);

    msg_entry_response_t r;
    e = raft_recv_entry(sv->raft, sv->node_idx, &entry, &r);
    if (0 != e)
        return h2oh_respond_with_error(req, 500, "BAD");

    /* block until the entry is committed */
    int done = 0;
    do
    {
        uv_cond_wait(&sv->appendentries_received, &sv->raft_lock);
        e = raft_msg_entry_response_committed(sv->raft, &r);
        switch (e)
        {
        case 0:
            /* not committed yet */
            break;
        case 1:
            done = 1;
            uv_mutex_unlock(&sv->raft_lock);
            break;
        case -1:
            uv_mutex_unlock(&sv->raft_lock);
            return h2oh_respond_with_error(req, 400, "TRY AGAIN");
        }
    }
    while (!done);

    /* serialize ID */
    char id_str[100];
    h2o_iovec_t body;
    sprintf(id_str, "%d", entry.id);
    body = h2o_iovec_init(id_str, strlen(id_str));

    req->res.status = 200;
    req->res.reason = "OK";
    h2o_start_response(req, &generator);
    h2o_send(req, &body, 1, 1);
    return 0;
}
Exemple #28
0
static int send_headers(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
{
    h2o_timestamp_t ts;

    h2o_get_timestamp(conn->super.ctx, &stream->req.pool, &ts);

    /* cancel push with an error response */
    if (h2o_http2_stream_is_push(stream->stream_id)) {
        if (400 <= stream->req.res.status)
            goto CancelPush;
        if (stream->cache_digests != NULL) {
            ssize_t etag_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_ETAG, -1);
            if (etag_index != -1) {
                h2o_iovec_t url = h2o_concat(&stream->req.pool, stream->req.input.scheme->name, h2o_iovec_init(H2O_STRLIT("://")),
                                             stream->req.input.authority, stream->req.input.path);
                h2o_iovec_t *etag = &stream->req.headers.entries[etag_index].value;
                if (h2o_cache_digests_lookup_by_url_and_etag(stream->cache_digests, url.base, url.len, etag->base, etag->len) ==
                    H2O_CACHE_DIGESTS_STATE_FRESH)
                    goto CancelPush;
            }
        }
    }

    /* reset casper cookie in case cache-digests exist */
    if (stream->cache_digests != NULL && stream->req.hostconf->http2.casper.capacity_bits != 0) {
        h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE,
                       H2O_STRLIT("h2o_casper=; Path=/; Expires=Sat, 01 Jan 2000 00:00:00 GMT"));
    }

    /* CASPER */
    if (conn->casper != NULL) {
        /* update casper if necessary */
        if (stream->req.hostconf->http2.casper.track_all_types || is_blocking_asset(&stream->req)) {
            if (h2o_http2_casper_lookup(conn->casper, stream->req.path.base, stream->req.path.len, 1)) {
                /* cancel if the pushed resource is already marked as cached */
                if (h2o_http2_stream_is_push(stream->stream_id))
                    goto CancelPush;
            }
        }
        if (stream->cache_digests != NULL)
            goto SkipCookie;
        /* browsers might ignore push responses, or they may process the responses in a different order than they were pushed.
         * Therefore H2O tries to include casper cookie only in the last stream that may be received by the client, or when the
         * value become stable; see also: https://github.com/h2o/h2o/issues/421
         */
        if (h2o_http2_stream_is_push(stream->stream_id)) {
            if (!(conn->num_streams.pull.open == 0 && (conn->num_streams.push.half_closed - conn->num_streams.push.send_body) == 1))
                goto SkipCookie;
        } else {
            if (conn->num_streams.push.half_closed - conn->num_streams.push.send_body != 0)
                goto SkipCookie;
        }
        h2o_iovec_t cookie = h2o_http2_casper_get_cookie(conn->casper);
        h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, cookie.base, cookie.len);
    SkipCookie:;
    }

    if (h2o_http2_stream_is_push(stream->stream_id)) {
        /* for push, send the push promise */
        if (!stream->push.promise_sent)
            h2o_http2_stream_send_push_promise(conn, stream);
        /* send ASAP if it is a blocking asset (even in case of Firefox we can't wait 1RTT for it to reprioritize the asset) */
        if (is_blocking_asset(&stream->req))
            h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0);
    } else {
        /* raise the priority of asset files that block rendering to highest if the user-agent is _not_ using dependency-based
         * prioritization (e.g. that of Firefox)
         */
        if (conn->num_streams.priority.open == 0 && stream->req.hostconf->http2.reprioritize_blocking_assets &&
            h2o_http2_scheduler_get_parent(&stream->_refs.scheduler) == &conn->scheduler && is_blocking_asset(&stream->req))
            h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0);
    }

    /* send HEADERS, as well as start sending body */
    if (h2o_http2_stream_is_push(stream->stream_id))
        h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, H2O_STRLIT("pushed"));
    h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, stream->stream_id,
                               conn->peer_settings.max_frame_size, &stream->req.res, &ts, &conn->super.ctx->globalconf->server_name,
                               stream->req.res.content_length);
    h2o_http2_conn_request_write(conn);
    h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY);

    return 0;

CancelPush:
    h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, H2O_STRLIT("cancelled"));
    h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
    h2o_linklist_insert(&conn->_write.streams_to_proceed, &stream->_refs.link);
    if (stream->push.promise_sent) {
#ifndef _MSC_VER
        h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, -H2O_HTTP2_ERROR_INTERNAL);
#else
		h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, H2O_HTTP2_ERROR_INTERNAL);
#endif
        h2o_http2_conn_request_write(conn);
    }
    return -1;
}