mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-29 22:27:57 +03:00
1485 lines
33 KiB
C
1485 lines
33 KiB
C
/*
|
|
GameSpy GHTTP SDK
|
|
Dan "Mr. Pants" Schoenblum
|
|
dan@gamespy.com
|
|
|
|
Copyright 1999-2001 GameSpy Industries, Inc
|
|
|
|
18002 Skypark Circle
|
|
Irvine, California 92614
|
|
949.798.4200 (Tel)
|
|
949.798.4299 (Fax)
|
|
devsupport@gamespy.com
|
|
*/
|
|
|
|
#include "ghttpMain.h"
|
|
#include "ghttpASCII.h"
|
|
#include "ghttpConnection.h"
|
|
#include "ghttpCallbacks.h"
|
|
#include "ghttpProcess.h"
|
|
#include "ghttpPost.h"
|
|
#include "ghttpCommon.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Ascii versions which must be available even in the unicode build
|
|
GHTTPRequest ghttpGetExA(const char * URL, const char * headers, char * buffer, int bufferSize, GHTTPPost post, GHTTPBool throttle, GHTTPBool blocking, ghttpProgressCallback progressCallback, ghttpCompletedCallback completedCallback, void * param);
|
|
GHTTPRequest ghttpSaveExA(const char * URL, const char * filename, const char * headers, GHTTPPost post, GHTTPBool throttle, GHTTPBool blocking, ghttpProgressCallback progressCallback, ghttpCompletedCallback completedCallback, void * param);
|
|
GHTTPRequest ghttpStreamExA(const char * URL, const char * headers, GHTTPPost post, GHTTPBool throttle, GHTTPBool blocking, ghttpProgressCallback progressCallback, ghttpCompletedCallback completedCallback, void * param);
|
|
GHTTPRequest ghttpHeadExA(const char * URL, const char * headers, GHTTPBool throttle, GHTTPBool blocking, ghttpProgressCallback progressCallback, ghttpCompletedCallback completedCallback, void * param);
|
|
GHTTPRequest ghttpPostExA(const char * URL, const char * headers, GHTTPPost post, GHTTPBool throttle, GHTTPBool blocking, ghttpProgressCallback progressCallback, ghttpCompletedCallback completedCallback, void * param);
|
|
|
|
|
|
|
|
// Reference count.
|
|
///////////////////
|
|
static int ghiReferenceCount;
|
|
|
|
// Called right before callback is called.
|
|
// Sets result based on response status code.
|
|
/////////////////////////////////////////////
|
|
static void ghiHandleStatus
|
|
(
|
|
GHIConnection * connection
|
|
)
|
|
{
|
|
// Check the status code.
|
|
/////////////////////////
|
|
switch(connection->statusCode / 100)
|
|
{
|
|
case 1: // Informational.
|
|
return;
|
|
case 2: // Successful.
|
|
return;
|
|
case 3: // Redirection.
|
|
return;
|
|
case 4: // Client Error.
|
|
switch(connection->statusCode)
|
|
{
|
|
case 401:
|
|
connection->result = GHTTPUnauthorized;
|
|
break;
|
|
case 403:
|
|
connection->result = GHTTPForbidden;
|
|
break;
|
|
case 404:
|
|
case 410:
|
|
connection->result = GHTTPFileNotFound;
|
|
break;
|
|
default:
|
|
connection->result = GHTTPRequestRejected;
|
|
break;
|
|
}
|
|
return;
|
|
case 5: // Internal Server Error.
|
|
connection->result = GHTTPServerError;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Processes a single connection based on its state.
|
|
// Returns true if the connection is finished.
|
|
////////////////////////////////////////////////////
|
|
static GHTTPBool ghiProcessConnection
|
|
(
|
|
GHIConnection * connection
|
|
)
|
|
{
|
|
GHTTPBool completed;
|
|
|
|
assert(connection);
|
|
assert(ghiRequestToConnection(connection->request) == connection);
|
|
|
|
// Don't process if already processing this connection.
|
|
// Happens if, for example, ghttpThink is called from a callback.
|
|
/////////////////////////////////////////////////////////////////
|
|
if(connection->processing)
|
|
return GHTTPFalse;
|
|
|
|
// We're now processing.
|
|
////////////////////////
|
|
connection->processing = GHTTPTrue;
|
|
|
|
// Process based on state.
|
|
// else-if is not used so that if one ghiDo*()
|
|
// finishes the one after it can start.
|
|
//////////////////////////////////////////////
|
|
|
|
if(connection->state == GHTTPSocketInit)
|
|
ghiDoSocketInit(connection);
|
|
if(connection->state == GHTTPHostLookup)
|
|
ghiDoHostLookup(connection);
|
|
if(connection->state == GHTTPLookupPending)
|
|
ghiDoLookupPending(connection);
|
|
if(connection->state == GHTTPConnecting)
|
|
ghiDoConnecting(connection);
|
|
if(connection->state == GHTTPSecuringSession)
|
|
ghiDoSecuringSession(connection);
|
|
if(connection->state == GHTTPSendingRequest)
|
|
ghiDoSendingRequest(connection);
|
|
if(connection->state == GHTTPPosting)
|
|
ghiDoPosting(connection);
|
|
if(connection->state == GHTTPWaiting)
|
|
ghiDoWaiting(connection);
|
|
if(connection->state == GHTTPReceivingStatus)
|
|
ghiDoReceivingStatus(connection);
|
|
if(connection->state == GHTTPReceivingHeaders)
|
|
ghiDoReceivingHeaders(connection);
|
|
if(connection->state == GHTTPReceivingFile)
|
|
ghiDoReceivingFile(connection);
|
|
|
|
// Check for a redirect.
|
|
////////////////////////
|
|
if(connection->redirectURL)
|
|
ghiRedirectConnection(connection);
|
|
|
|
// Grab completed before we possibly free it.
|
|
/////////////////////////////////////////////
|
|
completed = connection->completed;
|
|
|
|
// Graceful shutdown support.
|
|
// Close connection when there is no more data
|
|
if (connection->result == GHTTPRequestCancelled && !connection->completed && !CanReceiveOnSocket(connection->socket))
|
|
{
|
|
connection->completed = GHTTPTrue;
|
|
}
|
|
|
|
// Is it finished?
|
|
//////////////////
|
|
if(connection->completed)
|
|
{
|
|
// Set result based on status code.
|
|
///////////////////////////////////
|
|
ghiHandleStatus(connection);
|
|
|
|
// If we're saving to file, close it before the callback.
|
|
/////////////////////////////////////////////////////////
|
|
#ifndef NOFILE
|
|
if(connection->saveFile)
|
|
{
|
|
fclose(connection->saveFile);
|
|
connection->saveFile = NULL;
|
|
}
|
|
#endif
|
|
// Log buffer data
|
|
ghiLogResponse(connection->getFileBuffer.data, connection->getFileBuffer.len);
|
|
|
|
// Call the callback.
|
|
/////////////////////
|
|
ghiCallCompletedCallback(connection);
|
|
|
|
// Free it.
|
|
///////////
|
|
ghiFreeConnection(connection);
|
|
}
|
|
else
|
|
{
|
|
// Done processing. This is in the else,
|
|
// because we don't want to set it if the
|
|
// connection has already been freed.
|
|
/////////////////////////////////////////
|
|
connection->processing = GHTTPFalse;
|
|
}
|
|
|
|
return completed;
|
|
}
|
|
|
|
void ghttpStartup
|
|
(
|
|
void
|
|
)
|
|
{
|
|
// This will just return if we haven't created the lock yet.
|
|
////////////////////////////////////////////////////////////
|
|
ghiLock();
|
|
|
|
// One more startup.
|
|
////////////////////
|
|
ghiReferenceCount++;
|
|
|
|
// Check if we are the first.
|
|
/////////////////////////////
|
|
if(ghiReferenceCount == 1)
|
|
{
|
|
// Create the lock.
|
|
///////////////////
|
|
ghiCreateLock();
|
|
|
|
// Set some defaults.
|
|
/////////////////////
|
|
ghiThrottleBufferSize = GHI_DEFAULT_THROTTLE_BUFFER_SIZE;
|
|
ghiThrottleTimeDelay = GHI_DEFAULT_THROTTLE_TIME_DELAY;
|
|
}
|
|
else
|
|
{
|
|
// Unlock the lock.
|
|
///////////////////
|
|
ghiUnlock();
|
|
}
|
|
}
|
|
|
|
void ghttpCleanup
|
|
(
|
|
void
|
|
)
|
|
{
|
|
// Lockdown for cleanup.
|
|
////////////////////////
|
|
ghiLock();
|
|
|
|
// One less.
|
|
////////////
|
|
ghiReferenceCount--;
|
|
|
|
// Should we cleanup?
|
|
/////////////////////
|
|
if(!ghiReferenceCount)
|
|
{
|
|
// Cleanup the connections.
|
|
///////////////////////////
|
|
ghiCleanupConnections();
|
|
|
|
// Cleanup proxy.
|
|
/////////////////
|
|
if(ghiProxyAddress)
|
|
{
|
|
gsifree(ghiProxyAddress);
|
|
ghiProxyAddress = NULL;
|
|
}
|
|
|
|
// Unlock the lock before freeing it.
|
|
/////////////////////////////////////
|
|
ghiUnlock();
|
|
|
|
// Free the lock.
|
|
/////////////////
|
|
ghiFreeLock();
|
|
}
|
|
else
|
|
{
|
|
// Unlock our lock.
|
|
///////////////////
|
|
ghiUnlock();
|
|
}
|
|
}
|
|
|
|
GHTTPRequest ghttpGetA
|
|
(
|
|
const char * URL,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
return ghttpGetExA(URL, NULL, NULL, 0, NULL, GHTTPFalse, blocking, NULL, completedCallback, param);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpGetW
|
|
(
|
|
const unsigned short * URL,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024];
|
|
|
|
assert(URL != NULL);
|
|
UCS2ToAsciiString(URL, (char*)URL_A);
|
|
return ghttpGetA(URL_A, blocking, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpGetExA
|
|
(
|
|
const char * URL,
|
|
const char * headers,
|
|
char * buffer,
|
|
int bufferSize,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
GHTTPBool bResult;
|
|
GHIConnection * connection;
|
|
|
|
assert(URL && URL[0]);
|
|
assert(bufferSize >= 0);
|
|
assert(!buffer || bufferSize);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!URL || !URL[0])
|
|
return GHTTPInvalidURL;
|
|
if(bufferSize < 0)
|
|
return GHTTPInvalidBufferSize;
|
|
if(buffer && !bufferSize)
|
|
return GHTTPInvalidBufferSize;
|
|
|
|
// Startup if it hasn't been done.
|
|
//////////////////////////////////
|
|
if(!ghiReferenceCount)
|
|
ghttpStartup();
|
|
|
|
// Get a new connection object.
|
|
///////////////////////////////
|
|
connection = ghiNewConnection();
|
|
if(!connection)
|
|
return GHTTPInsufficientMemory;
|
|
|
|
// Fill in the necessary info.
|
|
//////////////////////////////
|
|
connection->type = GHIGET;
|
|
connection->URL = goastrdup(URL);
|
|
if(!connection->URL)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
if(headers && *headers)
|
|
{
|
|
connection->sendHeaders = goastrdup(headers);
|
|
if(!connection->sendHeaders)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
}
|
|
connection->post = post;
|
|
connection->blocking = blocking;
|
|
connection->progressCallback = progressCallback;
|
|
connection->completedCallback = completedCallback;
|
|
connection->callbackParam = param;
|
|
connection->throttle = throttle;
|
|
connection->userBufferSupplied = (buffer != NULL)?GHTTPTrue:GHTTPFalse;
|
|
if(connection->userBufferSupplied)
|
|
bResult = ghiInitFixedBuffer(connection, &connection->getFileBuffer, buffer, bufferSize);
|
|
else
|
|
bResult = ghiInitBuffer(connection, &connection->getFileBuffer, GET_FILE_BUFFER_INITIAL_SIZE, GET_FILE_BUFFER_INCREMENT_SIZE);
|
|
if(!bResult)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPUnspecifiedError;
|
|
}
|
|
|
|
// Setup the post state if needed.
|
|
//////////////////////////////////
|
|
if(post && !ghiPostInitState(connection))
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInvalidPost;
|
|
}
|
|
|
|
// Check blocking.
|
|
//////////////////
|
|
if(blocking)
|
|
{
|
|
// Loop until completed.
|
|
////////////////////////
|
|
while(!ghiProcessConnection(connection))
|
|
msleep(10);
|
|
|
|
// Done.
|
|
////////
|
|
return 0;
|
|
}
|
|
|
|
return connection->request;
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpGetExW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * headers,
|
|
char * buffer,
|
|
int bufferSize,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024];
|
|
char headers_A[1024] = { '\0' };
|
|
|
|
assert(URL != NULL);
|
|
UCS2ToAsciiString(URL, (char*)URL_A);
|
|
if (headers != NULL)
|
|
UCS2ToAsciiString(headers, headers_A);
|
|
return ghttpGetExA((char*)URL_A, (char*)headers_A, buffer, bufferSize, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpSaveA
|
|
(
|
|
const char * URL,
|
|
const char * filename,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
return ghttpSaveExA(URL, filename, NULL, NULL, GHTTPFalse, blocking, NULL, completedCallback, param);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpSaveW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * filename,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = { '\0' };
|
|
char filename_A[1024] = { '\0' };
|
|
|
|
assert(URL != NULL);
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
UCS2ToAsciiString(filename, filename_A);
|
|
return ghttpSaveA(URL_A, filename_A, blocking, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
static GHTTPRequest _ghttpSaveEx
|
|
(
|
|
const char * URL,
|
|
const gsi_char * filename,
|
|
const char * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
assert(URL && URL[0]);
|
|
assert(filename && filename[0]);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!URL || !URL[0])
|
|
return GHTTPInvalidURL;
|
|
if(!filename || !filename[0])
|
|
return GHTTPInvalidFileName;
|
|
|
|
// Startup if it hasn't been done.
|
|
//////////////////////////////////
|
|
if(!ghiReferenceCount)
|
|
ghttpStartup();
|
|
|
|
// Get a new connection object.
|
|
///////////////////////////////
|
|
connection = ghiNewConnection();
|
|
if(!connection)
|
|
return GHTTPInsufficientMemory;
|
|
|
|
// Fill in the necessary info.
|
|
//////////////////////////////
|
|
connection->type = GHISAVE;
|
|
connection->URL = goastrdup(URL);
|
|
if(!connection->URL)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
if(headers && *headers)
|
|
{
|
|
connection->sendHeaders = goastrdup(headers);
|
|
if(!connection->sendHeaders)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
}
|
|
connection->post = post;
|
|
connection->blocking = blocking;
|
|
connection->progressCallback = progressCallback;
|
|
connection->completedCallback = completedCallback;
|
|
connection->callbackParam = param;
|
|
connection->throttle = throttle;
|
|
|
|
// Setup the post state if needed.
|
|
//////////////////////////////////
|
|
if(post && !ghiPostInitState(connection))
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInvalidPost;
|
|
}
|
|
|
|
// Open the file we're saving to.
|
|
/////////////////////////////////
|
|
#ifdef NOFILE
|
|
connection->saveFile = NULL;
|
|
#else
|
|
connection->saveFile = _tfopen(filename, _T("wb"));
|
|
#endif
|
|
if(!connection->saveFile)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPFailedToOpenFile;
|
|
}
|
|
|
|
// Check blocking.
|
|
//////////////////
|
|
if(blocking)
|
|
{
|
|
// Loop until completed.
|
|
////////////////////////
|
|
while(!ghiProcessConnection(connection))
|
|
msleep(10);
|
|
|
|
// Done.
|
|
////////
|
|
return 0;
|
|
}
|
|
|
|
return connection->request;
|
|
}
|
|
|
|
GHTTPRequest ghttpSaveExA
|
|
(
|
|
const char * URL,
|
|
const char * filename,
|
|
const char * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
#ifdef GSI_UNICODE
|
|
unsigned short filename_W[1024];
|
|
AsciiToUCS2String(filename, filename_W);
|
|
return _ghttpSaveEx(URL, filename_W, headers, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
#else
|
|
return _ghttpSaveEx(URL, filename, headers, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
#endif
|
|
}
|
|
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpSaveExW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * filename,
|
|
const unsigned short * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024];
|
|
//char filename_A[1024] = { '\0' };
|
|
char headers_A[1024] = { '\0' };
|
|
|
|
assert(URL_A != NULL);
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
//if (filename != NULL)
|
|
// UCS2ToAsciiString(filename, filename_A);
|
|
if (headers != NULL)
|
|
UCS2ToAsciiString(headers, headers_A);
|
|
|
|
return _ghttpSaveEx(URL_A, filename, headers_A, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpStreamA
|
|
(
|
|
const char * URL,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
return ghttpStreamExA(URL, NULL, NULL, GHTTPFalse, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpStreamW
|
|
(
|
|
const unsigned short * URL,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char* URL_A = { '\0' };
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
return ghttpStreamA(URL_A, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpStreamExA
|
|
(
|
|
const char * URL,
|
|
const char * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
assert(URL && URL[0]);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!URL || !URL[0])
|
|
return GHTTPInvalidURL;
|
|
|
|
// Startup if it hasn't been done.
|
|
//////////////////////////////////
|
|
if(!ghiReferenceCount)
|
|
ghttpStartup();
|
|
|
|
// Get a new connection object.
|
|
///////////////////////////////
|
|
connection = ghiNewConnection();
|
|
if(!connection)
|
|
return GHTTPInsufficientMemory;
|
|
|
|
// Fill in the necessary info.
|
|
//////////////////////////////
|
|
connection->type = GHISTREAM;
|
|
connection->URL = goastrdup(URL);
|
|
if(!connection->URL)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
if(headers && *headers)
|
|
{
|
|
connection->sendHeaders = goastrdup(headers);
|
|
if(!connection->sendHeaders)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
}
|
|
connection->post = post;
|
|
connection->blocking = blocking;
|
|
connection->progressCallback = progressCallback;
|
|
connection->completedCallback = completedCallback;
|
|
connection->callbackParam = param;
|
|
connection->throttle = throttle;
|
|
|
|
// Setup the post state if needed.
|
|
//////////////////////////////////
|
|
if(post && !ghiPostInitState(connection))
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInvalidPost;
|
|
}
|
|
|
|
// Check blocking.
|
|
//////////////////
|
|
if(blocking)
|
|
{
|
|
// Loop until completed.
|
|
////////////////////////
|
|
while(!ghiProcessConnection(connection))
|
|
msleep(10);
|
|
|
|
// Done.
|
|
////////
|
|
return 0;
|
|
}
|
|
|
|
return connection->request;
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpStreamExW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = {'\0'};
|
|
char headers_A[1024] = {'\0'};
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
if(headers != NULL)
|
|
UCS2ToAsciiString(headers, headers_A);
|
|
return ghttpStreamExA(URL_A, headers_A, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpHeadA
|
|
(
|
|
const char * URL,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
return ghttpHeadExA(URL, NULL, GHTTPFalse, blocking, NULL, completedCallback, param);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpHeadW
|
|
(
|
|
const unsigned short * URL,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = {'\0'};
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
return ghttpHeadA(URL_A, blocking, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpHeadExA
|
|
(
|
|
const char * URL,
|
|
const char * headers,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
assert(URL && URL[0]);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!URL || !URL[0])
|
|
return GHTTPInvalidURL;
|
|
|
|
// Startup if it hasn't been done.
|
|
//////////////////////////////////
|
|
if(!ghiReferenceCount)
|
|
ghttpStartup();
|
|
|
|
// Get a new connection object.
|
|
///////////////////////////////
|
|
connection = ghiNewConnection();
|
|
if(!connection)
|
|
return GHTTPInsufficientMemory;
|
|
|
|
// Fill in the necessary info.
|
|
//////////////////////////////
|
|
connection->type = GHIHEAD;
|
|
connection->URL = goastrdup(URL);
|
|
if(!connection->URL)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
if(headers && *headers)
|
|
{
|
|
connection->sendHeaders = goastrdup(headers);
|
|
if(!connection->sendHeaders)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
}
|
|
connection->blocking = blocking;
|
|
connection->progressCallback = progressCallback;
|
|
connection->completedCallback = completedCallback;
|
|
connection->callbackParam = param;
|
|
connection->throttle = throttle;
|
|
|
|
// Check blocking.
|
|
//////////////////
|
|
if(blocking)
|
|
{
|
|
// Loop until completed.
|
|
////////////////////////
|
|
while(!ghiProcessConnection(connection))
|
|
msleep(10);
|
|
|
|
// Done.
|
|
////////
|
|
return 0;
|
|
}
|
|
|
|
return connection->request;
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpHeadExW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * headers,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = {'\0'};
|
|
char headers_A[1024] = {'\0'};
|
|
if (URL != NULL)
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
if (headers != NULL)
|
|
UCS2ToAsciiString(headers, headers_A);
|
|
return ghttpHeadExA(URL_A, headers_A, throttle, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpPostA
|
|
(
|
|
const char * URL,
|
|
GHTTPPost post,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
return ghttpPostExA(URL, NULL, post, GHTTPFalse, blocking, NULL, completedCallback, param);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpPostW
|
|
(
|
|
const unsigned short * URL,
|
|
GHTTPPost post,
|
|
GHTTPBool blocking,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = {'\0'};
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
return ghttpPostA(URL_A, post, blocking, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
GHTTPRequest ghttpPostExA
|
|
(
|
|
const char * URL,
|
|
const char * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
assert(URL && URL[0]);
|
|
assert(post);
|
|
|
|
// Check args.
|
|
//////////////
|
|
if(!URL || !URL[0])
|
|
return GHTTPInvalidURL;
|
|
if(!post)
|
|
return GHTTPInvalidPost;
|
|
|
|
// Startup if it hasn't been done.
|
|
//////////////////////////////////
|
|
if(!ghiReferenceCount)
|
|
ghttpStartup();
|
|
|
|
// Get a new connection object.
|
|
///////////////////////////////
|
|
connection = ghiNewConnection();
|
|
if(!connection)
|
|
return GHTTPInsufficientMemory;
|
|
|
|
// Fill in the necessary info.
|
|
//////////////////////////////
|
|
connection->type = GHIPOST;
|
|
connection->URL = goastrdup(URL);
|
|
if(!connection->URL)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
if(headers && *headers)
|
|
{
|
|
connection->sendHeaders = goastrdup(headers);
|
|
if(!connection->sendHeaders)
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInsufficientMemory;
|
|
}
|
|
}
|
|
connection->post = post;
|
|
connection->blocking = blocking;
|
|
connection->progressCallback = progressCallback;
|
|
connection->completedCallback = completedCallback;
|
|
connection->callbackParam = param;
|
|
connection->throttle = throttle;
|
|
|
|
// Setup the post state if needed.
|
|
//////////////////////////////////
|
|
if(post && !ghiPostInitState(connection))
|
|
{
|
|
ghiFreeConnection(connection);
|
|
return GHTTPInvalidPost;
|
|
}
|
|
|
|
// Check blocking.
|
|
//////////////////
|
|
if(blocking)
|
|
{
|
|
// Loop until completed.
|
|
////////////////////////
|
|
while(!ghiProcessConnection(connection))
|
|
msleep(10);
|
|
|
|
// Done.
|
|
////////
|
|
return 0;
|
|
}
|
|
|
|
return connection->request;
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPRequest ghttpPostExW
|
|
(
|
|
const unsigned short * URL,
|
|
const unsigned short * headers,
|
|
GHTTPPost post,
|
|
GHTTPBool throttle,
|
|
GHTTPBool blocking,
|
|
ghttpProgressCallback progressCallback,
|
|
ghttpCompletedCallback completedCallback,
|
|
void * param
|
|
)
|
|
{
|
|
char URL_A[1024] = {'\0'};
|
|
char headers_A[1024] = {'\0'};
|
|
UCS2ToAsciiString(URL, URL_A);
|
|
if (headers != NULL)
|
|
UCS2ToAsciiString(headers, headers_A);
|
|
return ghttpPostExA(URL_A, headers_A, post, throttle, blocking, progressCallback, completedCallback, param);
|
|
}
|
|
#endif
|
|
|
|
void ghttpThink
|
|
(
|
|
void
|
|
)
|
|
{
|
|
// Process all the connections.
|
|
///////////////////////////////
|
|
ghiEnumConnections(ghiProcessConnection);
|
|
}
|
|
|
|
GHTTPBool ghttpRequestThink
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return GHTTPFalse;
|
|
|
|
// Think.
|
|
/////////
|
|
ghiProcessConnection(connection);
|
|
return GHTTPTrue;
|
|
}
|
|
|
|
void ghttpCancelRequest
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return;
|
|
|
|
// Free it.
|
|
///////////
|
|
ghiFreeConnection(connection);
|
|
}
|
|
|
|
#if !defined(INSOCK)
|
|
// INSOCK does not support partial shutdown
|
|
void ghttpCloseRequest
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
connection = ghiRequestToConnection(request);
|
|
if (!connection)
|
|
return;
|
|
|
|
if (connection->socket)
|
|
{
|
|
// Gracefully close the connection
|
|
// SDK will dispatch a "request cancelled" callback when all data
|
|
// has been received
|
|
shutdown(connection->socket, 1);
|
|
connection->result = GHTTPRequestCancelled;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GHTTPState ghttpGetState
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return (GHTTPState)0;
|
|
|
|
return connection->state;
|
|
}
|
|
|
|
const char * ghttpGetResponseStatus
|
|
(
|
|
GHTTPRequest request,
|
|
int * statusCode
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return NULL;
|
|
|
|
// Check if we don't have the status yet.
|
|
/////////////////////////////////////////
|
|
if(connection->state <= GHTTPReceivingStatus)
|
|
return NULL;
|
|
|
|
// Set the status code.
|
|
///////////////////////
|
|
if(statusCode)
|
|
*statusCode = connection->statusCode;
|
|
|
|
return (connection->recvBuffer.data + connection->statusStringIndex);
|
|
}
|
|
|
|
const char * ghttpGetHeaders
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return NULL;
|
|
|
|
// Check if we don't have the headers yet.
|
|
//////////////////////////////////////////
|
|
if(connection->state < GHTTPReceivingHeaders)
|
|
return NULL;
|
|
|
|
// Verify we have headers.
|
|
//////////////////////////
|
|
if(connection->headerStringIndex >= connection->recvBuffer.len)
|
|
return NULL;
|
|
|
|
return (connection->recvBuffer.data + connection->headerStringIndex);
|
|
}
|
|
|
|
const char * ghttpGetURL
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return NULL;
|
|
|
|
return connection->URL;
|
|
}
|
|
|
|
GHTTPBool ghttpSetProxy
|
|
(
|
|
const char * server
|
|
)
|
|
{
|
|
return ghiSetProxy(server);
|
|
}
|
|
|
|
GHTTPBool ghttpSetRequestProxy
|
|
(
|
|
GHTTPRequest request,
|
|
const char * server
|
|
)
|
|
{
|
|
return ghiSetRequestProxy(request, server);
|
|
}
|
|
|
|
|
|
void ghttpSetThrottle
|
|
(
|
|
GHTTPRequest request,
|
|
GHTTPBool throttle
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return;
|
|
|
|
connection->throttle = throttle;
|
|
|
|
// Set the buffer size based on the throttle setting.
|
|
/////////////////////////////////////////////////////
|
|
if(connection->socket != INVALID_SOCKET)
|
|
SetReceiveBufferSize(connection->socket, throttle?ghiThrottleBufferSize:(8 * 1024));
|
|
}
|
|
|
|
void ghttpThrottleSettings
|
|
(
|
|
int bufferSize,
|
|
gsi_time timeDelay
|
|
)
|
|
{
|
|
ghiThrottleSettings(bufferSize, timeDelay);
|
|
}
|
|
|
|
void ghttpSetMaxRecvTime
|
|
(
|
|
GHTTPRequest request,
|
|
gsi_time maxRecvTime
|
|
)
|
|
{
|
|
GHIConnection* connection = ghiRequestToConnection(request);
|
|
if (connection == NULL)
|
|
return;
|
|
|
|
connection->maxRecvTime = maxRecvTime;
|
|
}
|
|
|
|
// Internal prototypes for persistent HTTP connections
|
|
// Prevents warnings from strict compilers
|
|
//////////////////////////////////////////////////////
|
|
SOCKET ghttpGetSocket(GHTTPRequest request);
|
|
GHTTPBool ghttpReuseSocket(GHTTPRequest request, SOCKET socket);
|
|
|
|
// For use in persistent HTTP connections
|
|
// Call this in the completed callback to obtain the socket, which can be used with
|
|
// ghttpReuseSocket to make a second request to the same host
|
|
///////////////////////////////////////////////////////////////////
|
|
SOCKET ghttpGetSocket
|
|
(
|
|
GHTTPRequest request
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
SOCKET ret;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return INVALID_SOCKET;
|
|
|
|
// Only allow them to grab the socket during the competion callback (not while a request is in process)
|
|
/////////////////////////////////////////////////////////
|
|
if (!connection->completed)
|
|
return INVALID_SOCKET;
|
|
|
|
ret = connection->socket;
|
|
// Mark the connection as invalid so that it doesn't get closed
|
|
connection->socket = INVALID_SOCKET;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// For use in persistent HTTP connections
|
|
// Call this after creating a request, but before calling think the first time in order to reuse
|
|
// an existing connection to the same server
|
|
// If the socket passed is INVALID_SOCKET, a new connection will be created and marked as persistent
|
|
///////////////////////////////////////////////////////////////////
|
|
GHTTPBool ghttpReuseSocket
|
|
(
|
|
GHTTPRequest request,
|
|
SOCKET socket
|
|
)
|
|
{
|
|
GHIConnection * connection;
|
|
|
|
// Get the connection object for this request.
|
|
//////////////////////////////////////////////
|
|
connection = ghiRequestToConnection(request);
|
|
if(!connection)
|
|
return GHTTPFalse;
|
|
if (connection->state != GHTTPSocketInit)
|
|
return GHTTPFalse;
|
|
if (connection->socket != INVALID_SOCKET)
|
|
return GHTTPFalse;
|
|
|
|
connection->persistConnection = GHTTPTrue;
|
|
connection->socket = socket;
|
|
|
|
|
|
/*
|
|
if (socket == INVALID_SOCKET)
|
|
{
|
|
// The connection is marked as persistent, but we still need to lookup and connect, so don't advance the state
|
|
/////////////////////////////////////////////////////////////////////
|
|
return GHTTPTrue;
|
|
}
|
|
|
|
// Skip the host lookup & connect - send data once the socket is writable
|
|
//////////////////////////////////////////////
|
|
connection->state = GHTTPConnecting;
|
|
*/
|
|
return GHTTPTrue;
|
|
}
|
|
|
|
|
|
GHTTPPost ghttpNewPost
|
|
(
|
|
void
|
|
)
|
|
{
|
|
return ghiNewPost();
|
|
}
|
|
|
|
void ghttpPostSetAutoFree
|
|
(
|
|
GHTTPPost post,
|
|
GHTTPBool autoFree
|
|
)
|
|
{
|
|
assert(post);
|
|
if(!post)
|
|
return;
|
|
|
|
ghiPostSetAutoFree(post, autoFree);
|
|
}
|
|
|
|
void ghttpFreePost
|
|
(
|
|
GHTTPPost post
|
|
)
|
|
{
|
|
assert(post);
|
|
if(!post)
|
|
return;
|
|
|
|
ghiFreePost(post);
|
|
}
|
|
|
|
GHTTPBool ghttpPostAddStringA
|
|
(
|
|
GHTTPPost post,
|
|
const char * name,
|
|
const char * string
|
|
)
|
|
{
|
|
assert(post);
|
|
assert(name && name[0]);
|
|
|
|
if(!post)
|
|
return GHTTPFalse;
|
|
if(!name || !name[0])
|
|
return GHTTPFalse;
|
|
if(!string)
|
|
string = "";
|
|
|
|
return ghiPostAddString(post, name, string);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPBool ghttpPostAddStringW
|
|
(
|
|
GHTTPPost post,
|
|
const unsigned short * name,
|
|
const unsigned short * string
|
|
)
|
|
{
|
|
char name_A[1024] = {'\0'};
|
|
char string_A[1024] = {'\0'};
|
|
if (name != NULL)
|
|
UCS2ToAsciiString(name, name_A);
|
|
if (string != NULL)
|
|
UCS2ToAsciiString(string, string_A);
|
|
return ghttpPostAddStringA(post, name_A, string_A);
|
|
}
|
|
#endif
|
|
|
|
GHTTPBool ghttpPostAddFileFromDiskA
|
|
(
|
|
GHTTPPost post,
|
|
const char * name,
|
|
const char * filename,
|
|
const char * reportFilename,
|
|
const char * contentType
|
|
)
|
|
{
|
|
assert(post);
|
|
assert(name && name[0]);
|
|
assert(filename && filename[0]);
|
|
|
|
if(!post)
|
|
return GHTTPFalse;
|
|
if(!name || !name[0])
|
|
return GHTTPFalse;
|
|
if(!filename || !filename[0])
|
|
return GHTTPFalse;
|
|
if(!reportFilename || !reportFilename[0])
|
|
reportFilename = filename;
|
|
if(!contentType)
|
|
contentType = "application/octet-stream";
|
|
|
|
return ghiPostAddFileFromDisk(post, name, filename, reportFilename, contentType);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPBool ghttpPostAddFileFromDiskW
|
|
(
|
|
GHTTPPost post,
|
|
const unsigned short * name,
|
|
const unsigned short * filename,
|
|
const unsigned short * reportFilename,
|
|
const unsigned short * contentType
|
|
)
|
|
{
|
|
char name_A[1024] = {'\0'};
|
|
char filename_A[1024] = {'\0'};
|
|
char reportFilename_A[1024] = {'\0'};
|
|
char contentType_A[1024] = {'\0'};
|
|
if (name != NULL) UCS2ToAsciiString(name, name_A);
|
|
if (filename != NULL) UCS2ToAsciiString(filename, filename_A);
|
|
if (reportFilename != NULL) UCS2ToAsciiString(reportFilename, reportFilename_A);
|
|
if (contentType != NULL) UCS2ToAsciiString(contentType, contentType_A);
|
|
return ghttpPostAddFileFromDiskA(post, name_A, filename_A, reportFilename_A, contentType_A);
|
|
}
|
|
#endif
|
|
|
|
GHTTPBool ghttpPostAddFileFromMemoryA
|
|
(
|
|
GHTTPPost post,
|
|
const char * name,
|
|
const char * buffer,
|
|
int bufferLen,
|
|
const char * reportFilename,
|
|
const char * contentType
|
|
)
|
|
{
|
|
assert(post);
|
|
assert(name && name[0]);
|
|
assert(bufferLen >= 0);
|
|
#ifdef _DEBUG
|
|
if(bufferLen > 0)
|
|
assert(buffer);
|
|
#endif
|
|
assert(reportFilename && reportFilename[0]);
|
|
|
|
if(!post)
|
|
return GHTTPFalse;
|
|
if(!name || !name[0])
|
|
return GHTTPFalse;
|
|
if(bufferLen < 0)
|
|
return GHTTPFalse;
|
|
if(!bufferLen && !buffer)
|
|
return GHTTPFalse;
|
|
if(!contentType)
|
|
contentType = "application/octet-stream";
|
|
|
|
return ghiPostAddFileFromMemory(post, name, buffer, bufferLen, reportFilename, contentType);
|
|
}
|
|
#ifdef GSI_UNICODE
|
|
GHTTPBool ghttpPostAddFileFromMemoryW
|
|
(
|
|
GHTTPPost post,
|
|
const unsigned short * name,
|
|
const char * buffer,
|
|
int bufferLen,
|
|
const unsigned short * reportFilename,
|
|
const unsigned short * contentType
|
|
)
|
|
{
|
|
char name_A[1024] = { '\0' };
|
|
char reportFilename_A[1024] = { '\0' };
|
|
char contentType_A[1024] = { '\0' };
|
|
if (name != NULL)
|
|
UCS2ToAsciiString(name, name_A);
|
|
if (reportFilename != NULL)
|
|
UCS2ToAsciiString(reportFilename, reportFilename_A);
|
|
if (contentType != NULL)
|
|
UCS2ToAsciiString(contentType, contentType_A);
|
|
|
|
|
|
return ghttpPostAddFileFromMemoryA(post, name_A, buffer, bufferLen, reportFilename_A, contentType_A);
|
|
|
|
GSI_UNUSED(reportFilename);
|
|
GSI_UNUSED(contentType);
|
|
}
|
|
#endif
|
|
|
|
GHTTPBool ghttpPostAddXml
|
|
(
|
|
GHTTPPost post,
|
|
GSXmlStreamWriter soap
|
|
)
|
|
{
|
|
GS_ASSERT(post != NULL);
|
|
GS_ASSERT(soap != NULL);
|
|
|
|
return ghiPostAddXml(post, soap);
|
|
}
|
|
|
|
void ghttpPostSetCallback
|
|
(
|
|
GHTTPPost post,
|
|
ghttpPostCallback callback,
|
|
void * param
|
|
)
|
|
{
|
|
assert(post);
|
|
|
|
if(!post)
|
|
return;
|
|
|
|
ghiPostSetCallback(post, callback, param);
|
|
}
|
|
|
|
|