New gitea client.
This commit is contained in:
parent
3f60abde14
commit
1f2e3b7efe
360
gitea_client.py
Normal file
360
gitea_client.py
Normal file
@ -0,0 +1,360 @@
|
||||
import aiohttp
|
||||
|
||||
class GiteaClient:
|
||||
"""
|
||||
Asynchronous Gitea API client using aiohttp.
|
||||
Uses a persistent ClientSession and base_url for all requests.
|
||||
|
||||
Args:
|
||||
base_url (str): Root server endpoint (e.g. 'https://gitea.example.com').
|
||||
token (str|None): API token sent in 'Authorization: token <token>'.
|
||||
otp (str|None): Optional TOTP for X-Gitea-OTP header (for MFA-protected endpoints).
|
||||
sudo (str|None): Optional user to impersonate via Sudo header.
|
||||
timeout (int|float): Session-wide total request timeout seconds.
|
||||
|
||||
Attributes:
|
||||
session (aiohttp.ClientSession): Created on start(), closed on stop().
|
||||
"""
|
||||
def __init__(self, base_url, token=None, otp=None, sudo=None, timeout=30):
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token
|
||||
self.otp = otp
|
||||
self.sudo = sudo
|
||||
self.timeout = aiohttp.ClientTimeout(total=timeout)
|
||||
self.session = None
|
||||
|
||||
async def start(self):
|
||||
"""
|
||||
Open persistent aiohttp.ClientSession with preconfigured headers.
|
||||
"""
|
||||
headers = {"Accept": "application/json"}
|
||||
if self.token:
|
||||
headers["Authorization"] = f"token {self.token}"
|
||||
if self.otp:
|
||||
headers["X-Gitea-OTP"] = self.otp
|
||||
if self.sudo:
|
||||
headers["Sudo"] = self.sudo
|
||||
self.session = aiohttp.ClientSession(headers=headers, timeout=self.timeout)
|
||||
|
||||
async def stop(self):
|
||||
"""
|
||||
Close persistent aiohttp.ClientSession.
|
||||
"""
|
||||
if self.session:
|
||||
await self.session.close()
|
||||
|
||||
async def _request(self, method, path, params=None, json=None):
|
||||
"""
|
||||
Internal unified low-level API call executor.
|
||||
|
||||
Args:
|
||||
method (str): HTTP method, e.g., 'GET', 'POST', 'PATCH', etc.
|
||||
path (str): API path relative to base_url (e.g., 'api/v1/user').
|
||||
params (dict|None): Query parameters.
|
||||
json (dict|None): JSON-encoded request payload.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP response status code.
|
||||
headers (dict): HTTP response headers.
|
||||
data (dict|list|str): Parsed JSON response if Content-Type is application/json,
|
||||
otherwise raw text.
|
||||
"""
|
||||
url = f"{self.base_url}/{path.lstrip('/')}"
|
||||
async with self.session.request(method, url, params=params, json=json) as resp:
|
||||
status = resp.status
|
||||
headers = dict(resp.headers)
|
||||
ct = headers.get("Content-Type", "")
|
||||
if "application/json" in ct:
|
||||
data = await resp.json()
|
||||
else:
|
||||
data = await resp.text()
|
||||
return {"status": status, "headers": headers, "data": data}
|
||||
|
||||
async def me(self):
|
||||
"""
|
||||
Get the authenticated user's detailed account info.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200 if OK.
|
||||
headers (dict): Response headers.
|
||||
data (dict): JSON user object.
|
||||
|
||||
JSON Format Example:
|
||||
{
|
||||
"id": 123,
|
||||
"login": "username",
|
||||
"full_name": "User Name",
|
||||
"email": "user@example.com",
|
||||
...
|
||||
}
|
||||
"""
|
||||
return await self._request("GET", "api/v1/user")
|
||||
|
||||
async def user(self, username):
|
||||
"""
|
||||
Get public profile of a given user.
|
||||
|
||||
Args:
|
||||
username (str): Target username.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200 if OK; 404 if missing.
|
||||
headers (dict): Response headers.
|
||||
data (dict): JSON user object (public fields).
|
||||
|
||||
JSON Format:
|
||||
{
|
||||
"id": 124,
|
||||
"login": "publicname",
|
||||
"full_name": "",
|
||||
...
|
||||
}
|
||||
"""
|
||||
return await self._request("GET", f"api/v1/users/{username}")
|
||||
|
||||
async def user_tokens(self, username, page=None, limit=None):
|
||||
"""
|
||||
List user access tokens for admin or self.
|
||||
|
||||
Args:
|
||||
username (str): Username to list tokens for.
|
||||
page (int|None): Pagination.
|
||||
limit (int|None): Page size.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200.
|
||||
headers (dict): Contains X-Total-Count for pagination.
|
||||
data (list): List of token meta objects (no plaintext token).
|
||||
|
||||
JSON Format Example:
|
||||
[
|
||||
{
|
||||
"id": 873,
|
||||
"name": "ci",
|
||||
"sha1_last_eight": "abcd1234",
|
||||
...
|
||||
}
|
||||
]
|
||||
"""
|
||||
params = {}
|
||||
if page is not None:
|
||||
params["page"] = page
|
||||
if limit is not None:
|
||||
params["limit"] = limit
|
||||
return await self._request("GET", f"api/v1/users/{username}/tokens", params=params)
|
||||
|
||||
async def create_token(self, username, name):
|
||||
"""
|
||||
Create a new API token for a user (requires BasicAuth).
|
||||
|
||||
Args:
|
||||
username (str): Username to create token for.
|
||||
name (str): New token name.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 201 if created.
|
||||
headers (dict): Response headers.
|
||||
data (dict): Token object including token sha1 seen only once.
|
||||
|
||||
JSON Format Example:
|
||||
{
|
||||
"id": 322,
|
||||
"name": "ci-token",
|
||||
"sha1": "tokenplaintextvalue",
|
||||
"sha1_last_eight": "1234abcd"
|
||||
}
|
||||
"""
|
||||
return await self._request("POST", f"api/v1/users/{username}/tokens", json={"name": name})
|
||||
|
||||
async def delete_token(self, username, token_id):
|
||||
"""
|
||||
Delete a token.
|
||||
|
||||
Args:
|
||||
username (str): Username.
|
||||
token_id (int): Token id.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 204 on success.
|
||||
headers (dict): Response headers.
|
||||
data (str): '' (empty text).
|
||||
|
||||
JSON: No content (empty body) on success.
|
||||
"""
|
||||
return await self._request("DELETE", f"api/v1/users/{username}/tokens/{token_id}")
|
||||
|
||||
async def user_repos(self, username, page=None, limit=None, sort=None):
|
||||
"""
|
||||
List repositories for a user.
|
||||
|
||||
Args:
|
||||
username (str): Username.
|
||||
page (int|None): Page number.
|
||||
limit (int|None): Per-page count.
|
||||
sort (str|None): Sort field.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200.
|
||||
headers (dict): Includes 'X-Total-Count' and pagination links.
|
||||
data (list): List of repo objects.
|
||||
|
||||
JSON Format Example:
|
||||
[
|
||||
{
|
||||
"id": 8,
|
||||
"name": "repo1",
|
||||
"full_name": "username/repo1",
|
||||
"private": false,
|
||||
"owner": {...},
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
params = {}
|
||||
if page is not None:
|
||||
params["page"] = page
|
||||
if limit is not None:
|
||||
params["limit"] = limit
|
||||
if sort is not None:
|
||||
params["sort"] = sort
|
||||
return await self._request("GET", f"api/v1/users/{username}/repos", params=params)
|
||||
|
||||
async def repo(self, owner, repo):
|
||||
"""
|
||||
Get full metadata for a repository.
|
||||
|
||||
Args:
|
||||
owner (str): Username/Org.
|
||||
repo (str): Repository name.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200 or 404.
|
||||
headers (dict): Response headers.
|
||||
data (dict): Repository object.
|
||||
|
||||
JSON Format Example:
|
||||
{
|
||||
"id": 8,
|
||||
"name": "repo1",
|
||||
"full_name": "user/repo1",
|
||||
"private": false,
|
||||
"description": "",
|
||||
"forks_count": 0,
|
||||
...
|
||||
}
|
||||
"""
|
||||
return await self._request("GET", f"api/v1/repos/{owner}/{repo}")
|
||||
|
||||
async def create_repo(self, name, private=False, description=None, auto_init=False, default_branch=None):
|
||||
"""
|
||||
Create a repository for the authenticated user.
|
||||
|
||||
Args:
|
||||
name (str): Repository name.
|
||||
private (bool): Create as private repo.
|
||||
description (str|None): Optional description.
|
||||
auto_init (bool): Initialize with README.
|
||||
default_branch (str|None): Set initial branch name.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 201 if created.
|
||||
headers (dict): Response headers.
|
||||
data (dict): Repository object.
|
||||
|
||||
JSON Format: as per `repo()`
|
||||
"""
|
||||
payload = {"name": name, "private": private, "auto_init": auto_init}
|
||||
if description is not None:
|
||||
payload["description"] = description
|
||||
if default_branch is not None:
|
||||
payload["default_branch"] = default_branch
|
||||
return await self._request("POST", "api/v1/user/repos", json=payload)
|
||||
|
||||
async def repo_issues(self, owner, repo, state=None, page=None, limit=None):
|
||||
"""
|
||||
List issues for a repository.
|
||||
|
||||
Args:
|
||||
owner (str): Owner.
|
||||
repo (str): Repo name.
|
||||
state (str|None): Optional state ('open', 'closed', 'all').
|
||||
page (int|None): Page.
|
||||
limit (int|None): Per-page count.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 200.
|
||||
headers (dict): Pagination info.
|
||||
data (list): List of issue objects.
|
||||
|
||||
JSON Example:
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"index": 2,
|
||||
"title": "Bug found",
|
||||
"state": "open",
|
||||
"user": {...},
|
||||
...
|
||||
}, ...
|
||||
]
|
||||
"""
|
||||
params = {}
|
||||
if state is not None:
|
||||
params["state"] = state
|
||||
if page is not None:
|
||||
params["page"] = page
|
||||
if limit is not None:
|
||||
params["limit"] = limit
|
||||
return await self._request("GET", f"api/v1/repos/{owner}/{repo}/issues", params=params)
|
||||
|
||||
async def create_issue(self, owner, repo, title, body=None, assignees=None, labels=None, milestone=None):
|
||||
"""
|
||||
Create a new issue.
|
||||
|
||||
Args:
|
||||
owner (str): Owner.
|
||||
repo (str): Repo name.
|
||||
title (str): Issue title.
|
||||
body (str|None): Issue text.
|
||||
assignees (list|None): Assignees.
|
||||
labels (list|None): Labels.
|
||||
milestone (int|None): Milestone number.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
status (int): HTTP 201 if created.
|
||||
headers (dict): Response headers.
|
||||
data (dict): Issue object.
|
||||
|
||||
JSON Format Example:
|
||||
{
|
||||
"id": 1,
|
||||
"index": 2,
|
||||
"title": "Bug",
|
||||
"body": "...",
|
||||
"state": "open",
|
||||
...
|
||||
}
|
||||
"""
|
||||
payload = {"title": title}
|
||||
if body is not None:
|
||||
payload["body"] = body
|
||||
if assignees is not None:
|
||||
payload["assignees"] = assignees
|
||||
if labels is not None:
|
||||
payload["labels"] = labels
|
||||
if milestone is not None:
|
||||
payload["milestone"] = milestone
|
||||
return await self._request("POST", f"api/v1/repos/{owner}/{repo}/issues", json=payload)
|
||||
|
679
http.h
679
http.h
@ -1,12 +1,8 @@
|
||||
// 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. It also interfaces with JSON and handles authentication using an external "auth.h" file.
|
||||
|
||||
// Includes: "auth.h", <json-c/json.h>
|
||||
// Improved version with better reliability and chunked content support
|
||||
|
||||
// MIT License
|
||||
|
||||
|
||||
#ifndef R_HTTP_H
|
||||
#define R_HTTP_H
|
||||
|
||||
@ -18,11 +14,13 @@
|
||||
#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"
|
||||
|
||||
#define INITIAL_BUFFER_SIZE (1024 * 1024)
|
||||
#define MAX_HEADER_SIZE (8 * 1024)
|
||||
|
||||
void init_openssl() {
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
@ -33,20 +31,15 @@ void cleanup_openssl() {
|
||||
}
|
||||
|
||||
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 NULL;
|
||||
}
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
||||
SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -57,13 +50,13 @@ int create_socket(const char *hostname, int port) {
|
||||
host = gethostbyname(hostname);
|
||||
if (!host) {
|
||||
perror("Unable to resolve host");
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
perror("Unable to create socket");
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
@ -73,45 +66,71 @@ int create_socket(const char *hostname, int port) {
|
||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
perror("Unable to connect to host");
|
||||
close(sock);
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
typedef struct ssl_st ssl_stt;
|
||||
|
||||
char *read_until_ssl(ssl_stt *sock, char *until) {
|
||||
static char data[1024 * 1024] = {0};
|
||||
int index = 0;
|
||||
char chunk[2];
|
||||
while (SSL_read(sock, chunk, 1) == 1) {
|
||||
data[index++] = chunk[0];
|
||||
data[index] = 0;
|
||||
if (strstr(data, until) != NULL) {
|
||||
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) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *read_until(int sock, char *until) {
|
||||
static char data[1024 * 1024] = {0};
|
||||
int index = 0;
|
||||
char chunk[2];
|
||||
while (recv(sock, chunk, 1, 0) == 1) {
|
||||
data[index++] = chunk[0];
|
||||
data[index] = 0;
|
||||
if (strstr(data, until) != NULL) {
|
||||
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) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t hex_to_int(const char *hex) {
|
||||
size_t result = 0;
|
||||
while (*hex) {
|
||||
while (*hex && *hex != '\r' && *hex != '\n') {
|
||||
char c = *hex++;
|
||||
if (c >= '0' && c <= '9') {
|
||||
result = result * 16 + (c - '0');
|
||||
@ -124,304 +143,422 @@ size_t hex_to_int(const char *hex) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool http_has_header(char *http_headers, char *http_header_name) {
|
||||
char search_for[strlen(http_header_name) + 10];
|
||||
search_for[0] = '\0';
|
||||
strcpy(search_for, http_header_name);
|
||||
strcat(search_for, ": ");
|
||||
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);
|
||||
return strstr(http_headers, search_for) != NULL;
|
||||
}
|
||||
|
||||
char *http_header_get_str(char *http_headers, char *http_header_name) {
|
||||
char search_for[strlen(http_header_name) + 10];
|
||||
search_for[0] = '\0';
|
||||
strcpy(search_for, http_header_name);
|
||||
strcat(search_for, ": ");
|
||||
char *header = strstr(http_headers, search_for);
|
||||
if (!header) {
|
||||
return NULL;
|
||||
}
|
||||
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;
|
||||
|
||||
header += strlen(search_for);
|
||||
char *end = strstr(header, "\r\n");
|
||||
*end = '\0';
|
||||
char *result = (char *)malloc(end - header + 1);
|
||||
strcpy(result, header);
|
||||
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';
|
||||
return result;
|
||||
}
|
||||
|
||||
long http_header_get_long(char *http_headers, char *http_header_name) {
|
||||
long http_header_get_long(const char *http_headers, const char *http_header_name) {
|
||||
char *str_value = http_header_get_str(http_headers, http_header_name);
|
||||
if (!str_value) {
|
||||
return 0;
|
||||
}
|
||||
if (!str_value) return 0;
|
||||
|
||||
long long_value = atol(str_value);
|
||||
free(str_value);
|
||||
return long_value;
|
||||
}
|
||||
|
||||
char *https_post(const 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 = 1024 * 1024;
|
||||
char *buffer = (char *)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 = (char *)malloc(len + 1024 * 1024);
|
||||
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;
|
||||
|
||||
long content_length = http_header_get_long(headers, "Content-Length");
|
||||
if (content_length) {
|
||||
if (content_length > buffer_size) {
|
||||
buffer_size = content_length;
|
||||
buffer = (char *)realloc(buffer, buffer_size);
|
||||
}
|
||||
size_t bytes_read = 0;
|
||||
while (bytes_read < content_length) {
|
||||
int bytes_read_chunk = SSL_read(ssl, buffer + bytes_read, buffer_size - bytes_read);
|
||||
if (bytes_read_chunk <= 0) {
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
bytes_read += bytes_read_chunk;
|
||||
}
|
||||
return buffer;
|
||||
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 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 = (char *)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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
buffer[chunk_size_total] = 0;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
SSL_CTX_free(ctx);
|
||||
cleanup_openssl();
|
||||
|
||||
|
||||
buffer[total_size] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *https_get(const char *url) {
|
||||
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) {
|
||||
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);
|
||||
if (!ctx) return NULL;
|
||||
|
||||
int sock = create_socket(parsed_url.hostname, 443);
|
||||
if (sock < 0) {
|
||||
SSL_CTX_free(ctx);
|
||||
cleanup_openssl();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SSL *ssl = SSL_new(ctx);
|
||||
SSL_set_connect_state(ssl);
|
||||
SSL_set_tlsext_host_name(ssl, hostname);
|
||||
SSL_set_tlsext_host_name(ssl, parsed_url.hostname);
|
||||
SSL_set_fd(ssl, sock);
|
||||
|
||||
int buffer_size = 1024 * 1024;
|
||||
char *buffer = malloc(buffer_size);
|
||||
|
||||
|
||||
char *result = NULL;
|
||||
|
||||
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());
|
||||
|
||||
size_t data_len = strlen(data);
|
||||
size_t request_size = data_len + 1024;
|
||||
char *request = (char *)malloc(request_size);
|
||||
|
||||
snprintf(request, request_size,
|
||||
"POST %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
parsed_url.path, parsed_url.hostname, data_len,
|
||||
data);
|
||||
|
||||
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;
|
||||
free(request);
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
remaining -= bytes_read;
|
||||
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
||||
result = read_chunked_body_ssl(ssl);
|
||||
}
|
||||
|
||||
free(headers);
|
||||
}
|
||||
buffer[chunk_size_total] = 0;
|
||||
}
|
||||
|
||||
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
SSL_CTX_free(ctx);
|
||||
cleanup_openssl();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
char *https_get(char *url) {
|
||||
url_t parsed_url;
|
||||
parse_url(url, &parsed_url);
|
||||
|
||||
init_openssl();
|
||||
SSL_CTX *ctx = create_context();
|
||||
if (!ctx) return NULL;
|
||||
|
||||
int sock = create_socket(parsed_url.hostname, 443);
|
||||
if (sock < 0) {
|
||||
SSL_CTX_free(ctx);
|
||||
cleanup_openssl();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SSL *ssl = SSL_new(ctx);
|
||||
SSL_set_connect_state(ssl);
|
||||
SSL_set_tlsext_host_name(ssl, parsed_url.hostname);
|
||||
SSL_set_fd(ssl, sock);
|
||||
|
||||
char *result = NULL;
|
||||
|
||||
if (SSL_connect(ssl) <= 0) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
} else {
|
||||
char request[2048];
|
||||
snprintf(request, sizeof(request),
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
parsed_url.path, parsed_url.hostname);
|
||||
|
||||
SSL_write(ssl, request, strlen(request));
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
||||
result = read_chunked_body_ssl(ssl);
|
||||
}
|
||||
|
||||
free(headers);
|
||||
}
|
||||
}
|
||||
|
||||
SSL_free(ssl);
|
||||
close(sock);
|
||||
SSL_CTX_free(ctx);
|
||||
cleanup_openssl();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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 = 1024 * 1024;
|
||||
char *buffer = malloc(buffer_size);
|
||||
size_t chunk_size_total = 0;
|
||||
|
||||
size_t len = strlen(data) + strlen(path) + strlen(resolve_api_key()) + 10;
|
||||
char *request = malloc(len + buffer_size);
|
||||
sprintf(request,
|
||||
|
||||
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,
|
||||
"POST %s HTTP/1.1\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Content-Length: %zu\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);
|
||||
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
parsed_url.path, parsed_url.hostname, data_len,
|
||||
data);
|
||||
|
||||
send(sock, request, strlen(request), 0);
|
||||
free(request);
|
||||
|
||||
char *headers = read_until(sock, "\r\n\r\n");
|
||||
(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) {
|
||||
printf("END\n");
|
||||
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 + 10);
|
||||
actual_buffer_size += to_read;
|
||||
size_t bytes_read = recv(sock, buffer + chunk_size_total, to_read, 0);
|
||||
|
||||
if (bytes_read <= 0) {
|
||||
fprintf(stderr, "Error reading chunk data\n");
|
||||
return NULL;
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
chunk_size_total += bytes_read;
|
||||
remaining -= bytes_read;
|
||||
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
||||
result = read_chunked_body(sock);
|
||||
}
|
||||
|
||||
free(headers);
|
||||
}
|
||||
printf("HERE!\n");
|
||||
buffer[chunk_size_total] = 0;
|
||||
|
||||
|
||||
close(sock);
|
||||
|
||||
return buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
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 = 1024 * 1024;
|
||||
char *buffer = malloc(buffer_size);
|
||||
|
||||
char request[buffer_size];
|
||||
sprintf(request,
|
||||
|
||||
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),
|
||||
"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());
|
||||
|
||||
"Host: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
parsed_url.path, parsed_url.hostname);
|
||||
|
||||
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;
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
remaining -= bytes_read;
|
||||
} else if (http_has_header(headers, "Transfer-Encoding")) {
|
||||
result = read_chunked_body(sock);
|
||||
}
|
||||
|
||||
free(headers);
|
||||
}
|
||||
buffer[chunk_size_total] = 0;
|
||||
|
||||
close(sock);
|
||||
|
||||
return buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user