summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/HttpParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/HttpParser.cpp')
-rw-r--r--xbmc/utils/HttpParser.cpp236
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;
+}
+