//----------------------------------------------------------------------------- // Copyright (c) 2013 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 "httpObject.h" #include "platform/platform.h" #include "platform/event.h" #include "io/fileStream.h" #include "sim/simBase.h" #include "console/consoleInternal.h" #include "httpObject_ScriptBinding.h" IMPLEMENT_CONOBJECT(HTTPObject); //-------------------------------------- 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['\"'] = true; asciiEscapeTable['_'] = true; asciiEscapeTable['\''] = true; asciiEscapeTable['#'] = true; asciiEscapeTable['$'] = true; asciiEscapeTable['%'] = true; asciiEscapeTable['&'] = true; asciiEscapeTable['+'] = true; asciiEscapeTable['-'] = true; asciiEscapeTable['~'] = true; } U32 destIndex = 0; U32 srcIndex = 0; while(path[srcIndex] && destIndex < destSize - 3) { char c = path[srcIndex++]; if(asciiEscapeTable[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(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; }