void http_cfg_free(struct http_cfg *cfg) { http_free(cfg->content_decoders); http_headers_delete(cfg->default_headers); memset(cfg, 0, sizeof(struct http_cfg)); }
void http_response_delete(struct http_response *response) { if (!response) return; c_free(response->reason); http_headers_delete(response->headers); c_free(response->body); c_vector_delete(response->content_codings); http_url_delete(response->redirection_location); c_free0(response, sizeof(struct http_response)); }
int http_response_parse(const char *data, size_t sz, uint32_t flags, struct http_response **presponse, size_t *psz) { struct http_response *response; const char *ptr; size_t len, toklen; char status_string[4]; size_t status_sz; int32_t status_value; int ret; ptr = data; len = sz; #define HTTP_FAIL(fmt_, ...) \ do { \ if (fmt_) \ c_set_error(fmt_, ##__VA_ARGS__); \ http_response_delete(response); \ return -1; \ } while (0) #define HTTP_TRUNCATED() \ do { \ http_response_delete(response); \ return 0; \ } while (0) response = http_response_new(); /* Version */ toklen = c_memcspn(ptr, len, " "); if (toklen == len) { if (len > HTTP_VERSION_MAX_LENGTH) HTTP_FAIL("invalid version"); HTTP_TRUNCATED(); } if (http_version_parse(ptr, toklen, &response->version) == -1) HTTP_FAIL(NULL); ptr += toklen + 1; len -= toklen + 1; /* Status */ toklen = c_memcspn(ptr, len, " "); if (toklen == len) { if (len > 3) HTTP_FAIL("invalid status code"); HTTP_TRUNCATED(); } if (toklen > 3) HTTP_FAIL("invalid status code"); memcpy(status_string, ptr, toklen); status_string[toklen] = '\0'; if (c_parse_i32(status_string, &status_value, &status_sz) == -1) HTTP_FAIL("invalid status code"); if (status_sz != toklen) HTTP_FAIL("invalid trailing data after status code"); response->status = (enum http_status)status_value; ptr += toklen + 1; len -= toklen + 1; /* Reason */ toklen = c_memcspn(ptr, len, "\r"); if (toklen == len) { if (len > HTTP_REASON_MAX_LENGTH) HTTP_FAIL("reason string too long"); HTTP_TRUNCATED(); } response->reason = c_strndup(ptr, toklen); ptr += toklen; len -= toklen; /* End of status line */ if (len < 2) HTTP_TRUNCATED(); if (ptr[0] != '\r' || ptr[1] != '\n') HTTP_FAIL("malformed status line"); ptr += 2; len -= 2; /* Headers */ http_headers_delete(response->headers); response->headers = NULL; ret = http_headers_parse(ptr, len, &response->headers, NULL, &toklen); if (ret == -1) HTTP_FAIL(NULL); if (ret == 0) HTTP_TRUNCATED(); ptr += toklen; len -= toklen; if (http_response_preprocess_headers(response) == -1) { http_response_delete(response); return -1; } /* Body */ if (!http_response_can_have_body(response)) goto end; if (response->has_content_length) { if (response->content_length > HTTP_RESPONSE_MAX_CONTENT_LENGTH) HTTP_FAIL("payload too large"); if (len < response->content_length) HTTP_TRUNCATED(); response->body_sz = response->content_length; response->body = c_strndup(ptr, response->content_length); ptr += response->body_sz; len -= response->body_sz; } else if (response->is_body_chunked) { struct http_headers *trailer; size_t chunked_data_sz; ret = http_chunked_data_parse(ptr, len, &response->body, &response->body_sz, &chunked_data_sz); if (ret == -1) HTTP_FAIL("invalid chunked body: %s", c_get_error()); if (ret == 0) HTTP_TRUNCATED(); ptr += chunked_data_sz; len -= chunked_data_sz; /* Trailer */ ret = http_headers_parse(ptr, len, &trailer, NULL, &toklen); if (ret == -1) HTTP_FAIL(NULL); if (ret == 0) HTTP_TRUNCATED(); http_headers_merge_nocopy(response->headers, trailer); http_headers_delete(trailer); ptr += toklen; len -= toklen; } else if (response->has_connection_close) { size_t content_length; if (flags & HTTP_RESPONSE_PARSE_EOF) { content_length = len; if (content_length > HTTP_RESPONSE_MAX_CONTENT_LENGTH) HTTP_FAIL("payload too large"); response->body_sz = content_length; response->body = c_strndup(ptr, content_length); ptr += response->body_sz; len -= response->body_sz; } else { HTTP_TRUNCATED(); } } else { HTTP_FAIL("missing content length"); } #undef HTTP_FAIL #undef HTTP_TRUNCATED end: *presponse = response; *psz = sz - len; return 1; }