2025-03-07 23:19:59 +01:00
|
|
|
// Written by retoor@molodetz.nl
|
2025-10-02 12:25:09 +02:00
|
|
|
// Improved version with better reliability and chunked content support
|
2025-03-07 23:19:59 +01:00
|
|
|
|
|
|
|
// MIT License
|
|
|
|
|
|
|
|
#ifndef R_HTTP_H
|
|
|
|
#define R_HTTP_H
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <json-c/json.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "url.h"
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
#define INITIAL_BUFFER_SIZE (1024 * 1024)
|
|
|
|
#define MAX_HEADER_SIZE (8 * 1024)
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
void init_openssl() {
|
|
|
|
SSL_load_error_strings();
|
|
|
|
OpenSSL_add_ssl_algorithms();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cleanup_openssl() {
|
|
|
|
EVP_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX *create_context() {
|
|
|
|
const SSL_METHOD *method = TLS_client_method();
|
|
|
|
SSL_CTX *ctx = SSL_CTX_new(method);
|
|
|
|
if (!ctx) {
|
|
|
|
perror("Unable to create SSL context");
|
|
|
|
ERR_print_errors_fp(stderr);
|
2025-10-02 12:25:09 +02:00
|
|
|
return NULL;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
|
|
|
SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL);
|
2025-03-07 23:19:59 +01:00
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
int create_socket(const char *hostname, int port) {
|
|
|
|
struct hostent *host;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
|
|
|
|
host = gethostbyname(hostname);
|
|
|
|
if (!host) {
|
|
|
|
perror("Unable to resolve host");
|
2025-10-02 12:25:09 +02:00
|
|
|
return -1;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sock < 0) {
|
|
|
|
perror("Unable to create socket");
|
2025-10-02 12:25:09 +02:00
|
|
|
return -1;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_port = htons(port);
|
|
|
|
addr.sin_addr.s_addr = *(long *)(host->h_addr);
|
|
|
|
|
|
|
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
|
|
|
perror("Unable to connect to host");
|
|
|
|
close(sock);
|
2025-10-02 12:25:09 +02:00
|
|
|
return -1;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
char *read_until_ssl(SSL *ssl, const char *until, size_t max_size) {
|
|
|
|
char *data = (char *)malloc(max_size);
|
|
|
|
if (!data) return NULL;
|
|
|
|
|
|
|
|
size_t index = 0;
|
|
|
|
size_t until_len = strlen(until);
|
|
|
|
|
|
|
|
while (index < max_size - 1) {
|
|
|
|
char c;
|
|
|
|
int bytes = SSL_read(ssl, &c, 1);
|
|
|
|
if (bytes <= 0) {
|
|
|
|
free(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[index++] = c;
|
|
|
|
data[index] = '\0';
|
|
|
|
|
|
|
|
if (index >= until_len &&
|
|
|
|
memcmp(data + index - until_len, until, until_len) == 0) {
|
2025-03-07 23:19:59 +01:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(data);
|
2025-03-07 23:19:59 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
char *read_until(int sock, const char *until, size_t max_size) {
|
|
|
|
char *data = (char *)malloc(max_size);
|
|
|
|
if (!data) return NULL;
|
|
|
|
|
|
|
|
size_t index = 0;
|
|
|
|
size_t until_len = strlen(until);
|
|
|
|
|
|
|
|
while (index < max_size - 1) {
|
|
|
|
char c;
|
|
|
|
ssize_t bytes = recv(sock, &c, 1, 0);
|
|
|
|
if (bytes <= 0) {
|
|
|
|
free(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[index++] = c;
|
|
|
|
data[index] = '\0';
|
|
|
|
|
|
|
|
if (index >= until_len &&
|
|
|
|
memcmp(data + index - until_len, until, until_len) == 0) {
|
2025-03-07 23:19:59 +01:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(data);
|
2025-03-07 23:19:59 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t hex_to_int(const char *hex) {
|
|
|
|
size_t result = 0;
|
2025-10-02 12:25:09 +02:00
|
|
|
while (*hex && *hex != '\r' && *hex != '\n') {
|
2025-03-07 23:19:59 +01:00
|
|
|
char c = *hex++;
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
result = result * 16 + (c - '0');
|
|
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
|
|
result = result * 16 + (c - 'a' + 10);
|
|
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
|
|
result = result * 16 + (c - 'A' + 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
bool http_has_header(const char *http_headers, const char *http_header_name) {
|
|
|
|
char search_for[strlen(http_header_name) + 3];
|
|
|
|
snprintf(search_for, sizeof(search_for), "%s: ", http_header_name);
|
2025-03-07 23:19:59 +01:00
|
|
|
return strstr(http_headers, search_for) != NULL;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
char *http_header_get_str(const char *http_headers, const char *http_header_name) {
|
|
|
|
char search_for[strlen(http_header_name) + 3];
|
|
|
|
snprintf(search_for, sizeof(search_for), "%s: ", http_header_name);
|
|
|
|
|
|
|
|
const char *header = strstr(http_headers, search_for);
|
|
|
|
if (!header) return NULL;
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
header += strlen(search_for);
|
2025-10-02 12:25:09 +02:00
|
|
|
const char *end = strstr(header, "\r\n");
|
|
|
|
if (!end) return NULL;
|
|
|
|
|
|
|
|
size_t value_len = end - header;
|
|
|
|
char *result = (char *)malloc(value_len + 1);
|
|
|
|
if (!result) return NULL;
|
|
|
|
|
|
|
|
memcpy(result, header, value_len);
|
|
|
|
result[value_len] = '\0';
|
2025-03-07 23:19:59 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
long http_header_get_long(const char *http_headers, const char *http_header_name) {
|
2025-03-07 23:19:59 +01:00
|
|
|
char *str_value = http_header_get_str(http_headers, http_header_name);
|
2025-10-02 12:25:09 +02:00
|
|
|
if (!str_value) return 0;
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
long long_value = atol(str_value);
|
|
|
|
free(str_value);
|
|
|
|
return long_value;
|
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
static char *read_chunked_body_ssl(SSL *ssl) {
|
|
|
|
size_t total_size = 0;
|
|
|
|
size_t buffer_capacity = INITIAL_BUFFER_SIZE;
|
|
|
|
char *buffer = (char *)malloc(buffer_capacity);
|
|
|
|
if (!buffer) return NULL;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
// Read chunk size line
|
|
|
|
char *chunk_header = read_until_ssl(ssl, "\r\n", 256);
|
|
|
|
if (!chunk_header) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t chunk_size = hex_to_int(chunk_header);
|
|
|
|
free(chunk_header);
|
|
|
|
|
|
|
|
if (chunk_size == 0) {
|
|
|
|
// Read trailing \r\n after last chunk
|
|
|
|
char trailing[3];
|
|
|
|
SSL_read(ssl, trailing, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize buffer if needed
|
|
|
|
if (total_size + chunk_size + 1 > buffer_capacity) {
|
|
|
|
buffer_capacity = total_size + chunk_size + INITIAL_BUFFER_SIZE;
|
|
|
|
char *new_buffer = (char *)realloc(buffer, buffer_capacity);
|
|
|
|
if (!new_buffer) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
buffer = new_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read chunk data
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < chunk_size) {
|
|
|
|
int n = SSL_read(ssl, buffer + total_size + bytes_read,
|
|
|
|
chunk_size - bytes_read);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
total_size += chunk_size;
|
|
|
|
|
|
|
|
// Read trailing \r\n after chunk data
|
|
|
|
char trailing[3];
|
|
|
|
SSL_read(ssl, trailing, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[total_size] = '\0';
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *read_chunked_body(int sock) {
|
|
|
|
size_t total_size = 0;
|
|
|
|
size_t buffer_capacity = INITIAL_BUFFER_SIZE;
|
|
|
|
char *buffer = (char *)malloc(buffer_capacity);
|
|
|
|
if (!buffer) return NULL;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
// Read chunk size line
|
|
|
|
char *chunk_header = read_until(sock, "\r\n", 256);
|
|
|
|
if (!chunk_header) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t chunk_size = hex_to_int(chunk_header);
|
|
|
|
free(chunk_header);
|
|
|
|
|
|
|
|
if (chunk_size == 0) {
|
|
|
|
// Read trailing \r\n after last chunk
|
|
|
|
char trailing[3];
|
|
|
|
recv(sock, trailing, 2, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize buffer if needed
|
|
|
|
if (total_size + chunk_size + 1 > buffer_capacity) {
|
|
|
|
buffer_capacity = total_size + chunk_size + INITIAL_BUFFER_SIZE;
|
|
|
|
char *new_buffer = (char *)realloc(buffer, buffer_capacity);
|
|
|
|
if (!new_buffer) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
buffer = new_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read chunk data
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < chunk_size) {
|
|
|
|
ssize_t n = recv(sock, buffer + total_size + bytes_read,
|
|
|
|
chunk_size - bytes_read, 0);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
total_size += chunk_size;
|
|
|
|
|
|
|
|
// Read trailing \r\n after chunk data
|
|
|
|
char trailing[3];
|
|
|
|
recv(sock, trailing, 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[total_size] = '\0';
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *https_post(char *url, char *data) {
|
2025-03-07 23:19:59 +01:00
|
|
|
url_t parsed_url;
|
|
|
|
parse_url(url, &parsed_url);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
init_openssl();
|
|
|
|
SSL_CTX *ctx = create_context();
|
2025-10-02 12:25:09 +02:00
|
|
|
if (!ctx) return NULL;
|
|
|
|
|
|
|
|
int sock = create_socket(parsed_url.hostname, 443);
|
|
|
|
if (sock < 0) {
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
cleanup_openssl();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL *ssl = SSL_new(ctx);
|
|
|
|
SSL_set_connect_state(ssl);
|
2025-10-02 12:25:09 +02:00
|
|
|
SSL_set_tlsext_host_name(ssl, parsed_url.hostname);
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_set_fd(ssl, sock);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *result = NULL;
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
if (SSL_connect(ssl) <= 0) {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
} else {
|
2025-10-02 12:25:09 +02:00
|
|
|
size_t data_len = strlen(data);
|
|
|
|
size_t request_size = data_len + 1024;
|
|
|
|
char *request = (char *)malloc(request_size);
|
|
|
|
|
|
|
|
snprintf(request, request_size,
|
2025-03-07 23:19:59 +01:00
|
|
|
"POST %s HTTP/1.1\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Host: %s\r\n"
|
|
|
|
"Content-Length: %zu\r\n"
|
2025-03-07 23:19:59 +01:00
|
|
|
"Content-Type: application/json\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Connection: close\r\n"
|
|
|
|
"\r\n"
|
|
|
|
"%s",
|
|
|
|
parsed_url.path, parsed_url.hostname, data_len,
|
|
|
|
data);
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_write(ssl, request, strlen(request));
|
|
|
|
free(request);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *headers = read_until_ssl(ssl, "\r\n\r\n", MAX_HEADER_SIZE);
|
|
|
|
if (headers) {
|
|
|
|
long content_length = http_header_get_long(headers, "Content-Length");
|
|
|
|
|
|
|
|
if (content_length > 0) {
|
|
|
|
result = (char *)malloc(content_length + 1);
|
|
|
|
if (result) {
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < (size_t)content_length) {
|
|
|
|
int n = SSL_read(ssl, result + bytes_read,
|
|
|
|
content_length - bytes_read);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(result);
|
|
|
|
result = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
if (result) {
|
|
|
|
result[content_length] = '\0';
|
|
|
|
}
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
|
|
|
result = read_chunked_body_ssl(ssl);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(headers);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_free(ssl);
|
|
|
|
close(sock);
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
cleanup_openssl();
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
return result;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
2025-10-02 12:25:09 +02:00
|
|
|
char *https_get(char *url) {
|
2025-03-07 23:19:59 +01:00
|
|
|
url_t parsed_url;
|
|
|
|
parse_url(url, &parsed_url);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
init_openssl();
|
|
|
|
SSL_CTX *ctx = create_context();
|
2025-10-02 12:25:09 +02:00
|
|
|
if (!ctx) return NULL;
|
|
|
|
|
|
|
|
int sock = create_socket(parsed_url.hostname, 443);
|
|
|
|
if (sock < 0) {
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
cleanup_openssl();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL *ssl = SSL_new(ctx);
|
|
|
|
SSL_set_connect_state(ssl);
|
2025-10-02 12:25:09 +02:00
|
|
|
SSL_set_tlsext_host_name(ssl, parsed_url.hostname);
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_set_fd(ssl, sock);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *result = NULL;
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
if (SSL_connect(ssl) <= 0) {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
} else {
|
2025-10-02 12:25:09 +02:00
|
|
|
char request[2048];
|
|
|
|
snprintf(request, sizeof(request),
|
2025-03-07 23:19:59 +01:00
|
|
|
"GET %s HTTP/1.1\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Host: %s\r\n"
|
|
|
|
"Connection: close\r\n"
|
|
|
|
"\r\n",
|
|
|
|
parsed_url.path, parsed_url.hostname);
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_write(ssl, request, strlen(request));
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *headers = read_until_ssl(ssl, "\r\n\r\n", MAX_HEADER_SIZE);
|
|
|
|
if (headers) {
|
|
|
|
long content_length = http_header_get_long(headers, "Content-Length");
|
|
|
|
|
|
|
|
if (content_length > 0) {
|
|
|
|
result = (char *)malloc(content_length + 1);
|
|
|
|
if (result) {
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < (size_t)content_length) {
|
|
|
|
int n = SSL_read(ssl, result + bytes_read,
|
|
|
|
content_length - bytes_read);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(result);
|
|
|
|
result = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
if (result) {
|
|
|
|
result[content_length] = '\0';
|
|
|
|
}
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
|
|
|
result = read_chunked_body_ssl(ssl);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(headers);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
SSL_free(ssl);
|
|
|
|
close(sock);
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
cleanup_openssl();
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
return result;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
char *http_post(char *url, char *data) {
|
|
|
|
url_t parsed_url;
|
|
|
|
parse_url(url, &parsed_url);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
int port = parsed_url.port ? atoi(parsed_url.port) : 80;
|
|
|
|
int sock = create_socket(parsed_url.hostname, port);
|
|
|
|
if (sock < 0) return NULL;
|
|
|
|
|
|
|
|
char *result = NULL;
|
|
|
|
size_t data_len = strlen(data);
|
|
|
|
size_t request_size = data_len + 1024;
|
|
|
|
char *request = (char *)malloc(request_size);
|
|
|
|
|
|
|
|
snprintf(request, request_size,
|
2025-03-07 23:19:59 +01:00
|
|
|
"POST %s HTTP/1.1\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Host: %s\r\n"
|
|
|
|
"Content-Length: %zu\r\n"
|
2025-03-07 23:19:59 +01:00
|
|
|
"Content-Type: application/json\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Connection: close\r\n"
|
|
|
|
"\r\n"
|
|
|
|
"%s",
|
|
|
|
parsed_url.path, parsed_url.hostname, data_len,
|
|
|
|
data);
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
send(sock, request, strlen(request), 0);
|
|
|
|
free(request);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *headers = read_until(sock, "\r\n\r\n", MAX_HEADER_SIZE);
|
|
|
|
if (headers) {
|
|
|
|
long content_length = http_header_get_long(headers, "Content-Length");
|
|
|
|
|
|
|
|
if (content_length > 0) {
|
|
|
|
result = (char *)malloc(content_length + 1);
|
|
|
|
if (result) {
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < (size_t)content_length) {
|
|
|
|
ssize_t n = recv(sock, result + bytes_read,
|
|
|
|
content_length - bytes_read, 0);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(result);
|
|
|
|
result = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
if (result) {
|
|
|
|
result[content_length] = '\0';
|
|
|
|
}
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
|
|
|
result = read_chunked_body(sock);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(headers);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
close(sock);
|
2025-10-02 12:25:09 +02:00
|
|
|
return result;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
char *http_get(char *url) {
|
|
|
|
url_t parsed_url;
|
|
|
|
parse_url(url, &parsed_url);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
int port = parsed_url.port ? atoi(parsed_url.port) : 80;
|
|
|
|
int sock = create_socket(parsed_url.hostname, port);
|
|
|
|
if (sock < 0) return NULL;
|
|
|
|
|
|
|
|
char *result = NULL;
|
|
|
|
char request[2048];
|
|
|
|
|
|
|
|
snprintf(request, sizeof(request),
|
2025-03-07 23:19:59 +01:00
|
|
|
"GET %s HTTP/1.1\r\n"
|
2025-10-02 12:25:09 +02:00
|
|
|
"Host: %s\r\n"
|
|
|
|
"Connection: close\r\n"
|
|
|
|
"\r\n",
|
|
|
|
parsed_url.path, parsed_url.hostname);
|
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
send(sock, request, strlen(request), 0);
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
char *headers = read_until(sock, "\r\n\r\n", MAX_HEADER_SIZE);
|
|
|
|
if (headers) {
|
|
|
|
long content_length = http_header_get_long(headers, "Content-Length");
|
|
|
|
|
|
|
|
if (content_length > 0) {
|
|
|
|
result = (char *)malloc(content_length + 1);
|
|
|
|
if (result) {
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
while (bytes_read < (size_t)content_length) {
|
|
|
|
ssize_t n = recv(sock, result + bytes_read,
|
|
|
|
content_length - bytes_read, 0);
|
|
|
|
if (n <= 0) {
|
|
|
|
free(result);
|
|
|
|
result = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bytes_read += n;
|
|
|
|
}
|
|
|
|
if (result) {
|
|
|
|
result[content_length] = '\0';
|
|
|
|
}
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
|
|
|
result = read_chunked_body(sock);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
|
|
|
free(headers);
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
2025-10-02 12:25:09 +02:00
|
|
|
|
2025-03-07 23:19:59 +01:00
|
|
|
close(sock);
|
2025-10-02 12:25:09 +02:00
|
|
|
return result;
|
2025-03-07 23:19:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|