2 * Copyright (C) 2011-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
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
14 #include "HttpParser.h"
16 HttpParser::~HttpParser() = default;
19 HttpParser::parseHeader()
30 // convert current character to null.
33 // set the header index to the current position
34 SET_HEADER_START
= 0x4,
36 // set the key index to the current position
39 // set value index to the current position.
42 // store current key/value pair.
43 STORE_KEY_VALUE
= 0x20,
45 // sets content start to current position + 1
46 SET_CONTENT_START
= 0x40
49 static const struct 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 }
74 for( unsigned i
= _parsedTo
; i
< _data
.length(); ++i
) {
76 State nextState
= p_error
;
78 for (const FSM
& f
: fsm
) {
79 if ( f
.curState
== _state
&&
80 ( c
== f
.c
|| f
.c
== ANY
) ) {
82 nextState
= f
.nextState
;
84 if ( f
.actions
& LOWER
) {
85 _data
[i
] = tolower( _data
[i
] );
88 if ( f
.actions
& NULLIFY
) {
92 if ( f
.actions
& SET_HEADER_START
) {
96 if ( f
.actions
& SET_KEY
) {
100 if ( f
.actions
& SET_VALUE
) {
104 if ( f
.actions
& SET_CONTENT_START
) {
105 _contentStart
= i
+ 1;
108 if ( f
.actions
& STORE_KEY_VALUE
) {
109 // store position of first character of key.
110 _keys
.push_back( _keyIndex
);
119 if ( _state
== p_content
) {
120 const char* str
= getValue("content-length");
122 _contentLength
= atoi( str
);
128 _parsedTo
= _data
.length();
133 HttpParser::parseRequestLine()
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;
150 HttpParser::addBytes( const char* bytes
, unsigned len
)
152 if ( _status
!= Incomplete
) {
156 // append the bytes to data.
157 _data
.append( bytes
, len
);
159 if ( _state
< p_content
) {
163 if ( _state
== p_error
) {
165 } else if ( _state
== p_content
) {
166 if ( _contentLength
== 0 || _data
.length() - _contentStart
>= _contentLength
) {
167 if ( parseRequestLine() ) {
179 HttpParser::getMethod() const
185 HttpParser::getUri() const
187 return &_data
[_uriIndex
];
191 HttpParser::getQueryString() const
193 const char* pos
= getUri();
205 HttpParser::getBody() const
207 if ( _contentLength
> 0 ) {
208 return &_data
[_contentStart
];
214 // key should be in lower case.
216 HttpParser::getValue( const char* key
) const
218 for( IntArray::const_iterator iter
= _keys
.begin();
219 iter
!= _keys
.end(); ++iter
)
221 unsigned index
= *iter
;
222 if ( strcmp( &_data
[index
], key
) == 0 ) {
223 return &_data
[index
+ strlen(key
) + 2];
232 HttpParser::getContentLength() const
234 return _contentLength
;