mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-13 03:33:48 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
455
Engine/source/app/net/httpObject.cpp
Normal file
455
Engine/source/app/net/httpObject.cpp
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "app/net/httpObject.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(HTTPObject);
|
||||
|
||||
ConsoleDocClass( HTTPObject,
|
||||
"@brief Allows communications between the game and a server using HTTP.\n\n"
|
||||
|
||||
"HTTPObject is derrived from TCPObject and makes use of the same callbacks for dealing with "
|
||||
"connections and received data. However, the way in which you use HTTPObject to connect "
|
||||
"with a server is different than TCPObject. Rather than opening a connection, sending data, "
|
||||
"waiting to receive data, and then closing the connection, you issue a get() or post() and "
|
||||
"handle the response. The connection is automatically created and destroyed for you.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// In this example we'll retrieve the weather in Las Vegas using\n"
|
||||
"// Google's API. The response is in XML which could be processed\n"
|
||||
"// and used by the game using SimXMLDocument, but we'll just output\n"
|
||||
"// the results to the console in this example.\n\n"
|
||||
|
||||
"// Define callbacks for our specific HTTPObject using our instance's\n"
|
||||
"// name (WeatherFeed) as the namespace.\n\n"
|
||||
|
||||
"// Handle an issue with resolving the server's name\n"
|
||||
"function WeatherFeed::onDNSFailed(%this)\n"
|
||||
"{\n"
|
||||
" // Store this state\n"
|
||||
" %this.lastState = \"DNSFailed\";\n\n"
|
||||
|
||||
" // Handle DNS failure\n"
|
||||
"}\n\n"
|
||||
|
||||
"function WeatherFeed::onConnectFailed(%this)\n"
|
||||
"{\n"
|
||||
" // Store this state\n"
|
||||
" %this.lastState = \"ConnectFailed\";\n\n"
|
||||
" // Handle connection failure\n"
|
||||
"}\n\n"
|
||||
|
||||
"function WeatherFeed::onDNSResolved(%this)\n"
|
||||
"{\n"
|
||||
" // Store this state\n"
|
||||
" %this.lastState = \"DNSResolved\";\n\n"
|
||||
"}\n\n"
|
||||
|
||||
"function WeatherFeed::onConnected(%this)\n"
|
||||
"{\n"
|
||||
" // Store this state\n"
|
||||
" %this.lastState = \"Connected\";\n\n"
|
||||
|
||||
" // Clear our buffer\n"
|
||||
" %this.buffer = \"\";\n"
|
||||
"}\n\n"
|
||||
|
||||
"function WeatherFeed::onDisconnect(%this)\n"
|
||||
"{\n"
|
||||
" // Store this state\n"
|
||||
" %this.lastState = \"Disconnected\";\n\n"
|
||||
|
||||
" // Output the buffer to the console\n"
|
||||
" echo(\"Google Weather Results:\");\n"
|
||||
" echo(%this.buffer);\n"
|
||||
"}\n\n"
|
||||
|
||||
"// Handle a line from the server\n"
|
||||
"function WeatherFeed::onLine(%this, %line)\n"
|
||||
"{\n"
|
||||
" // Store this line in out buffer\n"
|
||||
" %this.buffer = %this.buffer @ %line;\n"
|
||||
"}\n\n"
|
||||
|
||||
"// Create the HTTPObject\n"
|
||||
"%feed = new HTTPObject(WeatherFeed);\n\n"
|
||||
|
||||
"// Define a dynamic field to store the last connection state\n"
|
||||
"%feed.lastState = \"None\";\n\n"
|
||||
|
||||
"// Send the GET command\n"
|
||||
"%feed.get(\"www.google.com:80\", \"/ig/api\", \"weather=Las-Vegas,US\");\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see TCPObject\n"
|
||||
|
||||
"@ingroup Networking\n"
|
||||
);
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
HTTPObject::HTTPObject()
|
||||
{
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
|
||||
HTTPObject::~HTTPObject()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
dFree(mPost);
|
||||
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
dFree(mBufferSave);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
//--------------------------------------
|
||||
void HTTPObject::get(const char *host, const char *path, const char *query)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query)
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = NULL;
|
||||
|
||||
connect(host);
|
||||
}
|
||||
|
||||
void HTTPObject::post(const char *host, const char *path, const char *query, const char *post)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query && query[0])
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = dStrdup(post);
|
||||
connect(host);
|
||||
}
|
||||
|
||||
static char getHex(char c)
|
||||
{
|
||||
if(c <= 9)
|
||||
return c + '0';
|
||||
return c - 10 + 'A';
|
||||
}
|
||||
|
||||
static S32 getHexVal(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if(c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
else if(c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void HTTPObject::expandPath(char *dest, const char *path, U32 destSize)
|
||||
{
|
||||
static bool asciiEscapeTableBuilt = false;
|
||||
static bool asciiEscapeTable[256];
|
||||
if(!asciiEscapeTableBuilt)
|
||||
{
|
||||
asciiEscapeTableBuilt = true;
|
||||
U32 i;
|
||||
for(i = 0; i <= ' '; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
for(;i <= 0x7F; i++)
|
||||
asciiEscapeTable[i] = false;
|
||||
for(;i <= 0xFF; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
asciiEscapeTable[static_cast<U32>('\"')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('_')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('\'')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('#')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('$')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('%')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('&')] = false;
|
||||
asciiEscapeTable[static_cast<U32>('+')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('-')] = true;
|
||||
asciiEscapeTable[static_cast<U32>('~')] = true;
|
||||
}
|
||||
|
||||
U32 destIndex = 0;
|
||||
U32 srcIndex = 0;
|
||||
while(path[srcIndex] && destIndex < destSize - 3)
|
||||
{
|
||||
char c = path[srcIndex++];
|
||||
if(asciiEscapeTable[static_cast<U32>(c)])
|
||||
{
|
||||
dest[destIndex++] = '%';
|
||||
dest[destIndex++] = getHex((c >> 4) & 0xF);
|
||||
dest[destIndex++] = getHex(c & 0xF);
|
||||
}
|
||||
else
|
||||
dest[destIndex++] = c;
|
||||
}
|
||||
dest[destIndex] = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void HTTPObject::onConnected()
|
||||
{
|
||||
Parent::onConnected();
|
||||
char expPath[8192];
|
||||
char buffer[8192];
|
||||
|
||||
if(mQuery)
|
||||
{
|
||||
dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery);
|
||||
expandPath(expPath, buffer, sizeof(expPath));
|
||||
}
|
||||
else
|
||||
expandPath(expPath, mPath, sizeof(expPath));
|
||||
|
||||
char *pt = dStrchr(mHostName, ':');
|
||||
if(pt)
|
||||
*pt = 0;
|
||||
dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName);
|
||||
if(pt)
|
||||
*pt = ':';
|
||||
|
||||
send((U8*)buffer, dStrlen(buffer));
|
||||
mParseState = ParsingStatusLine;
|
||||
mChunkedEncoding = false;
|
||||
}
|
||||
|
||||
void HTTPObject::onConnectFailed()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onConnectFailed();
|
||||
}
|
||||
|
||||
|
||||
void HTTPObject::onDisconnect()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onDisconnect();
|
||||
}
|
||||
|
||||
bool HTTPObject::processLine(U8 *line)
|
||||
{
|
||||
if(mParseState == ParsingStatusLine)
|
||||
{
|
||||
mParseState = ParsingHeader;
|
||||
}
|
||||
else if(mParseState == ParsingHeader)
|
||||
{
|
||||
if(!dStricmp((char *) line, "transfer-encoding: chunked"))
|
||||
mChunkedEncoding = true;
|
||||
if(line[0] == 0)
|
||||
{
|
||||
if(mChunkedEncoding)
|
||||
mParseState = ParsingChunkHeader;
|
||||
else
|
||||
mParseState = ProcessingBody;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(mParseState == ParsingChunkHeader)
|
||||
{
|
||||
if(line[0]) // strip off the crlf if necessary
|
||||
{
|
||||
mChunkSize = 0;
|
||||
S32 hexVal;
|
||||
while((hexVal = getHexVal(*line++)) != -1)
|
||||
{
|
||||
mChunkSize *= 16;
|
||||
mChunkSize += hexVal;
|
||||
}
|
||||
if(mBufferSave)
|
||||
{
|
||||
mBuffer = mBufferSave;
|
||||
mBufferSize = mBufferSaveSize;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
if(mChunkSize)
|
||||
mParseState = ProcessingBody;
|
||||
else
|
||||
{
|
||||
mParseState = ProcessingDone;
|
||||
finishLastLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Parent::processLine((UTF8*)line);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
if(mParseState == ProcessingBody)
|
||||
{
|
||||
if(mChunkedEncoding && bufferLen >= mChunkSize)
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, mChunkSize);
|
||||
mChunkSize -= ret;
|
||||
if(mChunkSize == 0)
|
||||
{
|
||||
if(mBuffer)
|
||||
{
|
||||
mBufferSaveSize = mBufferSize;
|
||||
mBufferSave = mBuffer;
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
}
|
||||
mParseState = ParsingChunkHeader;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, bufferLen);
|
||||
mChunkSize -= ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else if(mParseState != ProcessingDone)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
return bufferLen;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
DefineEngineMethod( HTTPObject, get, void, ( const char* Address, const char* requirstURI, const char* query ), ( "" ),
|
||||
"@brief Send a GET command to a server to send or retrieve data.\n\n"
|
||||
|
||||
"@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n"
|
||||
"@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n"
|
||||
"@param query Optional. Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. "
|
||||
"If you were building the URL manually, this is the text that follows the question mark. For example: http://www.google.com/ig/api?<b>weather=Las-Vegas,US</b>\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Create an HTTP object for communications\n"
|
||||
"%httpObj = new HTTPObject();\n\n"
|
||||
"// Specify a URL to transmit to\n"
|
||||
"%url = \"www.garagegames.com:80\";\n\n"
|
||||
"// Specify a URI to communicate with\n"
|
||||
"%URI = \"/index.php\";\n\n"
|
||||
"// Specify a query to send.\n"
|
||||
"%query = \"\";\n\n"
|
||||
"// Send the GET command to the server\n"
|
||||
"%httpObj.get(%url,%URI,%query);\n"
|
||||
"@endtsexample\n\n"
|
||||
)
|
||||
{
|
||||
if( !query || !query[ 0 ] )
|
||||
object->get(Address, requirstURI, NULL);
|
||||
else
|
||||
object->get(Address, requirstURI, query);
|
||||
}
|
||||
|
||||
DefineEngineMethod( HTTPObject, post, void, ( const char* Address, const char* requirstURI, const char* query, const char* post ),,
|
||||
"@brief Send POST command to a server to send or retrieve data.\n\n"
|
||||
|
||||
"@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n"
|
||||
"@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n"
|
||||
"@param query Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. \n"
|
||||
"@param post Submission data to be processed.\n"
|
||||
|
||||
"@note The post() method is currently non-functional.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Create an HTTP object for communications\n"
|
||||
"%httpObj = new HTTPObject();\n\n"
|
||||
"// Specify a URL to transmit to\n"
|
||||
"%url = \"www.garagegames.com:80\";\n\n"
|
||||
"// Specify a URI to communicate with\n"
|
||||
"%URI = \"/index.php\";\n\n"
|
||||
"// Specify a query to send.\n"
|
||||
"%query = \"\";\n\n"
|
||||
"// Specify the submission data.\n"
|
||||
"%post = \"\";\n\n"
|
||||
"// Send the POST command to the server\n"
|
||||
"%httpObj.POST(%url,%URI,%query,%post);\n"
|
||||
"@endtsexample\n\n"
|
||||
)
|
||||
{
|
||||
object->post(Address, requirstURI, query, post);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue