/xbmc/utils/HttpParser.cpp
http://github.com/xbmc/xbmc · C++ · 236 lines · 170 code · 43 blank · 23 comment · 43 complexity · c4aecba3a27a74d1a666ba93c77a1abb MD5 · raw file
- /*
- * 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;
- }