PageRenderTime 23ms CodeModel.GetById 6ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/utils/HttpParser.cpp

http://github.com/xbmc/xbmc
C++ | 236 lines | 170 code | 43 blank | 23 comment | 42 complexity | c4aecba3a27a74d1a666ba93c77a1abb MD5 | raw file
  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
 16HttpParser::~HttpParser() = default;
 17
 18void
 19HttpParser::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
132bool
133HttpParser::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
149HttpParser::status_t
150HttpParser::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
178const char*
179HttpParser::getMethod() const
180{
181    return &_data[0];
182}
183
184const char*
185HttpParser::getUri() const
186{
187    return &_data[_uriIndex];
188}
189
190const char*
191HttpParser::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
204const char*
205HttpParser::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.
215const char*
216HttpParser::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
231unsigned
232HttpParser::getContentLength() const
233{
234    return _contentLength;
235}
236