mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
424 lines
10 KiB
C
424 lines
10 KiB
C
/*
|
|
GameSpy GHTTP SDK
|
|
Dan "Mr. Pants" Schoenblum
|
|
dan@gamespy.com
|
|
|
|
Copyright 1999-2007 GameSpy Industries, Inc
|
|
|
|
devsupport@gamespy.com
|
|
*/
|
|
|
|
#include "ghttpConnection.h"
|
|
#include "ghttpCommon.h"
|
|
|
|
// Initial size and increment amount for the connections array.
|
|
///////////////////////////////////////////////////////////////
|
|
#define CONNECTIONS_CHUNK_LEN 4
|
|
|
|
// An array of pointers to GHIConnection objects.
|
|
// A GHTTPRequest is an index into this array.
|
|
/////////////////////////////////////////////////
|
|
static GHIConnection ** ghiConnections;
|
|
static int ghiConnectionsLen;
|
|
static int ghiNumConnections;
|
|
static int ghiNextUniqueID;
|
|
|
|
// Finds a gsifree slot in the ghiConnections array.
|
|
// If there are no gsifree slots, the array size will be increased.
|
|
////////////////////////////////////////////////////////////////
|
|
static int ghiFindFreeSlot
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int i;
|
|
GHIConnection ** tempPtr;
|
|
int oldLen;
|
|
int newLen;
|
|
|
|
// Look for an open slot.
|
|
/////////////////////////
|
|
for(i = 0 ; i < ghiConnectionsLen ; i++)
|
|
{
|
|
if(!ghiConnections[i]->inUse)
|
|
return i;
|
|
}
|
|
|
|
assert(ghiNumConnections == ghiConnectionsLen);
|
|
|
|
// Nothing found, resize the array.
|
|
///////////////////////////////////
|
|
oldLen = ghiConnectionsLen;
|
|
newLen = (ghiConnectionsLen + CONNECTIONS_CHUNK_LEN);
|
|
tempPtr = (GHIConnection **)gsirealloc(ghiConnections, sizeof(GHIConnection *) * newLen);
|
|
if(!tempPtr)
|
|
return -1;
|
|
ghiConnections = tempPtr;
|
|
|
|
// Create the new connection objects.
|
|
/////////////////////////////////////
|
|
for(i = oldLen ; i < newLen ; i++)
|
|
{
|
|
ghiConnections[i] = (GHIConnection *)gsimalloc(sizeof(GHIConnection));
|
|
if(!ghiConnections[i])
|
|
{
|
|
for(i-- ; i >= oldLen ; i--)
|
|
gsifree(ghiConnections[i]);
|
|
return -1;
|
|
}
|
|
ghiConnections[i]->inUse = GHTTPFalse;
|
|
}
|
|
|
|
// Update the length.
|
|
/////////////////////
|
|
ghiConnectionsLen = newLen;
|
|
|
|
return oldLen;
|
|
}
|
|
|
|
GHIConnection * ghiNewConnection
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int slot;
|
|
GHIConnection * connection;
|
|
GHTTPBool bResult;
|
|
|
|
ghiLock();
|
|
|
|
// Find a gsifree slot.
|
|
////////////////////
|
|
slot = ghiFindFreeSlot();
|
|
if(slot == -1)
|
|
{
|
|
ghiUnlock();
|
|
return NULL;
|
|
}
|
|
|
|
// Get a pointer to the object.
|
|
///////////////////////////////
|
|
connection = ghiConnections[slot];
|
|
|
|
// Init the object.
|
|
///////////////////
|
|
memset(connection, 0, sizeof(GHIConnection));
|
|
connection->inUse = GHTTPTrue;
|
|
connection->request = (GHTTPRequest)slot;
|
|
connection->uniqueID = ghiNextUniqueID++;
|
|
connection->type = GHIGET;
|
|
connection->state = GHTTPSocketInit;
|
|
connection->URL = NULL;
|
|
connection->serverAddress = NULL;
|
|
connection->serverIP = INADDR_ANY;
|
|
connection->serverPort = (unsigned short)0;
|
|
connection->requestPath = NULL;
|
|
connection->sendHeaders = NULL;
|
|
connection->saveFile = NULL;
|
|
connection->blocking = GHTTPFalse;
|
|
connection->persistConnection = GHTTPFalse;
|
|
connection->result = GHTTPSuccess;
|
|
connection->progressCallback = NULL;
|
|
connection->completedCallback = NULL;
|
|
connection->callbackParam = NULL;
|
|
connection->socket = INVALID_SOCKET;
|
|
connection->socketError = 0;
|
|
connection->userBufferSupplied = GHTTPFalse;
|
|
connection->statusMajorVersion = 0;
|
|
connection->statusMinorVersion = 0;
|
|
connection->statusCode = 0;
|
|
connection->statusStringIndex = 0;
|
|
connection->headerStringIndex = 0;
|
|
connection->completed = GHTTPFalse;
|
|
connection->fileBytesReceived = 0;
|
|
connection->totalSize = -1;
|
|
connection->redirectURL = NULL;
|
|
connection->redirectCount = 0;
|
|
connection->chunkedTransfer = GHTTPFalse;
|
|
connection->processing = GHTTPFalse;
|
|
connection->throttle = GHTTPFalse;
|
|
connection->lastThrottleRecv = 0;
|
|
connection->post = NULL;
|
|
connection->maxRecvTime = 500; // Prevent blocking in async mode with systems that never generate WSAEWOULDBLOCK
|
|
connection->proxyOverridePort = GHI_DEFAULT_PORT;
|
|
connection->proxyOverrideServer = NULL;
|
|
connection->encryptor.mInterface = NULL;
|
|
|
|
//handle used for asynch DNS lookups
|
|
#if !defined(GSI_NO_THREADS)
|
|
connection->handle = NULL;
|
|
#endif
|
|
|
|
bResult = ghiInitBuffer(connection, &connection->sendBuffer, SEND_BUFFER_INITIAL_SIZE, SEND_BUFFER_INCREMENT_SIZE);
|
|
if(bResult)
|
|
bResult = ghiInitBuffer(connection, &connection->encodeBuffer, ENCODE_BUFFER_INITIAL_SIZE, ENCODE_BUFFER_INCREMENT_SIZE);
|
|
if(bResult)
|
|
bResult = ghiInitBuffer(connection, &connection->recvBuffer, RECV_BUFFER_INITIAL_SIZE, RECV_BUFFER_INCREMENT_SIZE);
|
|
if (bResult)
|
|
bResult = ghiInitBuffer(connection, &connection->decodeBuffer, DECODE_BUFFER_INITIAL_SIZE, DECODE_BUFFER_INCREMENT_SIZE);
|
|
|
|
if(!bResult)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
ghiUnlock();
|
|
return NULL;
|
|
}
|
|
|
|
// One more connection.
|
|
///////////////////////
|
|
ghiNumConnections++;
|
|
|
|
ghiUnlock();
|
|
|
|
return connection;
|
|
}
|
|
|
|
GHTTPBool ghiFreeConnection
|
|
(
|
|
GHIConnection * connection
|
|
)
|
|
{
|
|
assert(connection);
|
|
assert(connection->request >= 0);
|
|
assert(connection->request < ghiConnectionsLen);
|
|
assert(connection->inUse);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!connection)
|
|
return GHTTPFalse;
|
|
if(!connection->inUse)
|
|
return GHTTPFalse;
|
|
if(connection->request < 0)
|
|
return GHTTPFalse;
|
|
if(connection->request >= ghiConnectionsLen)
|
|
return GHTTPFalse;
|
|
|
|
ghiLock();
|
|
|
|
// Free data.
|
|
/////////////
|
|
gsifree(connection->URL);
|
|
gsifree(connection->serverAddress);
|
|
gsifree(connection->requestPath);
|
|
gsifree(connection->sendHeaders);
|
|
gsifree(connection->redirectURL);
|
|
gsifree(connection->proxyOverrideServer);
|
|
#ifndef NOFILE
|
|
if(connection->saveFile)
|
|
fclose(connection->saveFile);
|
|
#endif
|
|
if(connection->socket != INVALID_SOCKET)
|
|
{
|
|
shutdown(connection->socket, 2);
|
|
closesocket(connection->socket);
|
|
}
|
|
ghiFreeBuffer(&connection->sendBuffer);
|
|
ghiFreeBuffer(&connection->encodeBuffer);
|
|
ghiFreeBuffer(&connection->recvBuffer);
|
|
ghiFreeBuffer(&connection->decodeBuffer);
|
|
ghiFreeBuffer(&connection->getFileBuffer);
|
|
if(connection->postingState.states)
|
|
ghiPostCleanupState(connection);
|
|
|
|
#if !defined(GSI_NO_THREADS)
|
|
// Cancel and free asychronous lookup if it has not already been done
|
|
/////////////////////////////////////////////////////////////////////
|
|
if (connection->handle)
|
|
{
|
|
gsDebugFormat(GSIDebugCat_HTTP, GSIDebugType_State, GSIDebugLevel_Comment,
|
|
"Cancelling Thread and freeing memory\n");
|
|
gsiCancelResolvingHostname(connection->handle);
|
|
}
|
|
#endif
|
|
|
|
// Check for an auto-free post.
|
|
///////////////////////////////
|
|
if(connection->post && ghiIsPostAutoFree(connection->post))
|
|
{
|
|
ghiFreePost(connection->post);
|
|
connection->post = NULL;
|
|
}
|
|
|
|
// Check for an encryptor
|
|
if (connection->encryptor.mInitialized != GHTTPFalse)
|
|
{
|
|
if (connection->encryptor.mCleanupFunc)
|
|
(connection->encryptor.mCleanupFunc)(connection, &connection->encryptor);
|
|
connection->encryptor.mInitialized = GHTTPFalse;
|
|
}
|
|
|
|
// Free the slot.
|
|
/////////////////
|
|
connection->inUse = GHTTPFalse;
|
|
|
|
// One less connection.
|
|
///////////////////////
|
|
ghiNumConnections--;
|
|
|
|
ghiUnlock();
|
|
|
|
return GHTTPTrue;
|
|
}
|
|
|
|
GHIConnection * ghiRequestToConnection
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
assert(request >= 0);
|
|
assert(request < ghiConnectionsLen);
|
|
|
|
ghiLock();
|
|
|
|
// Check args.
|
|
//////////////
|
|
if((request < 0) || (request >= ghiConnectionsLen))
|
|
{
|
|
ghiUnlock();
|
|
return NULL;
|
|
}
|
|
|
|
connection = ghiConnections[request];
|
|
|
|
// Check for not in use.
|
|
////////////////////////
|
|
if(!connection->inUse)
|
|
connection = NULL;
|
|
|
|
ghiUnlock();
|
|
|
|
return connection;
|
|
}
|
|
|
|
void ghiEnumConnections
|
|
(
|
|
GHTTPBool (* callback)(GHIConnection *)
|
|
)
|
|
{
|
|
int i;
|
|
|
|
// Check for no connections.
|
|
////////////////////////////
|
|
if(ghiNumConnections <= 0)
|
|
return;
|
|
|
|
ghiLock();
|
|
for(i = 0 ; i < ghiConnectionsLen ; i++)
|
|
if(ghiConnections[i]->inUse)
|
|
callback(ghiConnections[i]);
|
|
ghiUnlock();
|
|
}
|
|
|
|
void ghiRedirectConnection
|
|
(
|
|
GHIConnection * connection
|
|
)
|
|
{
|
|
assert(connection);
|
|
assert(connection->redirectURL);
|
|
|
|
gsDebugFormat(GSIDebugCat_HTTP, GSIDebugType_State, GSIDebugLevel_Comment, "Redirecting Connection\n");
|
|
|
|
// Reset state.
|
|
///////////////
|
|
connection->state = GHTTPSocketInit;
|
|
|
|
#if !defined(GSI_NO_THREADS)
|
|
// Cancel and free asychronous lookup if it has not already been done
|
|
/////////////////////////////////////////////////////////////////////
|
|
if (connection->handle)
|
|
{
|
|
gsDebugFormat(GSIDebugCat_HTTP, GSIDebugType_State, GSIDebugLevel_Comment,
|
|
"Cancelling Thread and freeing memory\n");
|
|
gsiCancelResolvingHostname(connection->handle);
|
|
}
|
|
#endif
|
|
|
|
// New URL.
|
|
///////////
|
|
gsifree(connection->URL);
|
|
connection->URL = connection->redirectURL;
|
|
connection->redirectURL = NULL;
|
|
|
|
// Reset stuff parsed from the URL.
|
|
///////////////////////////////////
|
|
gsifree(connection->serverAddress);
|
|
connection->serverAddress = NULL;
|
|
connection->serverIP = 0;
|
|
connection->serverPort = 0;
|
|
gsifree(connection->requestPath);
|
|
connection->requestPath = NULL;
|
|
|
|
// Close the socket.
|
|
////////////////////
|
|
shutdown(connection->socket, 2);
|
|
closesocket(connection->socket);
|
|
connection->socket = INVALID_SOCKET;
|
|
|
|
// Reset buffers.
|
|
/////////////////
|
|
ghiResetBuffer(&connection->sendBuffer);
|
|
ghiResetBuffer(&connection->encodeBuffer);
|
|
ghiResetBuffer(&connection->recvBuffer);
|
|
ghiResetBuffer(&connection->decodeBuffer);
|
|
|
|
// Reset status.
|
|
////////////////
|
|
connection->statusMajorVersion = 0;
|
|
connection->statusMinorVersion = 0;
|
|
connection->statusCode = 0;
|
|
connection->statusStringIndex = 0;
|
|
|
|
connection->headerStringIndex = 0;
|
|
|
|
// The connection isn't closed.
|
|
///////////////////////////////
|
|
connection->connectionClosed = GHTTPFalse;
|
|
|
|
// Check for an encryptor
|
|
if (connection->encryptor.mInitialized != GHTTPFalse)
|
|
{
|
|
// cleanup the encryptor
|
|
if (connection->encryptor.mCleanupFunc)
|
|
(connection->encryptor.mCleanupFunc)(connection, &connection->encryptor);
|
|
connection->encryptor.mInitialized = GHTTPFalse;
|
|
|
|
// if the redirect isn't secure, clear it
|
|
if(strncmp("https://", connection->URL, 8) != 0)
|
|
{
|
|
connection->encryptor.mEngine = GHTTPEncryptionEngine_None;
|
|
connection->encryptor.mInterface = NULL;
|
|
}
|
|
}
|
|
|
|
// One more redirect.
|
|
/////////////////////
|
|
connection->redirectCount++;
|
|
}
|
|
|
|
void ghiCleanupConnections
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int i;
|
|
|
|
if(!ghiConnections)
|
|
return;
|
|
|
|
// Cleanup all running connections.
|
|
///////////////////////////////////
|
|
ghiEnumConnections(ghiFreeConnection);
|
|
|
|
// Cleanup the connection states.
|
|
/////////////////////////////////
|
|
for(i = 0 ; i < ghiConnectionsLen ; i++)
|
|
gsifree(ghiConnections[i]);
|
|
gsifree(ghiConnections);
|
|
ghiConnections = NULL;
|
|
ghiConnectionsLen = 0;
|
|
ghiNumConnections = 0;
|
|
}
|