|
// Written by retoor@molodetz.nl
|
|
|
|
// The source code provides functionality for making HTTP POST and GET requests over SSL/TLS using OpenSSL. It includes initialization and cleanup of the OpenSSL library, creation of SSL context, socket creation and connection, and sending requests with handling responses. Furthermore, it interfaces with JSON and handles authentication using an external "auth.h" file.
|
|
|
|
// Includes: "auth.h", <json-c/json.h>
|
|
|
|
// 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 "auth.h"
|
|
#include <json-c/json.h>
|
|
#include <stdbool.h>
|
|
#include "url.h"
|
|
|
|
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_method();
|
|
SSL_CTX *ctx = SSL_CTX_new(method);
|
|
SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
SSL_CTX *create_context2()
|
|
{
|
|
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);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
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");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
perror("Unable to create socket");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
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);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
typedef struct ssl_st ssl_stt ;
|
|
|
|
char *read_until_ssl(ssl_stt * sock, char *until)
|
|
{
|
|
static char data[1024 * 1024];
|
|
data[0] = 0;
|
|
int index = 0;
|
|
char chunk[2];
|
|
while (SSL_read(sock, chunk, 1) == 1)
|
|
{
|
|
data[index] = chunk[0];
|
|
index++;
|
|
data[index] = 0;
|
|
if (strstr(data, until) != NULL)
|
|
{
|
|
return data;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *read_until(int sock, char *until)
|
|
{
|
|
static char data[1024 * 1024];
|
|
data[0] = 0;
|
|
int index = 0;
|
|
char chunk[2];
|
|
while (recv(sock, chunk, 1,0) == 1)
|
|
{
|
|
data[index] = chunk[0];
|
|
index++;
|
|
data[index] = 0;
|
|
if (strstr(data, until) != NULL)
|
|
{
|
|
return data;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
size_t hex_to_int(const char *hex)
|
|
{
|
|
size_t result = 0;
|
|
while (*hex)
|
|
{
|
|
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;
|
|
}
|
|
|
|
char *https_post(char *url, char *data)
|
|
{
|
|
url_t parsed_url;
|
|
parse_url(url,&parsed_url);
|
|
char *hostname =parsed_url.hostname;
|
|
char *path = parsed_url.path;
|
|
init_openssl();
|
|
int port = 443;
|
|
SSL_CTX *ctx = create_context();
|
|
int sock = create_socket(hostname, port);
|
|
SSL *ssl = SSL_new(ctx);
|
|
SSL_set_connect_state(ssl);
|
|
SSL_set_tlsext_host_name(ssl, hostname);
|
|
SSL_set_fd(ssl, sock);
|
|
|
|
int buffer_size = 4096;
|
|
char *buffer = malloc(buffer_size);
|
|
size_t chunk_size_total = 0;
|
|
if (SSL_connect(ssl) <= 0)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
}
|
|
else
|
|
{
|
|
size_t len = strlen(data);
|
|
char *request = malloc(len + 4096);
|
|
sprintf(request,
|
|
"POST %s HTTP/1.1\r\n"
|
|
"Content-Length: %ld\r\n"
|
|
"Content-Type: application/json\r\n"
|
|
"Host: api.openai.com\r\n"
|
|
"Authorization: Bearer %s\r\n"
|
|
"Connection: close\r\n\r\n%s",
|
|
path, len, resolve_api_key(), data);
|
|
|
|
SSL_write(ssl, request, strlen(request));
|
|
free(request);
|
|
|
|
char *headers = read_until_ssl(ssl, "\r\n\r\n");
|
|
(void)headers;
|
|
|
|
size_t actual_buffer_size = buffer_size;
|
|
while (true)
|
|
{
|
|
char *header = read_until_ssl(ssl, "\r\n");
|
|
size_t chunk_size = hex_to_int(header);
|
|
if (chunk_size == 0)
|
|
break;
|
|
size_t remaining = chunk_size;
|
|
while (remaining > 0)
|
|
{
|
|
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
|
|
buffer = realloc(buffer, actual_buffer_size + to_read + 1);
|
|
actual_buffer_size += to_read;
|
|
size_t bytes_read = SSL_read(ssl, buffer + chunk_size_total, to_read);
|
|
chunk_size_total += bytes_read;
|
|
if (bytes_read <= 0)
|
|
{
|
|
fprintf(stderr, "Error reading chunk data\n");
|
|
return NULL;
|
|
}
|
|
remaining -= bytes_read;
|
|
}
|
|
}
|
|
buffer[chunk_size_total] = 0;
|
|
}
|
|
|
|
SSL_free(ssl);
|
|
close(sock);
|
|
SSL_CTX_free(ctx);
|
|
cleanup_openssl();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
char *https_get(char *url)
|
|
{
|
|
url_t parsed_url;
|
|
parse_url(url,&parsed_url);
|
|
char *hostname =parsed_url.hostname;
|
|
char *path = parsed_url.path;
|
|
|
|
init_openssl();
|
|
int port = 443;
|
|
SSL_CTX *ctx = create_context();
|
|
int sock = create_socket(hostname, port);
|
|
SSL *ssl = SSL_new(ctx);
|
|
SSL_set_connect_state(ssl);
|
|
SSL_set_tlsext_host_name(ssl, hostname);
|
|
SSL_set_fd(ssl, sock);
|
|
|
|
int buffer_size = 4096;
|
|
char *buffer = (char *)malloc(buffer_size);
|
|
|
|
if (SSL_connect(ssl) <= 0)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
}
|
|
else
|
|
{
|
|
char request[buffer_size];
|
|
sprintf(request,
|
|
"GET %s HTTP/1.1\r\n"
|
|
"Host: api.openai.com\r\n"
|
|
"Authorization: Bearer %s\r\n"
|
|
"Connection: close\r\n\r\n",
|
|
path, resolve_api_key());
|
|
|
|
SSL_write(ssl, request, strlen(request));
|
|
|
|
char *headers = read_until_ssl(ssl, "\r\n\r\n");
|
|
(void)headers;
|
|
size_t chunk_size_total = 0;
|
|
size_t actual_buffer_size = buffer_size;
|
|
while (true)
|
|
{
|
|
char *header = read_until_ssl(ssl, "\r\n");
|
|
size_t chunk_size = hex_to_int(header);
|
|
if (chunk_size == 0)
|
|
break;
|
|
size_t remaining = chunk_size;
|
|
while (remaining > 0)
|
|
{
|
|
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
|
|
buffer = realloc(buffer, actual_buffer_size + to_read + 1);
|
|
actual_buffer_size += to_read;
|
|
size_t bytes_read = SSL_read(ssl, buffer + chunk_size_total, to_read);
|
|
chunk_size_total += bytes_read;
|
|
if (bytes_read <= 0)
|
|
{
|
|
fprintf(stderr, "Error reading chunk data\n");
|
|
return NULL;
|
|
}
|
|
remaining -= bytes_read;
|
|
}
|
|
}
|
|
buffer[chunk_size_total] = 0;
|
|
}
|
|
|
|
SSL_free(ssl);
|
|
close(sock);
|
|
SSL_CTX_free(ctx);
|
|
cleanup_openssl();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
char *http_post(char *url, char *data)
|
|
{
|
|
url_t parsed_url;
|
|
parse_url(url,&parsed_url);
|
|
char *hostname =parsed_url.hostname;
|
|
char *path = parsed_url.path;
|
|
int port = atoi(parsed_url.port);
|
|
int sock = create_socket(hostname, port);
|
|
|
|
int buffer_size = 4096;
|
|
char *buffer = malloc(buffer_size);
|
|
size_t chunk_size_total = 0;
|
|
|
|
size_t len = strlen(data);
|
|
char *request = malloc(len + 4096);
|
|
sprintf(request,
|
|
"POST %s HTTP/1.1\r\n"
|
|
"Content-Length: %ld\r\n"
|
|
"Content-Type: application/json\r\n"
|
|
"Host: api.openai.com\r\n"
|
|
"Authorization: Bearer %s\r\n"
|
|
"Connection: close\r\n\r\n%s",
|
|
path, len, resolve_api_key(), data);
|
|
|
|
send(sock, request,strlen(request),0);
|
|
|
|
free(request);
|
|
|
|
char *headers = read_until(sock, "\r\n\r\n");
|
|
printf("%s\n",headers);
|
|
(void)headers;
|
|
|
|
size_t actual_buffer_size = buffer_size;
|
|
while (true)
|
|
{
|
|
char *header = read_until(sock, "\r\n");
|
|
size_t chunk_size = hex_to_int(header);
|
|
if (chunk_size == 0)
|
|
break;
|
|
size_t remaining = chunk_size;
|
|
while (remaining > 0)
|
|
{
|
|
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
|
|
buffer = realloc(buffer, actual_buffer_size + to_read + 1);
|
|
actual_buffer_size += to_read;
|
|
size_t bytes_read = recv(sock, buffer + chunk_size_total, to_read,0);
|
|
chunk_size_total += bytes_read;
|
|
if (bytes_read <= 0)
|
|
{
|
|
fprintf(stderr, "Error reading chunk data\n");
|
|
return NULL;
|
|
}
|
|
// fwrite(buffer, 1, bytes_read, stdout); // Output chunk data
|
|
remaining -= bytes_read;
|
|
}
|
|
}
|
|
buffer[chunk_size_total] = 0;
|
|
|
|
|
|
close(sock);
|
|
|
|
|
|
return buffer;
|
|
}
|
|
|
|
char *http_get(char *url)
|
|
{
|
|
url_t parsed_url;
|
|
parse_url(url,&parsed_url);
|
|
char *hostname =parsed_url.hostname;
|
|
char *path = parsed_url.path;
|
|
int port = atoi(parsed_url.port);
|
|
int sock = create_socket(hostname, port);
|
|
|
|
|
|
int buffer_size = 4096;
|
|
char *buffer = (char *)malloc(buffer_size);
|
|
|
|
|
|
char request[buffer_size];
|
|
sprintf(request,
|
|
"GET %s HTTP/1.1\r\n"
|
|
"Host: api.openai.com\r\n"
|
|
"Authorization: Bearer %s\r\n"
|
|
"Connection: close\r\n\r\n",
|
|
path, resolve_api_key());
|
|
|
|
send(sock, request, strlen(request),0);
|
|
|
|
char *headers = read_until(sock, "\r\n\r\n");
|
|
(void)headers;
|
|
size_t chunk_size_total = 0;
|
|
size_t actual_buffer_size = buffer_size;
|
|
while (true)
|
|
{
|
|
char *header = read_until(sock, "\r\n");
|
|
size_t chunk_size = hex_to_int(header);
|
|
if (chunk_size == 0)
|
|
break;
|
|
size_t remaining = chunk_size;
|
|
while (remaining > 0)
|
|
{
|
|
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
|
|
buffer = realloc(buffer, actual_buffer_size + to_read + 1);
|
|
actual_buffer_size += to_read;
|
|
size_t bytes_read = recv(sock, buffer + chunk_size_total, to_read,0);
|
|
chunk_size_total += bytes_read;
|
|
if (bytes_read <= 0)
|
|
{
|
|
fprintf(stderr, "Error reading chunk data\n");
|
|
return NULL;
|
|
}
|
|
remaining -= bytes_read;
|
|
}
|
|
}
|
|
buffer[chunk_size_total] = 0;
|
|
close(sock);
|
|
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
#endif
|