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 @@ +/* + * Copyright (C) 2011-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + * + * This code implements parsing of HTTP requests. + * This code was written by Steve Hanov in 2009, no copyright is claimed. + * This code is in the public domain. + * Code was taken from http://refactormycode.com/codes/778-an-efficient-http-parser + */ + +#include "HttpParser.h" + +HttpParser::~HttpParser() = default; + +void +HttpParser::parseHeader() +{ + // run the fsm. + const int CR = 13; + const int LF = 10; + const int ANY = 256; + + enum Action { + // make lower case + LOWER = 0x1, + + // convert current character to null. + NULLIFY = 0x2, + + // set the header index to the current position + SET_HEADER_START = 0x4, + + // set the key index to the current position + SET_KEY = 0x8, + + // set value index to the current position. + SET_VALUE = 0x10, + + // store current key/value pair. + STORE_KEY_VALUE = 0x20, + + // sets content start to current position + 1 + SET_CONTENT_START = 0x40 + }; + + static const struct FSM { + State curState; + int c; + State nextState; + unsigned actions; + } fsm[] = { + { p_request_line, CR, p_request_line_cr, NULLIFY }, + { p_request_line, ANY, p_request_line, 0 }, + { p_request_line_cr, LF, p_request_line_crlf, 0 }, + { p_request_line_crlf, CR, p_request_line_crlfcr, 0 }, + { p_request_line_crlf, ANY, p_key, SET_HEADER_START | SET_KEY | LOWER }, + { p_request_line_crlfcr, LF, p_content, SET_CONTENT_START }, + { p_key, ':', p_key_colon, NULLIFY }, + { p_key, ANY, p_key, LOWER }, + { p_key_colon, ' ', p_key_colon_sp, 0 }, + { p_key_colon_sp, ANY, p_value, SET_VALUE }, + { p_value, CR, p_value_cr, NULLIFY | STORE_KEY_VALUE }, + { p_value, ANY, p_value, 0 }, + { p_value_cr, LF, p_value_crlf, 0 }, + { p_value_crlf, CR, p_value_crlfcr, 0 }, + { p_value_crlf, ANY, p_key, SET_KEY | LOWER }, + { p_value_crlfcr, LF, p_content, SET_CONTENT_START }, + { p_error, ANY, p_error, 0 } + }; + + for( unsigned i = _parsedTo; i < _data.length(); ++i) { + char c = _data[i]; + State nextState = p_error; + + for (const FSM& f : fsm) { + if ( f.curState == _state && + ( c == f.c || f.c == ANY ) ) { + + nextState = f.nextState; + + if ( f.actions & LOWER ) { + _data[i] = tolower( _data[i] ); + } + + if ( f.actions & NULLIFY ) { + _data[i] = 0; + } + + if ( f.actions & SET_HEADER_START ) { + _headerStart = i; + } + + if ( f.actions & SET_KEY ) { + _keyIndex = i; + } + + if ( f.actions & SET_VALUE ) { + _valueIndex = i; + } + + if ( f.actions & SET_CONTENT_START ) { + _contentStart = i + 1; + } + + if ( f.actions & STORE_KEY_VALUE ) { + // store position of first character of key. + _keys.push_back( _keyIndex ); + } + + break; + } + } + + _state = nextState; + + if ( _state == p_content ) { + const char* str = getValue("content-length"); + if ( str ) { + _contentLength = atoi( str ); + } + break; + } + } + + _parsedTo = _data.length(); + +} + +bool +HttpParser::parseRequestLine() +{ + size_t sp1; + size_t sp2; + + sp1 = _data.find( ' ', 0 ); + if ( sp1 == std::string::npos ) return false; + sp2 = _data.find( ' ', sp1 + 1 ); + if ( sp2 == std::string::npos ) return false; + + _data[sp1] = 0; + _data[sp2] = 0; + _uriIndex = sp1 + 1; + return true; +} + +HttpParser::status_t +HttpParser::addBytes( const char* bytes, unsigned len ) +{ + if ( _status != Incomplete ) { + return _status; + } + + // append the bytes to data. + _data.append( bytes, len ); + + if ( _state < p_content ) { + parseHeader(); + } + + if ( _state == p_error ) { + _status = Error; + } else if ( _state == p_content ) { + if ( _contentLength == 0 || _data.length() - _contentStart >= _contentLength ) { + if ( parseRequestLine() ) { + _status = Done; + } else { + _status = Error; + } + } + } + + return _status; +} + +const char* +HttpParser::getMethod() const +{ + return &_data[0]; +} + +const char* +HttpParser::getUri() const +{ + return &_data[_uriIndex]; +} + +const char* +HttpParser::getQueryString() const +{ + const char* pos = getUri(); + while( *pos ) { + if ( *pos == '?' ) { + pos++; + break; + } + pos++; + } + return pos; +} + +const char* +HttpParser::getBody() const +{ + if ( _contentLength > 0 ) { + return &_data[_contentStart]; + } else { + return NULL; + } +} + +// key should be in lower case. +const char* +HttpParser::getValue( const char* key ) const +{ + for( IntArray::const_iterator iter = _keys.begin(); + iter != _keys.end(); ++iter ) + { + unsigned index = *iter; + if ( strcmp( &_data[index], key ) == 0 ) { + return &_data[index + strlen(key) + 2]; + } + + } + + return NULL; +} + +unsigned +HttpParser::getContentLength() const +{ + return _contentLength; +} + |