httpObject.cc 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "httpObject.h"
  23. #include "platform/platform.h"
  24. #include "platform/event.h"
  25. #include "io/fileStream.h"
  26. #include "sim/simBase.h"
  27. #include "console/consoleInternal.h"
  28. IMPLEMENT_CONOBJECT(HTTPObject);
  29. //--------------------------------------
  30. HTTPObject::HTTPObject()
  31. {
  32. mHostName = 0;
  33. mPath = 0;
  34. mQuery = 0;
  35. mPost = 0;
  36. mBufferSave = 0;
  37. }
  38. HTTPObject::~HTTPObject()
  39. {
  40. dFree(mHostName);
  41. dFree(mPath);
  42. dFree(mQuery);
  43. dFree(mPost);
  44. mHostName = 0;
  45. mPath = 0;
  46. mQuery = 0;
  47. mPost = 0;
  48. dFree(mBufferSave);
  49. }
  50. //--------------------------------------
  51. //--------------------------------------
  52. void HTTPObject::get(const char *host, const char *path, const char *query)
  53. {
  54. if(mHostName)
  55. dFree(mHostName);
  56. if(mPath)
  57. dFree(mPath);
  58. if(mQuery)
  59. dFree(mQuery);
  60. if(mPost)
  61. dFree(mPost);
  62. if(mBufferSave)
  63. dFree(mBufferSave);
  64. mBufferSave = 0;
  65. mHostName = dStrdup(host);
  66. mPath = dStrdup(path);
  67. if(query)
  68. mQuery = dStrdup(query);
  69. else
  70. mQuery = NULL;
  71. mPost = NULL;
  72. connect(host);
  73. }
  74. void HTTPObject::post(const char *host, const char *path, const char *query, const char *post)
  75. {
  76. if(mHostName)
  77. dFree(mHostName);
  78. if(mPath)
  79. dFree(mPath);
  80. if(mQuery)
  81. dFree(mQuery);
  82. if(mPost)
  83. dFree(mPost);
  84. if(mBufferSave)
  85. dFree(mBufferSave);
  86. mBufferSave = 0;
  87. mHostName = dStrdup(host);
  88. mPath = dStrdup(path);
  89. if(query && query[0])
  90. mQuery = dStrdup(query);
  91. else
  92. mQuery = NULL;
  93. mPost = dStrdup(post);
  94. connect(host);
  95. }
  96. static char getHex(char c)
  97. {
  98. if(c <= 9)
  99. return c + '0';
  100. return c - 10 + 'A';
  101. }
  102. static S32 getHexVal(char c)
  103. {
  104. if(c >= '0' && c <= '9')
  105. return c - '0';
  106. else if(c >= 'A' && c <= 'Z')
  107. return c - 'A' + 10;
  108. else if(c >= 'a' && c <= 'z')
  109. return c - 'a' + 10;
  110. return -1;
  111. }
  112. void HTTPObject::expandPath(char *dest, const char *path, U32 destSize)
  113. {
  114. static bool asciiEscapeTableBuilt = false;
  115. static bool asciiEscapeTable[256];
  116. if(!asciiEscapeTableBuilt)
  117. {
  118. asciiEscapeTableBuilt = true;
  119. U32 i;
  120. for(i = 0; i <= ' '; i++)
  121. asciiEscapeTable[i] = true;
  122. for(;i <= 0x7F; i++)
  123. asciiEscapeTable[i] = false;
  124. for(;i <= 0xFF; i++)
  125. asciiEscapeTable[i] = true;
  126. asciiEscapeTable['\"'] = true;
  127. asciiEscapeTable['_'] = true;
  128. asciiEscapeTable['\''] = true;
  129. asciiEscapeTable['#'] = true;
  130. asciiEscapeTable['$'] = true;
  131. asciiEscapeTable['%'] = true;
  132. asciiEscapeTable['&'] = true;
  133. asciiEscapeTable['+'] = true;
  134. asciiEscapeTable['-'] = true;
  135. asciiEscapeTable['~'] = true;
  136. }
  137. U32 destIndex = 0;
  138. U32 srcIndex = 0;
  139. while(path[srcIndex] && destIndex < destSize - 3)
  140. {
  141. char c = path[srcIndex++];
  142. if(asciiEscapeTable[c])
  143. {
  144. dest[destIndex++] = '%';
  145. dest[destIndex++] = getHex((c >> 4) & 0xF);
  146. dest[destIndex++] = getHex(c & 0xF);
  147. }
  148. else
  149. dest[destIndex++] = c;
  150. }
  151. dest[destIndex] = 0;
  152. }
  153. //--------------------------------------
  154. void HTTPObject::onConnected()
  155. {
  156. Parent::onConnected();
  157. char expPath[8192];
  158. char buffer[8192];
  159. if(mQuery)
  160. {
  161. dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery);
  162. expandPath(expPath, buffer, sizeof(expPath));
  163. }
  164. else
  165. expandPath(expPath, mPath, sizeof(expPath));
  166. char *pt = dStrchr(mHostName, ':');
  167. if(pt)
  168. *pt = 0;
  169. dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName);
  170. if(pt)
  171. *pt = ':';
  172. send((U8*)buffer, dStrlen(buffer));
  173. mParseState = ParsingStatusLine;
  174. mChunkedEncoding = false;
  175. }
  176. void HTTPObject::onConnectFailed()
  177. {
  178. dFree(mHostName);
  179. dFree(mPath);
  180. dFree(mQuery);
  181. mHostName = 0;
  182. mPath = 0;
  183. mQuery = 0;
  184. Parent::onConnectFailed();
  185. }
  186. void HTTPObject::onDisconnect()
  187. {
  188. dFree(mHostName);
  189. dFree(mPath);
  190. dFree(mQuery);
  191. mHostName = 0;
  192. mPath = 0;
  193. mQuery = 0;
  194. Parent::onDisconnect();
  195. }
  196. bool HTTPObject::processLine(U8 *line)
  197. {
  198. if(mParseState == ParsingStatusLine)
  199. {
  200. mParseState = ParsingHeader;
  201. }
  202. else if(mParseState == ParsingHeader)
  203. {
  204. if(!dStricmp((char *) line, "transfer-encoding: chunked"))
  205. mChunkedEncoding = true;
  206. if(line[0] == 0)
  207. {
  208. if(mChunkedEncoding)
  209. mParseState = ParsingChunkHeader;
  210. else
  211. mParseState = ProcessingBody;
  212. return true;
  213. }
  214. }
  215. else if(mParseState == ParsingChunkHeader)
  216. {
  217. if(line[0]) // strip off the crlf if necessary
  218. {
  219. mChunkSize = 0;
  220. S32 hexVal;
  221. while((hexVal = getHexVal(*line++)) != -1)
  222. {
  223. mChunkSize *= 16;
  224. mChunkSize += hexVal;
  225. }
  226. if(mBufferSave)
  227. {
  228. mBuffer = mBufferSave;
  229. mBufferSize = mBufferSaveSize;
  230. mBufferSave = 0;
  231. }
  232. if(mChunkSize)
  233. mParseState = ProcessingBody;
  234. else
  235. {
  236. mParseState = ProcessingDone;
  237. finishLastLine();
  238. }
  239. }
  240. }
  241. else
  242. {
  243. return Parent::processLine(line);
  244. }
  245. return true;
  246. }
  247. U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen)
  248. {
  249. U32 start = 0;
  250. parseLine(buffer, &start, bufferLen);
  251. return start;
  252. }
  253. //--------------------------------------
  254. U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen)
  255. {
  256. if(mParseState == ProcessingBody)
  257. {
  258. if(mChunkedEncoding && bufferLen >= mChunkSize)
  259. {
  260. U32 ret = onDataReceive(buffer, mChunkSize);
  261. mChunkSize -= ret;
  262. if(mChunkSize == 0)
  263. {
  264. if(mBuffer)
  265. {
  266. mBufferSaveSize = mBufferSize;
  267. mBufferSave = mBuffer;
  268. mBuffer = 0;
  269. mBufferSize = 0;
  270. }
  271. mParseState = ParsingChunkHeader;
  272. }
  273. return ret;
  274. }
  275. else
  276. {
  277. U32 ret = onDataReceive(buffer, bufferLen);
  278. mChunkSize -= ret;
  279. return ret;
  280. }
  281. }
  282. else if(mParseState != ProcessingDone)
  283. {
  284. U32 start = 0;
  285. parseLine(buffer, &start, bufferLen);
  286. return start;
  287. }
  288. return bufferLen;
  289. }
  290. //--------------------------------------
  291. ConsoleMethod( HTTPObject, get, void, 4, 5, "(TransportAddress addr, string requirstURI, string query=NULL)")
  292. {
  293. object->get(argv[2], argv[3], argc == 4 ? NULL : argv[4]);
  294. }
  295. ConsoleMethod( HTTPObject, post, void, 6, 6, "(TransportAddress addr, string requestURI, string query, string post)")
  296. {
  297. object->post(argv[2], argv[3], argv[4], argv[5]);
  298. }