<commit_before>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"

static const char *method_strings[] = {
    "GET ",    /* LCB_HTTP_METHOD_GET */
    "POST ",   /* LCB_HTTP_METHOD_POST */
    "PUT ",    /* LCB_HTTP_METHOD_PUT */
    "DELETE "  /* LCB_HTTP_METHOD_DELETE */
};


static const char http_version[] = " HTTP/1.1\r\n";
static const char req_headers[] = "User-Agent: libcouchbase/"LCB_VERSION_STRING"\r\n"
                                  "Accept: application/json\r\n";

void lcb_http_request_destroy(lcb_http_request_t req)
{
    if (req) {
        if (req->io) {
            if (req->event) {
                req->io->v.v0.destroy_event(req->io, req->event);
            }
            if (req->sock != INVALID_SOCKET) {
                req->io->v.v0.close(req->io, req->sock);
            }
        }
        if (req->root_ai) {
            freeaddrinfo(req->root_ai);
        }
        free(req->path);
        free(req->url);
        free(req->host);
        free(req->port);
        free(req->parser);
        free(req->password);
        ringbuffer_destruct(&req->input);
        ringbuffer_destruct(&req->output);
        ringbuffer_destruct(&req->result);
        {
            struct lcb_http_header_st *tmp, *hdr = req->headers_list;
            while (hdr) {
                tmp = hdr->next;
                free(hdr);
                hdr = tmp;
            }
        }
    }
    memset(req, 0xff, sizeof(struct lcb_http_request_st));
    free(req);
}

static int http_parser_header_cb(http_parser *p, const char *bytes,
                                 lcb_size_t nbytes)
{
    lcb_http_request_t req = p->data;
    struct lcb_http_header_st *item;

    item = calloc(1, sizeof(struct lcb_http_header_st));
    if (item == NULL) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                          "Failed to allocate buffer");
        return -1;
    }
    item->data = malloc(nbytes + 1);
    if (item->data == NULL) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                          "Failed to allocate buffer");
        return -1;
    }
    memcpy(item->data, bytes, nbytes);
    item->data[nbytes] = '\0';
    item->next = req->headers_list;
    req->headers_list = item;
    req->nheaders++;
    return 0;
}

static int http_parser_headers_complete_cb(http_parser *p)
{
    lcb_http_request_t req = p->data;
    struct lcb_http_header_st *hdr;
    lcb_size_t ii;

    /* +1 pointer for NULL-terminator */
    req->headers = calloc(req->nheaders + 1, sizeof(const char *));
    for (ii = req->nheaders - 1, hdr = req->headers_list; hdr; --ii, hdr = hdr->next) {
        req->headers[ii] = hdr->data;
    }
    return 0;
}

static void setup_lcb_http_resp_t(lcb_http_resp_t *resp,
                                  lcb_http_status_t status,
                                  const char *path,
                                  lcb_size_t npath,
                                  const char *const *headers,
                                  const void *bytes,
                                  lcb_size_t nbytes)
{
    memset(resp, 0, sizeof(*resp));
    resp->version = 0;
    resp->v.v0.status = status;
    resp->v.v0.path = path;
    resp->v.v0.npath = npath;
    resp->v.v0.headers = headers;
    resp->v.v0.bytes = bytes;
    resp->v.v0.nbytes = nbytes;
}

static int request_valid(lcb_http_request_t req)
{
    if (req->server) {
        return hashset_is_member(req->server->http_requests, req);
    } else {
        return hashset_is_member(req->instance->http_requests, req);
    }
}

static int http_parser_body_cb(http_parser *p, const char *bytes, lcb_size_t nbytes)
{
    lcb_error_t rc;
    lcb_http_request_t req = p->data;

    if (!request_valid(req)) {
        return 0;
    }
    if (req->chunked) {
        lcb_http_resp_t resp;
        setup_lcb_http_resp_t(&resp, p->status_code, req->path, req->npath,
                              req->headers, bytes, nbytes);
        rc = (p->status_code / 100 == 2) ?  LCB_SUCCESS : LCB_PROTOCOL_ERROR;
        req->on_data(req, req->instance, req->command_cookie, rc, &resp);
    } else {
        if (!ringbuffer_ensure_capacity(&req->result, nbytes)) {
            lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                              "Failed to allocate buffer");
            return -1;
        }
        ringbuffer_write(&req->result, bytes, nbytes);
    }
    return 0;
}

static int http_parser_complete_cb(http_parser *p)
{
    lcb_error_t rc;
    lcb_http_request_t req = p->data;
    char *bytes = NULL;
    lcb_size_t np = 0, nbytes = 0;
    lcb_http_resp_t resp;

    req->completed = 1;
    if (!request_valid(req)) {
        return 0;
    }
    rc = (p->status_code / 100 == 2) ?  LCB_SUCCESS : LCB_PROTOCOL_ERROR;
    if (!req->chunked) {
        nbytes = req->result.nbytes;
        if (ringbuffer_is_continous(&req->result, RINGBUFFER_READ, nbytes)) {
            bytes = ringbuffer_get_read_head(&req->result);
        } else {
            if ((bytes = malloc(nbytes)) == NULL) {
                lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM, NULL);
                return -1;
            }
            np = ringbuffer_peek(&req->input, bytes, nbytes);
            if (np != nbytes) {
                lcb_error_handler(req->instance, LCB_EINTERNAL, NULL);
                free(bytes);
                return -1;
            }
        }
    }
    setup_lcb_http_resp_t(&resp, p->status_code, req->path, req->npath,
                          req->headers, bytes, nbytes);
    req->on_complete(req, req->instance, req->command_cookie, rc, &resp);
    if (!req->chunked) {
        ringbuffer_consumed(&req->result, nbytes);
        if (np) {   /* release peek storage */
            free(bytes);
        }
    }
    return 0;
}

static int request_do_fill_input_buffer(lcb_http_request_t req)
{
    struct lcb_iovec_st iov[2];
    lcb_ssize_t nr;
    lcb_http_resp_t resp;

    if (!ringbuffer_ensure_capacity(&req->input, 8192)) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,<commit_msg>CCBC-118 lcb_error_t member in the http callbacks shouldn't reflect the HTTP response code<commit_after>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"

static const char *method_strings[] = {
    "GET ",    /* LCB_HTTP_METHOD_GET */
    "POST ",   /* LCB_HTTP_METHOD_POST */
    "PUT ",    /* LCB_HTTP_METHOD_PUT */
    "DELETE "  /* LCB_HTTP_METHOD_DELETE */
};


static const char http_version[] = " HTTP/1.1\r\n";
static const char req_headers[] = "User-Agent: libcouchbase/"LCB_VERSION_STRING"\r\n"
                                  "Accept: application/json\r\n";

void lcb_http_request_destroy(lcb_http_request_t req)
{
    if (req) {
        if (req->io) {
            if (req->event) {
                req->io->v.v0.destroy_event(req->io, req->event);
            }
            if (req->sock != INVALID_SOCKET) {
                req->io->v.v0.close(req->io, req->sock);
            }
        }
        if (req->root_ai) {
            freeaddrinfo(req->root_ai);
        }
        free(req->path);
        free(req->url);
        free(req->host);
        free(req->port);
        free(req->parser);
        free(req->password);
        ringbuffer_destruct(&req->input);
        ringbuffer_destruct(&req->output);
        ringbuffer_destruct(&req->result);
        {
            struct lcb_http_header_st *tmp, *hdr = req->headers_list;
            while (hdr) {
                tmp = hdr->next;
                free(hdr);
                hdr = tmp;
            }
        }
    }
    memset(req, 0xff, sizeof(struct lcb_http_request_st));
    free(req);
}

static int http_parser_header_cb(http_parser *p, const char *bytes,
                                 lcb_size_t nbytes)
{
    lcb_http_request_t req = p->data;
    struct lcb_http_header_st *item;

    item = calloc(1, sizeof(struct lcb_http_header_st));
    if (item == NULL) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                          "Failed to allocate buffer");
        return -1;
    }
    item->data = malloc(nbytes + 1);
    if (item->data == NULL) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                          "Failed to allocate buffer");
        return -1;
    }
    memcpy(item->data, bytes, nbytes);
    item->data[nbytes] = '\0';
    item->next = req->headers_list;
    req->headers_list = item;
    req->nheaders++;
    return 0;
}

static int http_parser_headers_complete_cb(http_parser *p)
{
    lcb_http_request_t req = p->data;
    struct lcb_http_header_st *hdr;
    lcb_size_t ii;

    /* +1 pointer for NULL-terminator */
    req->headers = calloc(req->nheaders + 1, sizeof(const char *));
    for (ii = req->nheaders - 1, hdr = req->headers_list; hdr; --ii, hdr = hdr->next) {
        req->headers[ii] = hdr->data;
    }
    return 0;
}

static void setup_lcb_http_resp_t(lcb_http_resp_t *resp,
                                  lcb_http_status_t status,
                                  const char *path,
                                  lcb_size_t npath,
                                  const char *const *headers,
                                  const void *bytes,
                                  lcb_size_t nbytes)
{
    memset(resp, 0, sizeof(*resp));
    resp->version = 0;
    resp->v.v0.status = status;
    resp->v.v0.path = path;
    resp->v.v0.npath = npath;
    resp->v.v0.headers = headers;
    resp->v.v0.bytes = bytes;
    resp->v.v0.nbytes = nbytes;
}

static int request_valid(lcb_http_request_t req)
{
    if (req->server) {
        return hashset_is_member(req->server->http_requests, req);
    } else {
        return hashset_is_member(req->instance->http_requests, req);
    }
}

static int http_parser_body_cb(http_parser *p, const char *bytes, lcb_size_t nbytes)
{
    lcb_http_request_t req = p->data;

    if (!request_valid(req)) {
        return 0;
    }
    if (req->chunked) {
        lcb_http_resp_t resp;
        setup_lcb_http_resp_t(&resp, p->status_code, req->path, req->npath,
                              req->headers, bytes, nbytes);
        req->on_data(req, req->instance, req->command_cookie, LCB_SUCCESS, &resp);
    } else {
        if (!ringbuffer_ensure_capacity(&req->result, nbytes)) {
            lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
                              "Failed to allocate buffer");
            return -1;
        }
        ringbuffer_write(&req->result, bytes, nbytes);
    }
    return 0;
}

static int http_parser_complete_cb(http_parser *p)
{
    lcb_http_request_t req = p->data;
    char *bytes = NULL;
    lcb_size_t np = 0, nbytes = 0;
    lcb_http_resp_t resp;

    req->completed = 1;
    if (!request_valid(req)) {
        return 0;
    }
    if (!req->chunked) {
        nbytes = req->result.nbytes;
        if (ringbuffer_is_continous(&req->result, RINGBUFFER_READ, nbytes)) {
            bytes = ringbuffer_get_read_head(&req->result);
        } else {
            if ((bytes = malloc(nbytes)) == NULL) {
                lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM, NULL);
                return -1;
            }
            np = ringbuffer_peek(&req->input, bytes, nbytes);
            if (np != nbytes) {
                lcb_error_handler(req->instance, LCB_EINTERNAL, NULL);
                free(bytes);
                return -1;
            }
        }
    }
    setup_lcb_http_resp_t(&resp, p->status_code, req->path, req->npath,
                          req->headers, bytes, nbytes);
    req->on_complete(req, req->instance, req->command_cookie, LCB_SUCCESS, &resp);
    if (!req->chunked) {
        ringbuffer_consumed(&req->result, nbytes);
        if (np) {   /* release peek storage */
            free(bytes);
        }
    }
    return 0;
}

static int request_do_fill_input_buffer(lcb_http_request_t req)
{
    struct lcb_iovec_st iov[2];
    lcb_ssize_t nr;
    lcb_http_resp_t resp;

    if (!ringbuffer_ensure_capacity(&req->input, 8192)) {
        lcb_error_handler(req->instance, LCB_CLIENT_ENOMEM,
{'fraction_non_alphanumeric': 0.12310223618278364, 'fraction_numerical': 0.008002393239099544, 'mean_word_length': 2.040472942246476, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 179, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1110017', 'n_tokens_mistral': 4456, 'n_tokens_neox': 4360, 'n_words': 925}