/http_server/stack/parser/AWSHttpResponseParser.cpp
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