Page MenuHomePhorge

http.c
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
#include <uv.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <tlsuv/http.h>
#include "modules/http.h"
#include "streams/brotli.h"
struct ant_http_request_s {
tlsuv_http_t client;
ant_http_response_t response;
tlsuv_http_req_t *req;
ant_http_response_cb on_response;
ant_http_body_cb on_body;
ant_http_complete_cb on_complete;
void *user_data;
char *error_message;
brotli_stream_state_t *brotli_decoder;
ant_http_result_t result;
int error_code;
bool completed;
bool canceled;
bool decode_brotli;
};
void ant_http_headers_free(ant_http_header_t *headers) {
while (headers) {
ant_http_header_t *next = headers->next;
free(headers->name);
free(headers->value);
free(headers);
headers = next;
}}
static void ant_http_request_free(ant_http_request_t *req) {
if (!req) return;
free((char *)req->response.status_text);
ant_http_headers_free((ant_http_header_t *)req->response.headers);
free(req->error_message);
if (req->brotli_decoder) brotli_stream_state_destroy(req->brotli_decoder);
free(req);
}
static void ant_http_upload_chunk_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) {
free(body);
}
static char *ant_http_copy_slice(const char *src, size_t len) {
char *out = malloc(len + 1);
if (!out) return NULL;
memcpy(out, src, len);
out[len] = '\0';
return out;
}
static char *ant_http_build_host_url(const struct tlsuv_url_s *url) {
size_t size = 0;
char port_buf[16] = {0};
int port_len = 0;
if (url->port != 0) port_len = snprintf(port_buf, sizeof(port_buf), ":%u", url->port);
size = url->scheme_len + 3 + url->hostname_len + (size_t)port_len + 1;
char *host_url = malloc(size);
if (!host_url) return NULL;
snprintf(
host_url, size, "%.*s://%.*s%s",
(int)url->scheme_len, url->scheme,
(int)url->hostname_len, url->hostname,
port_buf
);
return host_url;
}
static char *ant_http_build_path(const struct tlsuv_url_s *url) {
const char *path = (url->path && url->path_len > 0) ? url->path : "/";
size_t path_len = (url->path && url->path_len > 0) ? url->path_len : 1;
size_t query_extra = url->query && url->query_len > 0 ? (size_t)url->query_len + 1 : 0;
char *request_path = malloc(path_len + query_extra + 1);
if (!request_path) return NULL;
memcpy(request_path, path, path_len);
if (query_extra > 0) {
request_path[path_len] = '?';
memcpy(request_path + path_len + 1, url->query, url->query_len);
request_path[path_len + query_extra] = '\0';
} else request_path[path_len] = '\0';
return request_path;
}
static ant_http_header_t *ant_http_header_dup(const char *name, const char *value) {
ant_http_header_t *hdr = calloc(1, sizeof(*hdr));
if (!hdr) return NULL;
hdr->name = strdup(name ? name : "");
hdr->value = strdup(value ? value : "");
if (!hdr->name || !hdr->value) {
free(hdr->name);
free(hdr->value);
free(hdr);
return NULL;
}
return hdr;
}
static ant_http_header_t *ant_http_copy_headers(tlsuv_http_resp_t *resp) {
ant_http_header_t *head = NULL;
ant_http_header_t **tail = &head;
tlsuv_http_hdr *hdr = NULL;
LIST_FOREACH(hdr, &resp->headers, _next) {
ant_http_header_t *copy = ant_http_header_dup(hdr->name, hdr->value);
if (!copy) {
ant_http_headers_free(head);
return NULL;
}
*tail = copy;
tail = &copy->next;
}
return head;
}
static const char *ant_http_find_header(const ant_http_header_t *headers, const char *name) {
for (const ant_http_header_t *entry = headers; entry; entry = entry->next) {
if (entry->name && name && strcasecmp(entry->name, name) == 0) return entry->value;
}
return NULL;
}
static void ant_http_on_close(tlsuv_http_t *client) {
ant_http_request_t *req = (ant_http_request_t *)client->data;
if (!req) return;
if (req->on_complete) req->on_complete(
req, req->result, req->error_code,
req->error_message,
req->user_data
);
ant_http_request_free(req);
}
static void ant_http_complete(ant_http_request_t *req, int error_code, const char *error_message) {
if (!req || req->completed) return;
req->completed = 1;
req->error_code = error_code;
if (error_code == 0) req->result = ANT_HTTP_RESULT_OK;
else if (req->canceled) req->result = ANT_HTTP_RESULT_ABORTED;
else req->result = ANT_HTTP_RESULT_NETWORK_ERROR;
free(req->error_message);
req->error_message = error_message ? strdup(error_message) : NULL;
tlsuv_http_close(&req->client, ant_http_on_close);
}
static int ant_http_brotli_body_cb(void *ctx, const uint8_t *chunk, size_t len) {
ant_http_request_t *req = (ant_http_request_t *)ctx;
if (req->on_body && len > 0) req->on_body(req, chunk, len, req->user_data);
return 0;
}
static void ant_http_tlsuv_body_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) {
ant_http_request_t *req = (ant_http_request_t *)http_req->data;
if (!req) return;
if (len == UV_EOF) {
if (req->decode_brotli && req->brotli_decoder &&
brotli_stream_finish(req->brotli_decoder, ant_http_brotli_body_cb, req) != 0) {
ant_http_complete(req, UV_EINVAL, "brotli decompression failed");
return;
}
ant_http_complete(req, 0, NULL);
return;
}
if (len < 0) {
ant_http_complete(req, (int)len, uv_strerror((int)len));
return;
}
if (req->decode_brotli && req->brotli_decoder) {
if (brotli_stream_process(
req->brotli_decoder, (const uint8_t *)body, (size_t)len,
ant_http_brotli_body_cb, req) != 0
) ant_http_complete(req, UV_EINVAL, "brotli decompression failed");
return;
}
if (req->on_body && len > 0) req->on_body(req, (const uint8_t *)body, (size_t)len, req->user_data);
}
static void ant_http_resp_cb(tlsuv_http_resp_t *resp, void *data) {
ant_http_request_t *req = (ant_http_request_t *)resp->req->data;
const char *content_encoding = NULL;
if (!req) return;
if (resp->code < 0) {
ant_http_complete(req, resp->code, uv_strerror(resp->code));
return;
}
req->response.status = resp->code;
req->response.status_text = resp->status ? strdup(resp->status) : strdup("");
req->response.headers = ant_http_copy_headers(resp);
if (!req->response.status_text || (resp->headers.lh_first && !req->response.headers)) {
ant_http_complete(req, UV_ENOMEM, "out of memory");
return;
}
content_encoding = ant_http_find_header(req->response.headers, "content-encoding");
if (content_encoding && strcasecmp(content_encoding, "br") == 0) {
req->brotli_decoder = brotli_stream_state_new(true);
if (!req->brotli_decoder) {
ant_http_complete(req, UV_ENOMEM, "out of memory");
return;
}
req->decode_brotli = 1;
}
resp->body_cb = ant_http_tlsuv_body_cb;
if (req->on_response) req->on_response(req, &req->response, req->user_data);
}
const ant_http_response_t *ant_http_request_response(ant_http_request_t *req) {
return req ? &req->response : NULL;
}
int ant_http_request_cancel(ant_http_request_t *req) {
if (!req || !req->req || req->completed) return 0;
req->canceled = true;
return tlsuv_http_req_cancel(&req->client, req->req);
}
int ant_http_request_write(ant_http_request_t *req, const uint8_t *chunk, size_t len) {
uint8_t *copy = NULL;
int rc = 0;
if (!req || !req->req || req->completed) return UV_EINVAL;
if (len == 0) return 0;
copy = malloc(len);
if (!copy) return UV_ENOMEM;
memcpy(copy, chunk, len);
rc = tlsuv_http_req_data(req->req, (const char *)copy, len, ant_http_upload_chunk_cb);
if (rc != 0) free(copy);
return rc;
}
void ant_http_request_end(ant_http_request_t *req) {
if (!req || !req->req || req->completed) return;
tlsuv_http_req_end(req->req);
}
int ant_http_request_start(
uv_loop_t *loop,
const ant_http_request_options_t *options,
ant_http_response_cb on_response,
ant_http_body_cb on_body,
ant_http_complete_cb on_complete,
void *user_data,
ant_http_request_t **out_req
) {
struct tlsuv_url_s parsed = {0};
ant_http_request_t *req = NULL;
char *host_url = NULL;
char *request_path = NULL;
int rc = 0;
if (out_req) *out_req = NULL;
if (!loop || !options || !options->method || !options->url) return UV_EINVAL;
if (tlsuv_parse_url(&parsed, options->url) != 0) return UV_EINVAL;
if (!parsed.scheme || !parsed.hostname) return UV_EINVAL;
req = calloc(1, sizeof(ant_http_request_t));
if (!req) return UV_ENOMEM;
req->on_response = on_response;
req->on_body = on_body;
req->on_complete = on_complete;
req->user_data = user_data;
host_url = ant_http_build_host_url(&parsed);
request_path = ant_http_build_path(&parsed);
if (!host_url || !request_path) {
free(host_url);
free(request_path);
ant_http_request_free(req);
return UV_ENOMEM;
}
rc = tlsuv_http_init(loop, &req->client, host_url);
free(host_url);
if (rc != 0) {
free(request_path);
ant_http_request_free(req);
return rc;
}
req->client.data = req;
tlsuv_http_header(&req->client, "Accept-Encoding", NULL);
req->req = tlsuv_http_req(
&req->client,
options->method,
request_path, ant_http_resp_cb, req
);
free(request_path);
if (!req->req) {
tlsuv_http_close(&req->client, NULL);
ant_http_request_free(req);
return UV_ENOMEM;
}
req->req->data = req;
if (options->chunked_body) tlsuv_http_req_header(req->req, "transfer-encoding", "chunked");
for (const ant_http_header_t *hdr = options->headers; hdr; hdr = hdr->next) {
tlsuv_http_req_header(req->req, hdr->name, hdr->value);
}
if (options->body && options->body_len > 0) {
rc = tlsuv_http_req_data(req->req, (const char *)options->body, options->body_len, NULL);
if (rc != 0) {
ant_http_complete(req, rc, uv_strerror(rc));
if (out_req) *out_req = req;
return 0;
}}
if (out_req) *out_req = req;
return 0;
}

File Metadata

Mime Type
text/x-c
Expires
Sat, Apr 4, 6:33 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
520973
Default Alt Text
http.c (9 KB)

Event Timeline