Example #1
0
/*
 * This function allow the core to invoke the closing connection process
 * when some connection was not proceesed due to a premature close or similar
 * exception, it also take care of invoke the STAGE_40 and STAGE_50 plugins events
 */
static void mk_request_premature_close(int http_status, struct mk_http_session *cs)
{
    struct mk_http_request *sr;
    struct mk_list *sr_list = &cs->request_list;
    struct mk_list *host_list = &mk_config->hosts;

    /*
     * If the connection is too premature, we need to allocate a temporal session_request
     * to do not break the plugins stages
     */
    if (mk_list_is_empty(sr_list) == 0) {
        sr = &cs->sr_fixed;
        memset(sr, 0, sizeof(struct mk_http_request));
        mk_http_request_init(cs, sr);
        mk_list_add(&sr->_head, &cs->request_list);
    }
    else {
        sr = mk_list_entry_first(sr_list, struct mk_http_request, _head);
    }

    /* Raise error */
    if (http_status > 0) {
        if (!sr->host_conf) {
            sr->host_conf = mk_list_entry_first(host_list, struct host, _head);
        }
        mk_http_error(http_status, cs, sr);

        /* STAGE_40, request has ended */
        mk_plugin_stage_run_40(cs, sr);
    }
Example #2
0
/*
 * This function is invoked everytime the parser evaluate the request is
 * OK. Here we perform some extra validations mostly based on some logic
 * and protocol requirements according to the data received.
 */
static inline int mk_http_parser_ok(struct mk_http_request *req,
                                    struct mk_http_parser *p) {

    /* Validate HTTP Version */
    if (req->protocol == MK_HTTP_PROTOCOL_UNKNOWN) {
        mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP, req->session, req);
        return MK_HTTP_PARSER_ERROR;
    }

    /* POST checks */
    if (req->method == MK_METHOD_POST || req->method == MK_METHOD_PUT) {
        /* validate Content-Length exists */
        if (p->headers[MK_HEADER_CONTENT_LENGTH].type == 0) {
            mk_http_error(MK_CLIENT_LENGTH_REQUIRED, req->session, req);
            return MK_HTTP_PARSER_ERROR;
        }
    }

    return MK_HTTP_PARSER_OK;
}
Example #3
0
static int mk_http_request_prepare(struct mk_http_session *cs,
                                   struct mk_http_request *sr)
{
    int status = 0;
    char *temp;
    struct mk_list *hosts = &mk_config->hosts;
    struct mk_list *alias;
    struct mk_http_header *header;

    /*
     * Process URI, if it contains ASCII encoded strings like '%20',
     * it will return a new memory buffer with the decoded string, otherwise
     * it returns NULL
     */
    temp = mk_utils_url_decode(sr->uri);

    if (temp) {
        sr->uri_processed.data = temp;
        sr->uri_processed.len  = strlen(temp);
    }
    else {
        sr->uri_processed.data = sr->uri.data;
        sr->uri_processed.len  = sr->uri.len;
    }

    /* Always assign the default vhost' */
    sr->host_conf = mk_list_entry_first(hosts, struct host, _head);

    sr->user_home = MK_FALSE;

    /* Valid request URI? */
    if (sr->uri_processed.data[0] != '/') {
        mk_http_error(MK_CLIENT_BAD_REQUEST, cs, sr);
        return MK_EXIT_OK;
    }

    /* Check if we have a Host header: Hostname ; port */
    mk_http_point_header(&sr->host, &cs->parser, MK_HEADER_HOST);

    /* Header: Connection */
    mk_http_point_header(&sr->connection, &cs->parser, MK_HEADER_CONNECTION);

    /* Header: Range */
    mk_http_point_header(&sr->range, &cs->parser, MK_HEADER_RANGE);

    /* Header: If-Modified-Since */
    mk_http_point_header(&sr->if_modified_since,
                         &cs->parser,
                         MK_HEADER_IF_MODIFIED_SINCE);

    /* HTTP/1.1 needs Host header */
    if (!sr->host.data && sr->protocol == MK_HTTP_PROTOCOL_11) {
        mk_http_error(MK_CLIENT_BAD_REQUEST, cs, sr);
        return MK_EXIT_OK;
    }

    /* Should we close the session after this request ? */
    mk_http_keepalive_check(cs, sr);

    /* Content Length */
    header = &cs->parser.headers[MK_HEADER_CONTENT_LENGTH];
    if (header->type == MK_HEADER_CONTENT_LENGTH) {
        sr->_content_length.data = header->val.data;
        sr->_content_length.len  = header->val.len;
    }
    else {
        sr->_content_length.data = NULL;
    }

    /* Assign the first node alias */
    alias = &sr->host_conf->server_names;
    sr->host_alias = mk_list_entry_first(alias,
                                         struct host_alias, _head);

    if (sr->host.data) {
        /* Set the given port */
        if (cs->parser.header_host_port > 0) {
            sr->port = cs->parser.header_host_port;
        }

        /* Match the virtual host */
        mk_vhost_get(sr->host, &sr->host_conf, &sr->host_alias);

        /* Check if this virtual host have some redirection */
        if (sr->host_conf->header_redirect.data) {
            mk_header_set_http_status(sr, MK_REDIR_MOVED);
            sr->headers.location = mk_string_dup(sr->host_conf->header_redirect.data);
            sr->headers.content_length = 0;
            sr->headers.location = NULL;
            mk_header_prepare(cs, sr);
            return 0;
        }
    }

    /* Is requesting an user home directory ? */
    if (mk_config->user_dir &&
            sr->uri_processed.len > 2 &&
            sr->uri_processed.data[1] == MK_USER_HOME) {

        if (mk_user_init(cs, sr) != 0) {
            mk_http_error(MK_CLIENT_NOT_FOUND, cs, sr);
            return MK_EXIT_ABORT;
        }
    }

    /* Plugins Stage 20 */
    int ret;
    ret = mk_plugin_stage_run_20(cs, sr);
    if (ret == MK_PLUGIN_RET_CLOSE_CONX) {
        MK_TRACE("STAGE 20 requested close conexion");
        return MK_EXIT_ABORT;
    }

    /* Normal HTTP process */
    status = mk_http_init(cs, sr);

    MK_TRACE("[FD %i] HTTP Init returning %i", cs->socket, status);

    return status;
}
Example #4
0
/*
 * Parse the protocol and point relevant fields, don't take logic decisions
 * based on this, just parse to locate things.
 */
int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p,
                   char *buffer, int buf_len, struct mk_server *server)
{
    int s;
    int tmp;
    int ret;
    int len;

    /* lazy test

    printf("p->i=%i buf_len=%i\n",
           p->i, buf_len);

    for (s = p->i; s < buf_len; s++) {
        if (buffer[s] == '\r') {
            printf("CR");
        }
        else if (buffer[s] == '\n') {
            printf("LF");
        }
        else {
            printf("%c", buffer[s]);
        }
    }
    printf("\n");
    */

    len = buf_len;
    for (; p->i < len; p->i++, p->chars++) {
        /* FIRST LINE LEVEL: Method, URI & Protocol */
        if (p->level == REQ_LEVEL_FIRST) {
            switch (p->status) {
            case MK_ST_REQ_METHOD:                      /* HTTP Method */
                if (p->chars == -1) {
                    switch (buffer[p->i]) {
                    case 'G':
                        p->method = MK_METHOD_GET;
                        break;
                    case 'P':
                        p->method = MK_METHOD_POST;
                        break;
                    case 'H':
                        p->method = MK_METHOD_HEAD;
                        break;
                    case 'D':
                        p->method = MK_METHOD_DELETE;
                        break;
                    case 'O':
                        p->method = MK_METHOD_OPTIONS;
                        break;
                    }
                    continue;
                }

                if (buffer[p->i] == ' ') {
                    mark_end();
                    p->status = MK_ST_REQ_URI;
                    if (p->end < 2) {
                        return MK_HTTP_PARSER_ERROR;
                    }
                    method_lookup(req, p, buffer);
                    start_next();
                }
                else {
                    if ((p->i - p->start) > 10) {
                        return MK_HTTP_PARSER_ERROR;
                    }
                }
                break;
            case MK_ST_REQ_URI:                         /* URI */
                if (buffer[p->i] == ' ') {
                    mark_end();
                    p->status = MK_ST_REQ_PROT_VERSION;
                    if (field_len() < 1) {
                        return MK_HTTP_PARSER_ERROR;
                    }
                    request_set(&req->uri, p, buffer);
                    start_next();
                }
                else if (buffer[p->i] == '?') {
                    mark_end();
                    request_set(&req->uri, p, buffer);
                    p->status = MK_ST_REQ_QUERY_STRING;
                    start_next();
                }
                else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
                    mk_http_error(MK_CLIENT_BAD_REQUEST, req->session,
                                  req, server);
                    return MK_HTTP_PARSER_ERROR;
                }
                break;
            case MK_ST_REQ_QUERY_STRING:                /* Query string */
                char_lookup(buffer, ' ', len, p);
                if (buffer[p->i] == ' ') {
                    mark_end();
                    request_set(&req->query_string, p, buffer);
                    p->status = MK_ST_REQ_PROT_VERSION;
                    start_next();
                }
                else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
                    mk_http_error(MK_CLIENT_BAD_REQUEST, req->session,
                                  req, server);
                    return MK_HTTP_PARSER_ERROR;
                }
                break;
            case MK_ST_REQ_PROT_VERSION:                /* Protocol Version */
                /*
                 * Most of the time we already have the string version in our
                 * buffer, for that case try to match the version and avoid
                 * loop rounds.
                 */
                if (p->start + 6 >= p->i) {
                    continue;
                }

                tmp = p->start;
                if (buffer[tmp] == 'H' &&
                    buffer[tmp + 1] == 'T' &&
                    buffer[tmp + 2] == 'T' &&
                    buffer[tmp + 3] == 'P' &&
                    buffer[tmp + 4] == '/' &&
                    buffer[tmp + 5] == '1' &&
                    buffer[tmp + 6] == '.') {

                    request_set(&req->protocol_p, p, buffer);
                    req->protocol_p.len = 8;
                    mk_http_set_minor_version(buffer[tmp + 7]);
                }
                else {
                    mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP,
                                  req->session, req, server);
                    return MK_HTTP_PARSER_ERROR;
                }
                p->status = MK_ST_FIRST_CONTINUE;
                break;
            case MK_ST_FIRST_CONTINUE:
                if (buffer[p->i] == '\r') {
                    p->status = MK_ST_FIRST_FINALIZING;
                }
                else {
                    return MK_HTTP_PARSER_ERROR;
                }
                break;
            case MK_ST_FIRST_FINALIZING:                  /* New Line */
                if (buffer[p->i] == '\n') {
                    p->level = REQ_LEVEL_CONTINUE;
                    start_next();
                }
                else {
                    return MK_HTTP_PARSER_ERROR;
                }
                break;
            case MK_ST_BLOCK_END:
                if (buffer[p->i] == '\n') {
                    return mk_http_parser_ok(req, p, server);
                }
                else {
                    return MK_HTTP_PARSER_ERROR;
                }
                break;
            };
        }
        else if (p->level == REQ_LEVEL_CONTINUE) {
            if (buffer[p->i] == '\r') {
                p->level  = REQ_LEVEL_FIRST;
                p->status = MK_ST_BLOCK_END;
            }
            else {
                p->level  = REQ_LEVEL_HEADERS;
                p->status = MK_ST_HEADER_KEY;
                p->chars  = 0;
            }
        }
        /* HEADERS: all headers stuff */
        if (p->level == REQ_LEVEL_HEADERS) {
            /* Expect a Header key */
            if (p->status == MK_ST_HEADER_KEY) {
                if (buffer[p->i] == '\r') {
                    if (p->chars == 0) {
                        p->level = REQ_LEVEL_END;
                        start_next();
                    }
                    else {
                        return MK_HTTP_PARSER_ERROR;
                    }
                }

                if (p->chars == 0) {
                    /*
                     * We reach the start of a Header row, lets catch the most
                     * probable header.
                     *
                     * The goal of this 'first row character lookup', is to define a
                     * small range set of probable headers comparison once we catch
                     * a header end.
                     */
                    s = tolower(buffer[p->i]);
                    switch (s) {
                    case 'a':
                        p->header_min = MK_HEADER_ACCEPT;
                        p->header_max = MK_HEADER_AUTHORIZATION;
                        break;
                    case 'c':
                        p->header_min = MK_HEADER_CACHE_CONTROL;
                        p->header_max = MK_HEADER_CONTENT_TYPE;
                        break;
                    case 'h':
                        p->header_min = MK_HEADER_HOST;
                        p->header_max = MK_HEADER_HTTP2_SETTINGS;
                        break;
                    case 'i':
                        header_scope_eq(p, MK_HEADER_IF_MODIFIED_SINCE);
                        break;
                    case 'l':
                        p->header_min = MK_HEADER_LAST_MODIFIED;
                        p->header_max = MK_HEADER_LAST_MODIFIED_SINCE;
                        break;
                    case 'r':
                        p->header_min = MK_HEADER_RANGE;
                        p->header_max = MK_HEADER_REFERER;
                        break;
                    case 'u':
                        p->header_min = MK_HEADER_UPGRADE;
                        p->header_max = MK_HEADER_USER_AGENT;
                        break;
                    default:
                        p->header_key = -1;
                        p->header_sep = -1;
                        p->header_min = -1;
                        p->header_max = -1;
                    };
                    p->header_key = p->i;
                    continue;
                }

                /* Found key/value separator */
                char_lookup(buffer, ':', len, p);
                if (buffer[p->i] == ':') {
                    /* Set the key/value middle point */
                    p->header_sep = p->i;

                    /* validate length */
                    mark_end();
                    if (field_len() < 1) {
                        return MK_HTTP_PARSER_ERROR;
                    }

                    /* Wait for a value */
                    p->status = MK_ST_HEADER_VALUE;
                    start_next();
                }
            }
            /* Parsing the header value */
            else if (p->status == MK_ST_HEADER_VALUE) {
                /* Trim left, set starts only when found something != ' ' */
                if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
                    return MK_HTTP_PARSER_ERROR;
                }
                else if (buffer[p->i] != ' ') {
                    p->status = MK_ST_HEADER_VAL_STARTS;
                    p->start = p->header_val = p->i;
                }
                continue;
            }
            /* New header row starts */
            else if (p->status == MK_ST_HEADER_VAL_STARTS) {
                /* Maybe there is no more headers and we reach the end ? */
                if (buffer[p->i] == '\r') {
                    mark_end();
                    if (field_len() <= 0) {
                        return MK_HTTP_PARSER_ERROR;
                    }

                    /*
                     * A header row has ended, lets lookup the header and populate
                     * our headers table index.
                     */
                    ret = header_lookup(p, buffer);
                    if (ret != 0) {
                        if (ret < -1) {
                            mk_http_error(-ret, req->session, req, server);
                        }
                        return MK_HTTP_PARSER_ERROR;
                    }

                    /* Try to catch next LF */
                    if (p->i + 1 < len) {
                        if (buffer[p->i + 1] == '\n') {
                            p->i++;
                            p->status = MK_ST_HEADER_KEY;
                            p->chars = -1;
                            start_next();
                        }
                    }

                    p->status = MK_ST_HEADER_END;
                    start_next();
                }
                else if (buffer[p->i] == '\n' && buffer[p->i - 1] != '\r') {
                    return MK_HTTP_PARSER_ERROR;
                }
            }
            else if (p->status == MK_ST_HEADER_END) {
                if (buffer[p->i] == '\n') {
                    p->status = MK_ST_HEADER_KEY;
                    p->chars = -1;
                    start_next();
                }
                else {
                    return MK_HTTP_PARSER_ERROR;
                }
            }
        }
        else if (p->level == REQ_LEVEL_END) {
            if (buffer[p->i] == '\n') {
                if (p->header_content_length > 0) {
                    p->level = REQ_LEVEL_BODY;
                    p->chars = -1;
                    start_next();
                }
                else {
                    return mk_http_parser_ok(req, p, server);
                }
            }
            else {
                return MK_HTTP_PARSER_ERROR;
            }
        }
        else if (p->level == REQ_LEVEL_BODY) {
            /*
             * Reaching this level can means two things:
             *
             * - A Pipeline Request
             * - A Body content (POST/PUT methods)
             */
            if (p->header_content_length > 0) {

                p->body_received = len - p->start;
                if ((len - p->start) < p->header_content_length) {
                    return MK_HTTP_PARSER_PENDING;
                }

                /* Cut off */
                p->i += p->body_received;
                req->data.len  = p->body_received;
                req->data.data = (buffer + p->start);
            }
            return mk_http_parser_ok(req, p, server);
        }
    }

    return MK_HTTP_PARSER_PENDING;
}