openmohaa/code/gamespy/ghttp/ghttpConnection.c

425 lines
10 KiB
C
Raw Normal View History

2023-02-04 21:00:01 +01:00
/*
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;
}