simple http client

This commit is contained in:
2026-01-03 04:20:33 +02:00
parent 1897035d8e
commit 965cecc901
16 changed files with 428 additions and 521 deletions

76
http/build.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include "helper.h"
#include "c.h"
#include "ld.h"
#include "tier1/utlstring.h"
#include "tier1/commandline.h"
ADD_DEPENDENCY_BUILD_FILE(tier0, "../tier0/build.cpp")
ADD_DEPENDENCY_BUILD_FILE(tier1, "../tier1/build.cpp")
ADD_DEPENDENCY_BUILD_FILE(tier2, "../tier2/build.cpp")
DECLARE_BUILD_STAGE(funnyhttp)
{
CProject_t compileProject = {};
LinkProject_t ldProject = {};
compileProject.m_szName = "funnyhttp";
compileProject.files = {
"client.cpp"
};
compileProject.includeDirectories = {"../public"};
compileProject.bFPIC = true;
ldProject = ccompiler->Compile(&compileProject);
ldProject.objects.AppendTail({GET_PROJECT_LIBRARY(tier1, "tier1")});
ldProject.objects.AppendTail({GET_PROJECT_LIBRARY(tier2, "tier2")});
ldProject.libraries = {
"ssl",
"crypto",
};
ldProject.linkType = ELINK_DYNAMIC_LIBRARY;
CUtlString szOutputDir = linker->Link(&ldProject);
ADD_OUTPUT_OBJECT("funnyhttp", szOutputDir)
return 0;
};
DECLARE_BUILD_STAGE(test)
{
CProject_t compileProject = {};
LinkProject_t ldProject = {};
filesystem2->MakeDirectory("build");
filesystem2->CopyFile("build",GET_PROJECT_LIBRARY(funnyhttp, "funnyhttp"));
filesystem2->CopyFile("build",GET_PROJECT_LIBRARY(tier0, "tier0"));
compileProject.m_szName = "funnyhttptest";
compileProject.files = {
"test.cpp"
};
compileProject.includeDirectories = {"../public"};
compileProject.bFPIC = true;
ldProject = ccompiler->Compile(&compileProject);
ldProject.libraryObjects = {
GET_PROJECT_LIBRARY(tier1, "tier1"),
GET_PROJECT_LIBRARY(tier2, "tier2"),
};
ldProject.libraries = {
"tier0",
};
ldProject.libraryDirectories = {
"build",
};
ldProject.linkType = ELINK_EXECUTABLE;
CUtlString szOutputDir = linker->Link(&ldProject);
filesystem2->CopyFile("build",szOutputDir);
CUtlVector<CUtlString> args = {};
runner->Run("build/funnyhttptest", args);
runner->Wait();
return 0;
};

View File

@@ -0,0 +1,240 @@
#include "http/http.h"
#include "tier1/interface.h"
#include "tier1/utlstring.h"
#include "netdb.h"
#include "unistd.h"
#include "fcntl.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
abstract_class CHTTPClient: public IHTTPClient
{
public:
virtual void Post( const char *szResource, HTTPHeader_t *pHeader, uint32_t uDataSize, const char *data ) override;
virtual void Get( const char *szResource, HTTPHeader_t *pHeader ) override;
virtual HTTPResponse_t GetResponse() override;
HTTPResponse_t ParseResponse( const char *szMessage );
const char *GetVersion();
const char *m_szHostName;
bool m_bIsSecure;
SSL *m_pSSL;
SSL_CTX *m_pSSLCtx;
int m_iFileDescriptor;
};
void CHTTPClient::Post( const char *szResource, HTTPHeader_t *pHeader, uint32_t uDataSize, const char *data )
{
}
void CHTTPClient::Get( const char *szResource, HTTPHeader_t *pHeader )
{
if (pHeader == NULL)
return;
CUtlString szMessage = CUtlString(
"GET %s HTTP/%s\r\n",
szResource, GetVersion());
CUtlString szHeader = CUtlString(
"Host: %s\r\n",
m_szHostName
);
CUtlString szCombined;
int i = 0;
for ( i = 0; i < pHeader->m_nParamCount; i++ )
{
szHeader.AppendTail(CUtlString("%s: %s\r\n", pHeader->m_params[i].m_szParamName, pHeader->m_params[i].m_szValue));
}
szCombined = szMessage;
szCombined.AppendTail(szHeader);
szCombined.AppendTail("\r\n");
if (m_bIsSecure)
SSL_write(m_pSSL, szCombined.GetString(), szCombined.GetLenght());
else
write(m_iFileDescriptor, szCombined.GetString(), szCombined.GetLenght());
}
HTTPResponse_t CHTTPClient::GetResponse()
{
CUtlResizableBuffer<char> szCharBuffer(0);
char response[4096];
int n;
int nPreviousSize = 0;
if (m_bIsSecure)
{
secureRead:
n = SSL_read(m_pSSL, response, sizeof(response));
szCharBuffer.Resize(nPreviousSize+n);
V_memcpy((char*)szCharBuffer.GetMemory()+nPreviousSize, response, n);
nPreviousSize += n;
if (SSL_pending(m_pSSL)>0)
goto secureRead;
}
else {
httpRead:
n = read(m_iFileDescriptor, response, sizeof(response));
V_printf("%i\n",n);
szCharBuffer.Resize(nPreviousSize+n+1);
szCharBuffer[nPreviousSize+n] = 0;
V_memcpy((char*)szCharBuffer.GetMemory()+nPreviousSize, response, n);
nPreviousSize += n;
goto httpRead;
}
szCharBuffer.Resize(n+1);
return ParseResponse(szCharBuffer);
}
HTTPResponse_t CHTTPClient::ParseResponse( const char *szMessage )
{
char cPreviousCharacter = 0;
char cCurrentCharacter = 0;
const char *pcCurrentCharacter = szMessage;
CUtlString szBuffer = "";
bool bIsMessage = true;
HTTPResponse_t response = {};
CUtlVector<HTTPHeaderParam_t> headers = {};
CUtlBuffer<char> data = {};
// Parse header
while (*pcCurrentCharacter)
{
cCurrentCharacter = *pcCurrentCharacter;
if ( cPreviousCharacter == '\r')
{
if ( cCurrentCharacter == '\n')
{
if (bIsMessage)
{
uint32_t uResult = 0;
V_sscanf(szBuffer, "HTTP/1.1 %i", &uResult);
response.m_uCode = uResult;
}
bIsMessage = false;
if (szBuffer == "")
break;
szBuffer = "";
cPreviousCharacter = 0;
cCurrentCharacter = *pcCurrentCharacter++;
continue;
}
}
if (cPreviousCharacter != 0)
szBuffer.AppendTail(cPreviousCharacter);
pcCurrentCharacter++;
cPreviousCharacter = cCurrentCharacter;
};
pcCurrentCharacter++;
uint32_t nDataLen = V_strlen(pcCurrentCharacter);
data = CUtlBuffer<char>(nDataLen+1);
V_strcpy(data.GetMemory(), pcCurrentCharacter);
data[nDataLen] = 0;
response.m_message = data;
return response;
}
const char *CHTTPClient::GetVersion()
{
return "1.1";
}
abstract_class CHTTPClientManager: public IHTTPClientManager
{
public:
CHTTPClientManager();
virtual IHTTPClient *Connect( const char *szUrl, bool bSecure ) override;
virtual void Disconnect( IHTTPClient *szConnection ) override;
};
IHTTPClient *CHTTPClientManager::Connect( const char *szUrl, bool bSecure )
{
struct hostent *pServerHostName = NULL;
struct sockaddr_in serverAddress;
int fd = 0;
int err;
CHTTPClient *pClient;
SSL_CTX *ctx;
SSL *ssl;
pServerHostName = gethostbyname(szUrl);
if (!pServerHostName)
return NULL;
fd = socket(AF_INET, SOCK_STREAM, 0);
if ( fd < 0 )
return NULL;
V_memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
V_memcpy(&serverAddress.sin_addr.s_addr, pServerHostName->h_addr, pServerHostName->h_length);
// https
if ( bSecure )
serverAddress.sin_port = htons(443);
else
serverAddress.sin_port = htons(80);
err = connect(fd, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
if (err < 0)
return NULL;
if (bSecure)
{
ctx = SSL_CTX_new(TLS_client_method());
if (!ctx)
return NULL;
ssl =SSL_new(ctx);
if (!ssl)
return NULL;
SSL_set_fd(ssl, fd);
SSL_set_tlsext_host_name(ssl, szUrl);
int r = SSL_connect(ssl);
if (r <= 0)
{
ERR_print_errors_fp(stdout);
SSL_free(ssl);
SSL_CTX_free(ctx);
return NULL;
}
};
pClient = new CHTTPClient();
pClient->m_iFileDescriptor = fd;
pClient->m_szHostName = szUrl;
if (bSecure)
{
pClient->m_bIsSecure = bSecure;
pClient->m_pSSL = ssl;
pClient->m_pSSLCtx = ctx;
}
return pClient;
}
void CHTTPClientManager::Disconnect( IHTTPClient *pClient )
{
CHTTPClient *pC = (CHTTPClient*)pClient;
close(pC->m_iFileDescriptor);
}
CHTTPClientManager::CHTTPClientManager()
{
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
CHTTPClientManager s_HttpClientManager;
EXPOSE_INTERFACE_GLOBALVAR(IHTTPClientManager, CHTTPClientManager, HTTP_CLIENT_INTERFACE_VERSION, s_HttpClientManager);

36
http/test.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "http/http.h"
#include "tier1/interface.h"
IHTTPClientManager *g_pHttpClientMgr;
int main()
{
Sys_GetFactory("tier0");
CreateInterfaceFn pHttpFactory = Sys_GetFactory("funnyhttp");
g_pHttpClientMgr = (IHTTPClientManager*)pHttpFactory(HTTP_CLIENT_INTERFACE_VERSION, NULL);
if ( !g_pHttpClientMgr )
return 0;
IHTTPClient *pClient = g_pHttpClientMgr->Connect("ani.sidestore.io", true);
printf("%p\n",pClient);
if ( pClient == NULL )
return 0;
HTTPHeaderParam_t params[] = {
{"User-Agent", "Funny"},
{"Accept", "application/json"},
};
HTTPHeader_t stHeader = {
sizeof(params)/sizeof(params[0]),
params
};
pClient->Get("/", &stHeader);
HTTPResponse_t stResponse = pClient->GetResponse();
if (stResponse.m_uCode == 200)
{
V_printf("%s\n", stResponse.m_message.GetMemory());
}
g_pHttpClientMgr->Disconnect(pClient);
return 0;
}