PageRenderTime 30ms CodeModel.GetById 17ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/http_server/stack/parser/AWSHttpResponseParser.cpp

http://github.com/jtblatt/duderino
C++ | 269 lines | 173 code | 75 blank | 21 comment | 29 complexity | 5c0ec91217ae6a75f7117e16db636e8e MD5 | raw file
  1/* Copyright (c) 2009 Yahoo! Inc.  All rights reserved.
  2 * The copyrights embodied in the content of this file are licensed by Yahoo! Inc.
  3 * under the BSD (revised) open source license.
  4 */
  5
  6#ifndef AWS_HTTP_RESPONSE_PARSER_H
  7#include <AWSHttpResponseParser.h>
  8#endif
  9
 10#ifndef AWS_HTTP_UTIL_H
 11#include <AWSHttpUtil.h>
 12#endif
 13
 14#ifndef ESF_ASSERT_H
 15#include <ESFAssert.h>
 16#endif
 17
 18#ifndef AWS_HTTP_ERROR_H
 19#include <AWSHttpError.h>
 20#endif
 21
 22#define AWS_PARSING_VERSION (1 << 0)
 23#define AWS_PARSING_STATUS_CODE (1 << 1)
 24#define AWS_PARSING_REASON_PHRASE (1 << 2)
 25#define AWS_PARSE_COMPLETE (1 << 3)
 26
 27AWSHttpResponseParser::AWSHttpResponseParser(ESFBuffer *workingBuffer, ESFDiscardAllocator *allocator) :
 28    AWSHttpMessageParser(workingBuffer, allocator),
 29    _responseState(0x00)
 30{
 31}
 32
 33AWSHttpResponseParser::~AWSHttpResponseParser()
 34{
 35}
 36
 37void AWSHttpResponseParser::reset()
 38{
 39    AWSHttpMessageParser::reset();
 40
 41    _responseState = 0x00;
 42}
 43
 44ESFError AWSHttpResponseParser::parseStartLine(ESFBuffer *inputBuffer, AWSHttpMessage *message)
 45{
 46    // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 47
 48    if (AWS_PARSE_COMPLETE & _responseState)
 49    {
 50        return ESF_INVALID_STATE;
 51    }
 52
 53    if (0x00 == _responseState)
 54    {
 55        _responseState = AWS_PARSING_VERSION;
 56
 57        inputBuffer->readMark();
 58        _workingBuffer->clear();
 59    }
 60
 61    AWSHttpResponse *response = (AWSHttpResponse *) message;
 62
 63    ESFError error = ESF_SUCCESS;
 64
 65    if (AWS_PARSING_VERSION & _responseState)
 66    {
 67        error = parseVersion(inputBuffer, response, true);
 68
 69        if (ESF_SUCCESS != error)
 70        {
 71            return error;
 72        }
 73
 74        _responseState &= ~AWS_PARSING_VERSION;
 75        _responseState |= AWS_PARSING_STATUS_CODE;
 76
 77        inputBuffer->readMark();
 78        _workingBuffer->clear();
 79    }
 80
 81    if (AWS_PARSING_STATUS_CODE & _responseState)
 82    {
 83        error = parseStatusCode(inputBuffer, response);
 84
 85        if (ESF_SUCCESS != error)
 86        {
 87            return error;
 88        }
 89
 90        _responseState &= ~AWS_PARSING_STATUS_CODE;
 91        _responseState |= AWS_PARSING_REASON_PHRASE;
 92
 93        inputBuffer->readMark();
 94        _workingBuffer->clear();
 95    }
 96
 97    if (AWS_PARSING_REASON_PHRASE & _responseState)
 98    {
 99        error = parseReasonPhrase(inputBuffer, response);
100
101        if (ESF_SUCCESS != error)
102        {
103            return error;
104        }
105
106        _responseState &= ~AWS_PARSING_REASON_PHRASE;
107        _responseState |= AWS_PARSE_COMPLETE;
108
109        inputBuffer->readMark();
110        _workingBuffer->clear();
111
112        return ESF_SUCCESS;
113    }
114
115    return ESF_INVALID_STATE;
116}
117
118ESFError AWSHttpResponseParser::parseStatusCode(ESFBuffer *inputBuffer, AWSHttpResponse *response)
119{
120    // Status-Code    = 3DIGIT
121
122    ESF_ASSERT(AWS_PARSING_STATUS_CODE & _responseState);
123
124    // Clients SHOULD be tolerant in parsing the Status-Line and servers
125    // tolerant when parsing the Request-Line. In particular, they SHOULD
126    // accept any amount of SP or HT characters between fields, even though
127    // only a single SP is required.
128
129    AWSHttpUtil::SkipSpaces(inputBuffer);
130
131    if (4 > inputBuffer->getReadable())
132    {
133        return ESF_AGAIN;
134    }
135
136    int statusCode = 0;
137    unsigned char octet;
138
139    for (int i = 0; i < 3; ++i)
140    {
141        ESF_ASSERT(inputBuffer->isReadable());
142
143        octet = inputBuffer->getNext();
144
145        if (false == AWSHttpUtil::IsDigit(octet))
146        {
147            return AWS_HTTP_BAD_STATUS_CODE;
148        }
149
150        statusCode = (statusCode * 10) + (octet - '0');
151    }
152
153    ESF_ASSERT(inputBuffer->isReadable());
154
155    if (false == AWSHttpUtil::IsSpace(inputBuffer->getNext()))
156    {
157        return AWS_HTTP_BAD_STATUS_CODE;
158    }
159
160    response->setStatusCode(statusCode);
161
162    return ESF_SUCCESS;
163}
164
165
166ESFError AWSHttpResponseParser::parseReasonPhrase(ESFBuffer *inputBuffer, AWSHttpResponse *response)
167{
168    // Reason-Phrase  = *<TEXT, excluding CR, LF>
169
170    ESF_ASSERT(AWS_PARSING_REASON_PHRASE & _responseState);
171
172    ESFError error;
173    unsigned char octet;
174
175    while (true)
176    {
177        if (false == inputBuffer->isReadable())
178        {
179            return ESF_AGAIN;
180        }
181
182        if (false == _workingBuffer->isWritable())
183        {
184            return ESF_OVERFLOW;
185        }
186
187        octet = inputBuffer->getNext();
188
189        if (AWSHttpUtil::IsLWS(octet))
190        {
191            inputBuffer->setReadPosition(inputBuffer->getReadPosition() - 1);
192
193            error = AWSHttpUtil::SkipLWS(inputBuffer);
194
195            switch (error)
196            {
197                case ESF_SUCCESS:
198
199                    // newline encountered - save reason phrase
200
201                    {
202                        unsigned char *reasonPhrase = _workingBuffer->duplicate(_allocator, true);    // trims trailing whitespace
203
204                        if (! reasonPhrase)
205                        {
206                            return ESF_OUT_OF_MEMORY;
207                        }
208
209                        response->setReasonPhrase(reasonPhrase);
210                    }
211
212                    return ESF_SUCCESS;
213
214                case ESF_INPROGRESS:
215
216                    // LWS encountered - replace with a single space & trim leading white space
217
218                    if (0 < _workingBuffer->getWritePosition())
219                    {
220                        _workingBuffer->putNext(' ');
221                    }
222
223                    break;
224
225                default:
226
227                    return error;
228            }
229
230            continue;
231        }
232
233        if (AWSHttpUtil::IsText(octet))
234        {
235            _workingBuffer->putNext(octet);
236            continue;
237        }
238
239        return AWS_HTTP_BAD_REASON_PHRASE;
240    }
241}
242
243bool AWSHttpResponseParser::isBodyNotAllowed(AWSHttpMessage *message)
244{
245    // For response messages, whether or not a message-body is included with
246    // a message is dependent on both the request method and the response
247    // status code (section 6.1.1). All responses to the HEAD request method
248    // MUST NOT include a message-body, even though the presence of entity-
249    // header fields might lead one to believe they do. All 1xx
250    // (informational), 204 (no content), and 304 (not modified) responses
251    // MUST NOT include a message-body. All other responses do include a
252    // message-body, although it MAY be of zero length
253
254    AWSHttpResponse *response = (AWSHttpResponse *) message;
255
256    switch (response->getStatusCode())
257    {
258        case 204:
259        case 304:
260
261            return true;
262
263        default:
264
265            return 200 <= response->getStatusCode();
266    }
267}
268
269