diff options
Diffstat (limited to 'xbmc/utils/HttpParser.cpp')
| -rw-r--r-- | xbmc/utils/HttpParser.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/xbmc/utils/HttpParser.cpp b/xbmc/utils/HttpParser.cpp new file mode 100644 index 0000000..9276d4c --- /dev/null +++ b/xbmc/utils/HttpParser.cpp | |||
| @@ -0,0 +1,236 @@ | |||
| 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 | |||
| 14 | #include "HttpParser.h" | ||
| 15 | |||
| 16 | HttpParser::~HttpParser() = default; | ||
| 17 | |||
| 18 | void | ||
| 19 | HttpParser::parseHeader() | ||
| 20 | { | ||
| 21 | // run the fsm. | ||
| 22 | const int CR = 13; | ||
| 23 | const int LF = 10; | ||
| 24 | const int ANY = 256; | ||
| 25 | |||
| 26 | enum Action { | ||
| 27 | // make lower case | ||
| 28 | LOWER = 0x1, | ||
| 29 | |||
| 30 | // convert current character to null. | ||
| 31 | NULLIFY = 0x2, | ||
| 32 | |||
| 33 | // set the header index to the current position | ||
| 34 | SET_HEADER_START = 0x4, | ||
| 35 | |||
| 36 | // set the key index to the current position | ||
| 37 | SET_KEY = 0x8, | ||
| 38 | |||
| 39 | // set value index to the current position. | ||
| 40 | SET_VALUE = 0x10, | ||
| 41 | |||
| 42 | // store current key/value pair. | ||
| 43 | STORE_KEY_VALUE = 0x20, | ||
| 44 | |||
| 45 | // sets content start to current position + 1 | ||
| 46 | SET_CONTENT_START = 0x40 | ||
| 47 | }; | ||
| 48 | |||
| 49 | static const struct FSM { | ||
| 50 | State curState; | ||
| 51 | int c; | ||
| 52 | State nextState; | ||
| 53 | unsigned actions; | ||
| 54 | } fsm[] = { | ||
| 55 | { p_request_line, CR, p_request_line_cr, NULLIFY }, | ||
| 56 | { p_request_line, ANY, p_request_line, 0 }, | ||
| 57 | { p_request_line_cr, LF, p_request_line_crlf, 0 }, | ||
| 58 | { p_request_line_crlf, CR, p_request_line_crlfcr, 0 }, | ||
| 59 | { p_request_line_crlf, ANY, p_key, SET_HEADER_START | SET_KEY | LOWER }, | ||
| 60 | { p_request_line_crlfcr, LF, p_content, SET_CONTENT_START }, | ||
| 61 | { p_key, ':', p_key_colon, NULLIFY }, | ||
| 62 | { p_key, ANY, p_key, LOWER }, | ||
| 63 | { p_key_colon, ' ', p_key_colon_sp, 0 }, | ||
| 64 | { p_key_colon_sp, ANY, p_value, SET_VALUE }, | ||
| 65 | { p_value, CR, p_value_cr, NULLIFY | STORE_KEY_VALUE }, | ||
| 66 | { p_value, ANY, p_value, 0 }, | ||
| 67 | { p_value_cr, LF, p_value_crlf, 0 }, | ||
| 68 | { p_value_crlf, CR, p_value_crlfcr, 0 }, | ||
| 69 | { p_value_crlf, ANY, p_key, SET_KEY | LOWER }, | ||
| 70 | { p_value_crlfcr, LF, p_content, SET_CONTENT_START }, | ||
| 71 | { p_error, ANY, p_error, 0 } | ||
| 72 | }; | ||
| 73 | |||
| 74 | for( unsigned i = _parsedTo; i < _data.length(); ++i) { | ||
| 75 | char c = _data[i]; | ||
| 76 | State nextState = p_error; | ||
| 77 | |||
| 78 | for (const FSM& f : fsm) { | ||
| 79 | if ( f.curState == _state && | ||
| 80 | ( c == f.c || f.c == ANY ) ) { | ||
| 81 | |||
| 82 | nextState = f.nextState; | ||
| 83 | |||
| 84 | if ( f.actions & LOWER ) { | ||
| 85 | _data[i] = tolower( _data[i] ); | ||
| 86 | } | ||
| 87 | |||
| 88 | if ( f.actions & NULLIFY ) { | ||
| 89 | _data[i] = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | if ( f.actions & SET_HEADER_START ) { | ||
| 93 | _headerStart = i; | ||
| 94 | } | ||
| 95 | |||
| 96 | if ( f.actions & SET_KEY ) { | ||
| 97 | _keyIndex = i; | ||
| 98 | } | ||
| 99 | |||
| 100 | if ( f.actions & SET_VALUE ) { | ||
| 101 | _valueIndex = i; | ||
| 102 | } | ||
| 103 | |||
| 104 | if ( f.actions & SET_CONTENT_START ) { | ||
| 105 | _contentStart = i + 1; | ||
| 106 | } | ||
| 107 | |||
| 108 | if ( f.actions & STORE_KEY_VALUE ) { | ||
| 109 | // store position of first character of key. | ||
| 110 | _keys.push_back( _keyIndex ); | ||
| 111 | } | ||
| 112 | |||
| 113 | break; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | _state = nextState; | ||
| 118 | |||
| 119 | if ( _state == p_content ) { | ||
| 120 | const char* str = getValue("content-length"); | ||
| 121 | if ( str ) { | ||
| 122 | _contentLength = atoi( str ); | ||
| 123 | } | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | _parsedTo = _data.length(); | ||
| 129 | |||
| 130 | } | ||
| 131 | |||
| 132 | bool | ||
| 133 | HttpParser::parseRequestLine() | ||
| 134 | { | ||
| 135 | size_t sp1; | ||
| 136 | size_t sp2; | ||
| 137 | |||
| 138 | sp1 = _data.find( ' ', 0 ); | ||
| 139 | if ( sp1 == std::string::npos ) return false; | ||
| 140 | sp2 = _data.find( ' ', sp1 + 1 ); | ||
| 141 | if ( sp2 == std::string::npos ) return false; | ||
| 142 | |||
| 143 | _data[sp1] = 0; | ||
| 144 | _data[sp2] = 0; | ||
| 145 | _uriIndex = sp1 + 1; | ||
| 146 | return true; | ||
| 147 | } | ||
| 148 | |||
| 149 | HttpParser::status_t | ||
| 150 | HttpParser::addBytes( const char* bytes, unsigned len ) | ||
| 151 | { | ||
| 152 | if ( _status != Incomplete ) { | ||
| 153 | return _status; | ||
| 154 | } | ||
| 155 | |||
| 156 | // append the bytes to data. | ||
| 157 | _data.append( bytes, len ); | ||
| 158 | |||
| 159 | if ( _state < p_content ) { | ||
| 160 | parseHeader(); | ||
| 161 | } | ||
| 162 | |||
| 163 | if ( _state == p_error ) { | ||
| 164 | _status = Error; | ||
| 165 | } else if ( _state == p_content ) { | ||
| 166 | if ( _contentLength == 0 || _data.length() - _contentStart >= _contentLength ) { | ||
| 167 | if ( parseRequestLine() ) { | ||
| 168 | _status = Done; | ||
| 169 | } else { | ||
| 170 | _status = Error; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | return _status; | ||
| 176 | } | ||
| 177 | |||
| 178 | const char* | ||
| 179 | HttpParser::getMethod() const | ||
| 180 | { | ||
| 181 | return &_data[0]; | ||
| 182 | } | ||
| 183 | |||
| 184 | const char* | ||
| 185 | HttpParser::getUri() const | ||
| 186 | { | ||
| 187 | return &_data[_uriIndex]; | ||
| 188 | } | ||
| 189 | |||
| 190 | const char* | ||
| 191 | HttpParser::getQueryString() const | ||
| 192 | { | ||
| 193 | const char* pos = getUri(); | ||
| 194 | while( *pos ) { | ||
| 195 | if ( *pos == '?' ) { | ||
| 196 | pos++; | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | pos++; | ||
| 200 | } | ||
| 201 | return pos; | ||
| 202 | } | ||
| 203 | |||
| 204 | const char* | ||
| 205 | HttpParser::getBody() const | ||
| 206 | { | ||
| 207 | if ( _contentLength > 0 ) { | ||
| 208 | return &_data[_contentStart]; | ||
| 209 | } else { | ||
| 210 | return NULL; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | // key should be in lower case. | ||
| 215 | const char* | ||
| 216 | HttpParser::getValue( const char* key ) const | ||
| 217 | { | ||
| 218 | for( IntArray::const_iterator iter = _keys.begin(); | ||
| 219 | iter != _keys.end(); ++iter ) | ||
| 220 | { | ||
| 221 | unsigned index = *iter; | ||
| 222 | if ( strcmp( &_data[index], key ) == 0 ) { | ||
| 223 | return &_data[index + strlen(key) + 2]; | ||
| 224 | } | ||
| 225 | |||
| 226 | } | ||
| 227 | |||
| 228 | return NULL; | ||
| 229 | } | ||
| 230 | |||
| 231 | unsigned | ||
| 232 | HttpParser::getContentLength() const | ||
| 233 | { | ||
| 234 | return _contentLength; | ||
| 235 | } | ||
| 236 | |||
