/xbmc/utils/HttpParser.cpp

http://github.com/xbmc/xbmc · C++ · 236 lines · 170 code · 43 blank · 23 comment · 43 complexity · c4aecba3a27a74d1a666ba93c77a1abb MD5 · raw file

  1. /*
  2. * Copyright (C) 2011-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. *
  8. * This code implements parsing of HTTP requests.
  9. * This code was written by Steve Hanov in 2009, no copyright is claimed.
  10. * This code is in the public domain.
  11. * Code was taken from http://refactormycode.com/codes/778-an-efficient-http-parser
  12. */
  13. #include "HttpParser.h"
  14. HttpParser::~HttpParser() = default;
  15. void
  16. HttpParser::parseHeader()
  17. {
  18. // run the fsm.
  19. const int CR = 13;
  20. const int LF = 10;
  21. const int ANY = 256;
  22. enum Action {
  23. // make lower case
  24. LOWER = 0x1,
  25. // convert current character to null.
  26. NULLIFY = 0x2,
  27. // set the header index to the current position
  28. SET_HEADER_START = 0x4,
  29. // set the key index to the current position
  30. SET_KEY = 0x8,
  31. // set value index to the current position.
  32. SET_VALUE = 0x10,
  33. // store current key/value pair.
  34. STORE_KEY_VALUE = 0x20,
  35. // sets content start to current position + 1
  36. SET_CONTENT_START = 0x40
  37. };
  38. static const struct FSM {
  39. State curState;
  40. int c;
  41. State nextState;
  42. unsigned actions;
  43. } fsm[] = {
  44. { p_request_line, CR, p_request_line_cr, NULLIFY },
  45. { p_request_line, ANY, p_request_line, 0 },
  46. { p_request_line_cr, LF, p_request_line_crlf, 0 },
  47. { p_request_line_crlf, CR, p_request_line_crlfcr, 0 },
  48. { p_request_line_crlf, ANY, p_key, SET_HEADER_START | SET_KEY | LOWER },
  49. { p_request_line_crlfcr, LF, p_content, SET_CONTENT_START },
  50. { p_key, ':', p_key_colon, NULLIFY },
  51. { p_key, ANY, p_key, LOWER },
  52. { p_key_colon, ' ', p_key_colon_sp, 0 },
  53. { p_key_colon_sp, ANY, p_value, SET_VALUE },
  54. { p_value, CR, p_value_cr, NULLIFY | STORE_KEY_VALUE },
  55. { p_value, ANY, p_value, 0 },
  56. { p_value_cr, LF, p_value_crlf, 0 },
  57. { p_value_crlf, CR, p_value_crlfcr, 0 },
  58. { p_value_crlf, ANY, p_key, SET_KEY | LOWER },
  59. { p_value_crlfcr, LF, p_content, SET_CONTENT_START },
  60. { p_error, ANY, p_error, 0 }
  61. };
  62. for( unsigned i = _parsedTo; i < _data.length(); ++i) {
  63. char c = _data[i];
  64. State nextState = p_error;
  65. for (const FSM& f : fsm) {
  66. if ( f.curState == _state &&
  67. ( c == f.c || f.c == ANY ) ) {
  68. nextState = f.nextState;
  69. if ( f.actions & LOWER ) {
  70. _data[i] = tolower( _data[i] );
  71. }
  72. if ( f.actions & NULLIFY ) {
  73. _data[i] = 0;
  74. }
  75. if ( f.actions & SET_HEADER_START ) {
  76. _headerStart = i;
  77. }
  78. if ( f.actions & SET_KEY ) {
  79. _keyIndex = i;
  80. }
  81. if ( f.actions & SET_VALUE ) {
  82. _valueIndex = i;
  83. }
  84. if ( f.actions & SET_CONTENT_START ) {
  85. _contentStart = i + 1;
  86. }
  87. if ( f.actions & STORE_KEY_VALUE ) {
  88. // store position of first character of key.
  89. _keys.push_back( _keyIndex );
  90. }
  91. break;
  92. }
  93. }
  94. _state = nextState;
  95. if ( _state == p_content ) {
  96. const char* str = getValue("content-length");
  97. if ( str ) {
  98. _contentLength = atoi( str );
  99. }
  100. break;
  101. }
  102. }
  103. _parsedTo = _data.length();
  104. }
  105. bool
  106. HttpParser::parseRequestLine()
  107. {
  108. size_t sp1;
  109. size_t sp2;
  110. sp1 = _data.find( ' ', 0 );
  111. if ( sp1 == std::string::npos ) return false;
  112. sp2 = _data.find( ' ', sp1 + 1 );
  113. if ( sp2 == std::string::npos ) return false;
  114. _data[sp1] = 0;
  115. _data[sp2] = 0;
  116. _uriIndex = sp1 + 1;
  117. return true;
  118. }
  119. HttpParser::status_t
  120. HttpParser::addBytes( const char* bytes, unsigned len )
  121. {
  122. if ( _status != Incomplete ) {
  123. return _status;
  124. }
  125. // append the bytes to data.
  126. _data.append( bytes, len );
  127. if ( _state < p_content ) {
  128. parseHeader();
  129. }
  130. if ( _state == p_error ) {
  131. _status = Error;
  132. } else if ( _state == p_content ) {
  133. if ( _contentLength == 0 || _data.length() - _contentStart >= _contentLength ) {
  134. if ( parseRequestLine() ) {
  135. _status = Done;
  136. } else {
  137. _status = Error;
  138. }
  139. }
  140. }
  141. return _status;
  142. }
  143. const char*
  144. HttpParser::getMethod() const
  145. {
  146. return &_data[0];
  147. }
  148. const char*
  149. HttpParser::getUri() const
  150. {
  151. return &_data[_uriIndex];
  152. }
  153. const char*
  154. HttpParser::getQueryString() const
  155. {
  156. const char* pos = getUri();
  157. while( *pos ) {
  158. if ( *pos == '?' ) {
  159. pos++;
  160. break;
  161. }
  162. pos++;
  163. }
  164. return pos;
  165. }
  166. const char*
  167. HttpParser::getBody() const
  168. {
  169. if ( _contentLength > 0 ) {
  170. return &_data[_contentStart];
  171. } else {
  172. return NULL;
  173. }
  174. }
  175. // key should be in lower case.
  176. const char*
  177. HttpParser::getValue( const char* key ) const
  178. {
  179. for( IntArray::const_iterator iter = _keys.begin();
  180. iter != _keys.end(); ++iter )
  181. {
  182. unsigned index = *iter;
  183. if ( strcmp( &_data[index], key ) == 0 ) {
  184. return &_data[index + strlen(key) + 2];
  185. }
  186. }
  187. return NULL;
  188. }
  189. unsigned
  190. HttpParser::getContentLength() const
  191. {
  192. return _contentLength;
  193. }