| 1 |
// Copyright (c) 2009, Whispersoft s.r.l. |
|---|
| 2 |
// All rights reserved. |
|---|
| 3 |
// |
|---|
| 4 |
// Redistribution and use in source and binary forms, with or without |
|---|
| 5 |
// modification, are permitted provided that the following conditions are |
|---|
| 6 |
// met: |
|---|
| 7 |
// |
|---|
| 8 |
// * Redistributions of source code must retain the above copyright |
|---|
| 9 |
// notice, this list of conditions and the following disclaimer. |
|---|
| 10 |
// * Redistributions in binary form must reproduce the above |
|---|
| 11 |
// copyright notice, this list of conditions and the following disclaimer |
|---|
| 12 |
// in the documentation and/or other materials provided with the |
|---|
| 13 |
// distribution. |
|---|
| 14 |
// * Neither the name of Whispersoft s.r.l. nor the names of its |
|---|
| 15 |
// contributors may be used to endorse or promote products derived from |
|---|
| 16 |
// this software without specific prior written permission. |
|---|
| 17 |
// |
|---|
| 18 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 19 |
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 20 |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|---|
| 21 |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|---|
| 22 |
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|---|
| 23 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|---|
| 24 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|---|
| 25 |
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|---|
| 26 |
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 27 |
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|---|
| 28 |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 29 |
// |
|---|
| 30 |
// Author: Catalin Popescu |
|---|
| 31 |
|
|---|
| 32 |
#include <time.h> |
|---|
| 33 |
#include "net/http/http_header.h" |
|---|
| 34 |
#include "common/io/buffer/memory_stream.h" |
|---|
| 35 |
#include "common/base/errno.h" |
|---|
| 36 |
#include "common/base/strutil.h" |
|---|
| 37 |
#include "net/util/base64.h" |
|---|
| 38 |
|
|---|
| 39 |
namespace http { |
|---|
| 40 |
Header::Header(bool is_strict) |
|---|
| 41 |
: is_strict_(is_strict) { |
|---|
| 42 |
Clear(); |
|---|
| 43 |
} |
|---|
| 44 |
Header::~Header() { |
|---|
| 45 |
} |
|---|
| 46 |
|
|---|
| 47 |
void Header::Clear() { |
|---|
| 48 |
bytes_parsed_ = 0; |
|---|
| 49 |
parse_error_ = READ_INIT; |
|---|
| 50 |
last_parse_error_ = READ_INIT; |
|---|
| 51 |
crt_parsing_field_name_.clear(); |
|---|
| 52 |
crt_parsing_field_content_.clear(); |
|---|
| 53 |
|
|---|
| 54 |
http_version_ = VERSION_UNKNOWN; |
|---|
| 55 |
method_ = METHOD_UNKNOWN; |
|---|
| 56 |
uri_.clear(); |
|---|
| 57 |
status_code_ = UNKNOWN; |
|---|
| 58 |
reason_.clear(); |
|---|
| 59 |
first_line_type_ = UNKNOWN_LINE; |
|---|
| 60 |
|
|---|
| 61 |
fields_.clear(); |
|---|
| 62 |
verbatim_.clear(); |
|---|
| 63 |
} |
|---|
| 64 |
|
|---|
| 65 |
const char* Header::ParseErrorName(ParseError err) { |
|---|
| 66 |
switch (err) { |
|---|
| 67 |
CONSIDER(READ_INIT); |
|---|
| 68 |
CONSIDER(READ_OK); |
|---|
| 69 |
CONSIDER(READ_NO_DATA); |
|---|
| 70 |
CONSIDER(READ_BAD_FIELD_SPEC); |
|---|
| 71 |
CONSIDER(READ_NO_FIELD); |
|---|
| 72 |
CONSIDER(READ_NO_STATUS_REASON); |
|---|
| 73 |
CONSIDER(READ_NO_REQUEST_VERSION); |
|---|
| 74 |
CONSIDER(READ_INVALID_STATUS_CODE); |
|---|
| 75 |
CONSIDER(READ_NO_STATUS_CODE); |
|---|
| 76 |
CONSIDER(READ_NO_REQUEST_URI); |
|---|
| 77 |
} |
|---|
| 78 |
return "UNKNOWN"; |
|---|
| 79 |
} |
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
string Header::NormalizeFieldName(const char* field_name, int32 len) { |
|---|
| 83 |
const char* last = field_name + len - 1; |
|---|
| 84 |
while ( last >= field_name && IsLwfChar(*last) ) { |
|---|
| 85 |
--last; |
|---|
| 86 |
--len; |
|---|
| 87 |
} |
|---|
| 88 |
while ( IsLwfChar(*field_name) ) { |
|---|
| 89 |
++field_name; |
|---|
| 90 |
--len; |
|---|
| 91 |
} |
|---|
| 92 |
|
|---|
| 93 |
string s(field_name, len); |
|---|
| 94 |
char* p = const_cast<char*>(s.c_str()); |
|---|
| 95 |
bool should_upcase = true; |
|---|
| 96 |
while ( *p ) { |
|---|
| 97 |
if ( isalpha(*p) ) { |
|---|
| 98 |
if ( should_upcase ) { |
|---|
| 99 |
should_upcase = false; |
|---|
| 100 |
if ( islower(*p) ) { |
|---|
| 101 |
*p = toupper(*p); |
|---|
| 102 |
} |
|---|
| 103 |
} else if ( isupper(*p) ) { |
|---|
| 104 |
*p = tolower(*p); |
|---|
| 105 |
} |
|---|
| 106 |
} else { |
|---|
| 107 |
should_upcase = true; |
|---|
| 108 |
if ( IsWhiteSpace(*p) ) { |
|---|
| 109 |
*p = '-'; |
|---|
| 110 |
} |
|---|
| 111 |
} |
|---|
| 112 |
++p; |
|---|
| 113 |
} |
|---|
| 114 |
return s; |
|---|
| 115 |
} |
|---|
| 116 |
|
|---|
| 117 |
void Header::PrepareStatusLine(HttpReturnCode code, |
|---|
| 118 |
HttpVersion version) { |
|---|
| 119 |
first_line_type_ = STATUS_LINE; |
|---|
| 120 |
status_code_ = code; |
|---|
| 121 |
reason_ = GetHttpReturnCodeDescription(code); |
|---|
| 122 |
http_version_ = version; |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
void Header::PrepareRequestLine(const char* uri, |
|---|
| 126 |
HttpMethod method, |
|---|
| 127 |
HttpVersion version) { |
|---|
| 128 |
first_line_type_ = REQUEST_LINE; |
|---|
| 129 |
uri_ = uri; |
|---|
| 130 |
method_ = method; |
|---|
| 131 |
http_version_ = version; |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
bool Header::AddField(const char* field_name, int32 field_name_len, |
|---|
| 135 |
const char* field_content, int32 field_content_len, |
|---|
| 136 |
bool replace, bool as_is) { |
|---|
| 137 |
if ( !IsValidFieldName(field_name, field_name_len) || |
|---|
| 138 |
!IsValidFieldContent(field_content, field_content_len) ) { |
|---|
| 139 |
return false; |
|---|
| 140 |
} |
|---|
| 141 |
string normalized_name(as_is ? |
|---|
| 142 |
NormalizeFieldName(field_name, field_name_len) : |
|---|
| 143 |
string(field_name, field_name_len)); |
|---|
| 144 |
const FieldMap::iterator it = fields_.find(normalized_name); |
|---|
| 145 |
if ( it == fields_.end() ) { |
|---|
| 146 |
fields_.insert(make_pair(normalized_name, string(field_content))); |
|---|
| 147 |
} else if ( replace || it->second.empty() ) { |
|---|
| 148 |
it->second = field_content; |
|---|
| 149 |
} else { |
|---|
| 150 |
it->second += ", "; |
|---|
| 151 |
it->second += field_content; |
|---|
| 152 |
} |
|---|
| 153 |
return true; |
|---|
| 154 |
} |
|---|
| 155 |
|
|---|
| 156 |
// Removes the field alltogether from the field map |
|---|
| 157 |
bool Header::ClearField(const char* field_name, int32 len, bool as_is) { |
|---|
| 158 |
string normalized_name(as_is ? |
|---|
| 159 |
NormalizeFieldName(field_name, len) : |
|---|
| 160 |
string(field_name, len)); |
|---|
| 161 |
return fields_.erase(normalized_name) > 0; |
|---|
| 162 |
} |
|---|
| 163 |
|
|---|
| 164 |
bool Header::IsValidFieldName(const char* field_name, int32 len) { |
|---|
| 165 |
const char* p = field_name; |
|---|
| 166 |
bool valid = false; // ensures we do have something else beside |
|---|
| 167 |
// spaces |
|---|
| 168 |
while ( len-- > 0 ) { |
|---|
| 169 |
if ( !IsTokenChar(*p) && |
|---|
| 170 |
(!IsWhiteSpace(*p) || p == field_name) ) { |
|---|
| 171 |
return false; |
|---|
| 172 |
} else { |
|---|
| 173 |
valid = true; |
|---|
| 174 |
} |
|---|
| 175 |
++p; |
|---|
| 176 |
} |
|---|
| 177 |
return valid && p > field_name; |
|---|
| 178 |
} |
|---|
| 179 |
|
|---|
| 180 |
bool Header::IsValidFieldContent(const char* field_content, int32 len) { |
|---|
| 181 |
const char* p = field_content; |
|---|
| 182 |
while ( len-- > 0 ) { |
|---|
| 183 |
if ( IsCtlChar(*p) && !IsLwfChar(*p) ) |
|---|
| 184 |
return false; |
|---|
| 185 |
++p; |
|---|
| 186 |
} |
|---|
| 187 |
return true; // empty content fine.. |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
int32 Header::CopyHeaderFields(const Header& src, bool replace) { |
|---|
| 191 |
int32 num = 0; |
|---|
| 192 |
for ( FieldMap::const_iterator it = src.fields().begin(); |
|---|
| 193 |
it != src.fields().end(); ++it ) { |
|---|
| 194 |
if ( AddField(it->first, it->second, replace) ) { |
|---|
| 195 |
++num; |
|---|
| 196 |
} |
|---|
| 197 |
} |
|---|
| 198 |
return num; |
|---|
| 199 |
} |
|---|
| 200 |
|
|---|
| 201 |
int32 Header::CopyHeaders(const Header& src, bool replace) { |
|---|
| 202 |
const int32 num = CopyHeaderFields(src, replace); |
|---|
| 203 |
http_version_ = src.http_version_; |
|---|
| 204 |
method_ = src.method_; |
|---|
| 205 |
status_code_ = src.status_code_; |
|---|
| 206 |
uri_ = src.uri_; |
|---|
| 207 |
reason_ = src.reason_; |
|---|
| 208 |
first_line_type_ = src.first_line_type_; |
|---|
| 209 |
|
|---|
| 210 |
return num; |
|---|
| 211 |
} |
|---|
| 212 |
|
|---|
| 213 |
const char* Header::FindField(const string& field_name, |
|---|
| 214 |
int32* len) const { |
|---|
| 215 |
const FieldMap::const_iterator it = fields_.find(field_name); |
|---|
| 216 |
if ( it == fields_.end() ) { |
|---|
| 217 |
return NULL; |
|---|
| 218 |
} |
|---|
| 219 |
*len = it->second.size(); |
|---|
| 220 |
return it->second.data(); |
|---|
| 221 |
} |
|---|
| 222 |
|
|---|
| 223 |
void Header::AppendToStream(io::MemoryStream* io) const { |
|---|
| 224 |
io->Write(ComposeFirstLine()); |
|---|
| 225 |
for ( FieldMap::const_iterator it = fields_.begin(); |
|---|
| 226 |
it != fields_.end(); ++it ) { |
|---|
| 227 |
io->Write(it->first); |
|---|
| 228 |
io->Write(": "); |
|---|
| 229 |
io->Write(it->second); |
|---|
| 230 |
io->Write("\r\n"); |
|---|
| 231 |
} |
|---|
| 232 |
if ( !verbatim_.empty() ) { |
|---|
| 233 |
io->Write(verbatim_); |
|---|
| 234 |
} |
|---|
| 235 |
io->Write("\r\n"); |
|---|
| 236 |
} |
|---|
| 237 |
string Header::ToString() const { |
|---|
| 238 |
io::MemoryStream tmp; |
|---|
| 239 |
AppendToStream(&tmp); |
|---|
| 240 |
return tmp.ToString(); |
|---|
| 241 |
} |
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 |
string Header::ComposeFirstLine() const { |
|---|
| 245 |
if ( first_line_type_ == REQUEST_LINE ) { |
|---|
| 246 |
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
|---|
| 247 |
return (string(GetHttpMethodName(method_)) + " " + uri_ + " " + |
|---|
| 248 |
GetHttpVersionName(http_version_) + "\r\n"); |
|---|
| 249 |
} else if ( first_line_type_ == STATUS_LINE ) { |
|---|
| 250 |
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF |
|---|
| 251 |
return (GetHttpVersionName(http_version_) + |
|---|
| 252 |
strutil::StringPrintf(" %d ", static_cast<int>(status_code_)) + |
|---|
| 253 |
reason_ + "\r\n"); |
|---|
| 254 |
} |
|---|
| 255 |
return string(strutil::StringPrintf("--- %d UNKNOWN\r\n", first_line_type_)); |
|---|
| 256 |
} |
|---|
| 257 |
|
|---|
| 258 |
bool Header::ParseHeader(io::MemoryStream* io, |
|---|
| 259 |
FirstLineType expected_first_line) { |
|---|
| 260 |
CHECK_NE(last_parse_error_, READ_OK); |
|---|
| 261 |
if ( bytes_parsed_ == 0 ) { |
|---|
| 262 |
CHECK(parse_error_ == READ_INIT || |
|---|
| 263 |
parse_error_ == READ_NO_DATA) |
|---|
| 264 |
<< "Parse Error: " << ParseErrorName(); |
|---|
| 265 |
if ( !ReadFirstLine(io, expected_first_line) ) { |
|---|
| 266 |
return false; |
|---|
| 267 |
} |
|---|
| 268 |
} else { |
|---|
| 269 |
CHECK_NE(parse_error_, READ_INIT); |
|---|
| 270 |
} |
|---|
| 271 |
return ReadHeaderFields(io); |
|---|
| 272 |
} |
|---|
| 273 |
|
|---|
| 274 |
////////////////////////////////////////////////////////////////////// |
|---|
| 275 |
// |
|---|
| 276 |
// Parsing for the first line |
|---|
| 277 |
// |
|---|
| 278 |
bool Header::ReadFirstLine(io::MemoryStream* io, |
|---|
| 279 |
FirstLineType expected_first_line) { |
|---|
| 280 |
string line; |
|---|
| 281 |
if ( !io->ReadCRLFLine(&line) ) { |
|---|
| 282 |
set_parse_error(READ_NO_DATA); |
|---|
| 283 |
return false; |
|---|
| 284 |
} |
|---|
| 285 |
DLOG_DEBUG << "HTTP first line parse: " << line.size() << " [" |
|---|
| 286 |
<< strutil::JsonStrEscape(line.data(), |
|---|
| 287 |
min(line.size(), 0x100U)) << "]"; |
|---|
| 288 |
DCHECK_GE(line.size(), 2); // at least CRLF |
|---|
| 289 |
bytes_parsed_ += line.size(); |
|---|
| 290 |
|
|---|
| 291 |
// Cut the last CRLF |
|---|
| 292 |
line.resize(line.size() - 2); |
|---|
| 293 |
|
|---|
| 294 |
// We have three ' ' separated tokens. The meaning of each depend |
|---|
| 295 |
// on the type of line (status vs. request) |
|---|
| 296 |
|
|---|
| 297 |
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
|---|
| 298 |
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF |
|---|
| 299 |
|
|---|
| 300 |
////////////////////////////////////////////////////////////////////// |
|---|
| 301 |
// |
|---|
| 302 |
// 1nd Token |
|---|
| 303 |
// |
|---|
| 304 |
// Pull the first token - method / http_version |
|---|
| 305 |
size_t method_pos = line.find(' '); |
|---|
| 306 |
if ( method_pos == string::npos ) { |
|---|
| 307 |
// Error at the first step - save whatever is to be saved and return |
|---|
| 308 |
first_line_type_ = ERROR_LINE; |
|---|
| 309 |
if ( expected_first_line == REQUEST_LINE ) { |
|---|
| 310 |
set_parse_error(READ_NO_REQUEST_URI); |
|---|
| 311 |
method_ = GetHttpMethod(line.c_str()); |
|---|
| 312 |
} else { |
|---|
| 313 |
set_parse_error(READ_NO_STATUS_CODE); |
|---|
| 314 |
http_version_ = GetHttpVersion(line.c_str()); |
|---|
| 315 |
} |
|---|
| 316 |
return true; // we can parse more |
|---|
| 317 |
} |
|---|
| 318 |
// Got a good first token ! |
|---|
| 319 |
if ( expected_first_line == REQUEST_LINE ) { |
|---|
| 320 |
method_ = GetHttpMethod(line.substr(0, method_pos).c_str()); |
|---|
| 321 |
} else { |
|---|
| 322 |
http_version_ = GetHttpVersion(line.substr(0, method_pos).c_str()); |
|---|
| 323 |
} |
|---|
| 324 |
// Pass through any following spaces |
|---|
| 325 |
while ( line[method_pos] == ' ' ) { |
|---|
| 326 |
method_pos++; |
|---|
| 327 |
} |
|---|
| 328 |
|
|---|
| 329 |
////////////////////////////////////////////////////////////////////// |
|---|
| 330 |
// |
|---|
| 331 |
// 2nd Token |
|---|
| 332 |
// |
|---|
| 333 |
// Pull the second token - uri / status code (right after the first space) |
|---|
| 334 |
DCHECK_LE(method_pos, line.size()); |
|---|
| 335 |
size_t uri_pos = line.find(' ', method_pos); |
|---|
| 336 |
if ( uri_pos == string::npos ) { |
|---|
| 337 |
// Bad - no next space - set the error |
|---|
| 338 |
first_line_type_ = ERROR_LINE; |
|---|
| 339 |
if ( expected_first_line == REQUEST_LINE ) { |
|---|
| 340 |
set_parse_error(READ_NO_REQUEST_VERSION); |
|---|
| 341 |
uri_ = line.substr(method_pos); |
|---|
| 342 |
} else { |
|---|
| 343 |
// This is not an error for 1.0 or less.. |
|---|
| 344 |
if ( http_version_ <= VERSION_1_0 ) { |
|---|
| 345 |
first_line_type_ = expected_first_line; |
|---|
| 346 |
} else { |
|---|
| 347 |
set_parse_error(READ_NO_STATUS_REASON); |
|---|
| 348 |
} |
|---|
| 349 |
ParseStatusCode(line.substr(method_pos)); |
|---|
| 350 |
} |
|---|
| 351 |
return true; // we can parse more |
|---|
| 352 |
} |
|---|
| 353 |
|
|---|
| 354 |
// Got the second token !! |
|---|
| 355 |
if ( expected_first_line == REQUEST_LINE ) { |
|---|
| 356 |
uri_ = line.substr(method_pos, uri_pos - method_pos); |
|---|
| 357 |
} else { |
|---|
| 358 |
ParseStatusCode(line.substr(method_pos, uri_pos - method_pos)); |
|---|
| 359 |
} |
|---|
| 360 |
// Pass through any following spaces |
|---|
| 361 |
while ( line[uri_pos] == ' ' ) { |
|---|
| 362 |
uri_pos++; |
|---|
| 363 |
} |
|---|
| 364 |
|
|---|
| 365 |
////////////////////////////////////////////////////////////////////// |
|---|
| 366 |
// |
|---|
| 367 |
// 3rd Token |
|---|
| 368 |
// |
|---|
| 369 |
|
|---|
| 370 |
// Pull the third token - if any - http version / Reason |
|---|
| 371 |
if ( expected_first_line == REQUEST_LINE ) { |
|---|
| 372 |
http_version_ = GetHttpVersion(line.substr(uri_pos).c_str()); |
|---|
| 373 |
} else { |
|---|
| 374 |
reason_ = line.substr(uri_pos); |
|---|
| 375 |
} |
|---|
| 376 |
// We met our expectation ! |
|---|
| 377 |
first_line_type_ = expected_first_line; |
|---|
| 378 |
|
|---|
| 379 |
return true; |
|---|
| 380 |
} |
|---|
| 381 |
|
|---|
| 382 |
void Header::ParseStatusCode(const string& status_code_str) { |
|---|
| 383 |
errno = 0; // essential ! |
|---|
| 384 |
const int status_code_int = strtol(status_code_str.c_str(), NULL, 10); |
|---|
| 385 |
if ( errno ) { |
|---|
| 386 |
set_parse_error(READ_INVALID_STATUS_CODE); |
|---|
| 387 |
status_code_ = UNKNOWN; |
|---|
| 388 |
} else { |
|---|
| 389 |
status_code_ = HttpReturnCode(status_code_int); |
|---|
| 390 |
} |
|---|
| 391 |
} |
|---|
| 392 |
|
|---|
| 393 |
bool Header::AddCrtParsingData() { |
|---|
| 394 |
bool ret = true; |
|---|
| 395 |
if ( !crt_parsing_field_name_.empty() ) { |
|---|
| 396 |
if ( !IsValidFieldName(crt_parsing_field_name_) || |
|---|
| 397 |
!IsValidFieldContent(crt_parsing_field_content_) ) { |
|---|
| 398 |
set_parse_error(READ_BAD_FIELD_SPEC); |
|---|
| 399 |
ret = false; |
|---|
| 400 |
} |
|---|
| 401 |
if ( !is_strict_ || ret ) { |
|---|
| 402 |
string normalized_name( |
|---|
| 403 |
NormalizeFieldName(crt_parsing_field_name_)); |
|---|
| 404 |
const FieldMap::iterator it = fields_.find(normalized_name); |
|---|
| 405 |
if ( it == fields_.end() ) { |
|---|
| 406 |
fields_.insert(make_pair(normalized_name, |
|---|
| 407 |
crt_parsing_field_content_)); |
|---|
| 408 |
} else { |
|---|
| 409 |
it->second.append(", "); |
|---|
| 410 |
it->second.append(crt_parsing_field_content_); |
|---|
| 411 |
} |
|---|
| 412 |
} |
|---|
| 413 |
} |
|---|
| 414 |
crt_parsing_field_name_.clear(); |
|---|
| 415 |
crt_parsing_field_content_.clear(); |
|---|
| 416 |
return ret; |
|---|
| 417 |
} |
|---|
| 418 |
|
|---|
| 419 |
bool Header::ReadHeaderFields(io::MemoryStream* io) { |
|---|
| 420 |
string line; |
|---|
| 421 |
while ( io->ReadCRLFLine(&line) ) { |
|---|
| 422 |
DCHECK_GE(line.size(), 2); // at least CRLF |
|---|
| 423 |
bytes_parsed_ += line.size(); |
|---|
| 424 |
// Cut the last CRLF |
|---|
| 425 |
line.resize(line.size() - 2); |
|---|
| 426 |
|
|---|
| 427 |
// Process the line: |
|---|
| 428 |
if ( line.empty() ) { |
|---|
| 429 |
// The last empty line w/ CRLF |
|---|
| 430 |
if ( AddCrtParsingData() ) |
|---|
| 431 |
set_parse_error(READ_OK); |
|---|
| 432 |
return true; |
|---|
| 433 |
} |
|---|
| 434 |
// Is a continuation header ? |
|---|
| 435 |
int32 lwf_end = 0; |
|---|
| 436 |
while ( IsLwfChar(line[lwf_end]) ) |
|---|
| 437 |
++lwf_end; |
|---|
| 438 |
if ( lwf_end > 0 ) { |
|---|
| 439 |
// Continuation field |
|---|
| 440 |
if ( crt_parsing_field_name_.empty() ) { |
|---|
| 441 |
// continuation w/ no previous field - discard |
|---|
| 442 |
set_parse_error(READ_NO_FIELD); |
|---|
| 443 |
} else { |
|---|
| 444 |
// continue field content.. |
|---|
| 445 |
crt_parsing_field_content_.append(" "); |
|---|
| 446 |
crt_parsing_field_content_.append(line.substr(lwf_end)); |
|---|
| 447 |
} |
|---|
| 448 |
} else { |
|---|
| 449 |
// Save whatever is in for us .. |
|---|
| 450 |
AddCrtParsingData(); |
|---|
| 451 |
// Parse the current line .. |
|---|
| 452 |
size_t field_pos = line.find(':'); |
|---|
| 453 |
if ( field_pos == string::npos ) { |
|---|
| 454 |
set_parse_error(READ_NO_FIELD); |
|---|
| 455 |
} else { |
|---|
| 456 |
crt_parsing_field_name_ = line.substr(0, field_pos++); |
|---|
| 457 |
// Pass over leading spaces before field content .. |
|---|
| 458 |
while ( IsLwfChar(line[field_pos]) ) |
|---|
| 459 |
++field_pos; |
|---|
| 460 |
crt_parsing_field_content_ = line.substr(field_pos); |
|---|
| 461 |
} |
|---|
| 462 |
} |
|---|
| 463 |
} |
|---|
| 464 |
set_parse_error(READ_NO_DATA); |
|---|
| 465 |
return false; |
|---|
| 466 |
} |
|---|
| 467 |
|
|---|
| 468 |
// Acceptable HTTP date formats |
|---|
| 469 |
// |
|---|
| 470 |
static const char kHttpDateFormats[][128] = { |
|---|
| 471 |
// Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 |
|---|
| 472 |
"%a, %d %b %Y %H:%M:%S %Z", |
|---|
| 473 |
// Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 |
|---|
| 474 |
"%A, %d-%b-%y %H:%M:%S %Z", |
|---|
| 475 |
// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format |
|---|
| 476 |
"%a %b %d %H:%M:%S %Y", |
|---|
| 477 |
}; |
|---|
| 478 |
|
|---|
| 479 |
time_t Header::GetDateField(const char* field_name) { |
|---|
| 480 |
int32 len; |
|---|
| 481 |
const char* s = FindField(field_name, &len); |
|---|
| 482 |
if ( !s ) { |
|---|
| 483 |
return time_t(0); |
|---|
| 484 |
} |
|---|
| 485 |
if ( *(s+len) != '\0' ) |
|---|
| 486 |
return time_t(0); |
|---|
| 487 |
for ( int32 i = 0; i < NUMBEROF(kHttpDateFormats); ++i ) { |
|---|
| 488 |
struct tm t; |
|---|
| 489 |
if ( strptime(s, kHttpDateFormats[i], &t) != NULL ) { |
|---|
| 490 |
return mktime(&t); |
|---|
| 491 |
} |
|---|
| 492 |
} |
|---|
| 493 |
return time_t(0); |
|---|
| 494 |
} |
|---|
| 495 |
|
|---|
| 496 |
bool Header::SetDateField(const string& field_name, time_t t) { |
|---|
| 497 |
char buffer[128]; |
|---|
| 498 |
struct tm tstruct; |
|---|
| 499 |
if ( NULL == gmtime_r(&t, &tstruct) ) { |
|---|
| 500 |
return false; |
|---|
| 501 |
} |
|---|
| 502 |
if ( strftime(buffer, sizeof(buffer), kHttpDateFormats[0], &tstruct) == 0 ) { |
|---|
| 503 |
return false; |
|---|
| 504 |
} |
|---|
| 505 |
return AddField(field_name, string(buffer), true); |
|---|
| 506 |
} |
|---|
| 507 |
|
|---|
| 508 |
bool Header::GetAuthorizationField(string* user, string* passwd) { |
|---|
| 509 |
int32 len; |
|---|
| 510 |
const char* s = FindField(kHeaderAuthorization, &len); |
|---|
| 511 |
if ( !s || len == 0 ) { |
|---|
| 512 |
return false; |
|---|
| 513 |
} |
|---|
| 514 |
const char* p = strchr(strutil::StrFrontTrim(s), ' '); |
|---|
| 515 |
len -= p - s; |
|---|
| 516 |
char* const decoded_field = new char[len]; |
|---|
| 517 |
base64::Decoder decoder; |
|---|
| 518 |
int32 decoded_len = decoder.Decode(p, len, decoded_field); |
|---|
| 519 |
if ( decoded_len == 0 ) { |
|---|
| 520 |
delete [] decoded_field; |
|---|
| 521 |
return false; |
|---|
| 522 |
} |
|---|
| 523 |
decoded_field[decoded_len] = '\0'; |
|---|
| 524 |
const char* found_colon = strchr(decoded_field, ':'); |
|---|
| 525 |
if ( found_colon == NULL ) { |
|---|
| 526 |
delete [] decoded_field; |
|---|
| 527 |
return false; |
|---|
| 528 |
} |
|---|
| 529 |
*user = string(decoded_field, found_colon - decoded_field); |
|---|
| 530 |
*passwd = string(found_colon + 1, |
|---|
| 531 |
(decoded_field + decoded_len - found_colon - 1)); |
|---|
| 532 |
delete [] decoded_field; |
|---|
| 533 |
return true; |
|---|
| 534 |
} |
|---|
| 535 |
|
|---|
| 536 |
|
|---|
| 537 |
// Sets the Authorization header and sets the corresponding header |
|---|
| 538 |
// according to the specified parametes. |
|---|
| 539 |
// Returns false if an error occured (like the user contains : and other stuff) |
|---|
| 540 |
bool Header::SetAuthorizationField(const string& user, |
|---|
| 541 |
const string& passwd) { |
|---|
| 542 |
if ( user.find(':') != string::npos ) return false; |
|---|
| 543 |
const string to_encode(user + ":" + passwd); |
|---|
| 544 |
const int32 buflen = 2 * to_encode.size() + 4; |
|---|
| 545 |
char* encoded_header = new char[buflen]; |
|---|
| 546 |
base64::Encoder encoder; |
|---|
| 547 |
const int32 len = encoder.Encode(to_encode.c_str(), to_encode.size(), |
|---|
| 548 |
encoded_header, buflen); |
|---|
| 549 |
const int32 len2 = encoder.EncodeEnd(encoded_header + len); |
|---|
| 550 |
*(encoded_header + len + len2) = '\0'; |
|---|
| 551 |
bool ret = AddField(kHeaderAuthorization, sizeof(kHeaderAuthorization) - 1, |
|---|
| 552 |
string("Basic ") + encoded_header, |
|---|
| 553 |
true); |
|---|
| 554 |
delete [] encoded_header; |
|---|
| 555 |
return ret; |
|---|
| 556 |
} |
|---|
| 557 |
|
|---|
| 558 |
|
|---|
| 559 |
static const char kChunked[] = "chunked"; |
|---|
| 560 |
bool Header::IsChunkedTransfer() const { |
|---|
| 561 |
return strutil::StrCasePrefix( |
|---|
| 562 |
strutil::StrTrim(FindField(kHeaderTransferEncoding)).c_str(), |
|---|
| 563 |
kChunked); |
|---|
| 564 |
} |
|---|
| 565 |
void Header::SetChunkedTransfer(bool is_chunked) { |
|---|
| 566 |
if ( is_chunked ) { |
|---|
| 567 |
AddField(kHeaderTransferEncoding, kChunked, true); |
|---|
| 568 |
} else { |
|---|
| 569 |
ClearField(kHeaderTransferEncoding); |
|---|
| 570 |
} |
|---|
| 571 |
} |
|---|
| 572 |
|
|---|
| 573 |
static const char kGzip[] = "gzip"; |
|---|
| 574 |
static const char kDeflate[] = "deflate"; |
|---|
| 575 |
bool Header::IsGzipContentEncoding() const { |
|---|
| 576 |
return strutil::StrCasePrefix( |
|---|
| 577 |
strutil::StrTrim(FindField(kHeaderContentEncoding)).c_str(), |
|---|
| 578 |
kGzip); |
|---|
| 579 |
} |
|---|
| 580 |
bool Header::IsDeflateContentEncoding() const { |
|---|
| 581 |
return strutil::StrCasePrefix( |
|---|
| 582 |
strutil::StrTrim(FindField(kHeaderContentEncoding)).c_str(), |
|---|
| 583 |
kDeflate); |
|---|
| 584 |
} |
|---|
| 585 |
void Header::SetContentEncoding(const char* encoding) { |
|---|
| 586 |
if ( encoding ) { |
|---|
| 587 |
AddField(kHeaderContentEncoding, encoding, true); |
|---|
| 588 |
} else { |
|---|
| 589 |
ClearField(kHeaderContentEncoding); |
|---|
| 590 |
} |
|---|
| 591 |
} |
|---|
| 592 |
|
|---|
| 593 |
bool Header::IsKeepAliveConnection() const { |
|---|
| 594 |
return strutil::StrCasePrefix( |
|---|
| 595 |
strutil::StrTrim(FindField(kHeaderConnection)).c_str(), |
|---|
| 596 |
"keep-alive"); |
|---|
| 597 |
} |
|---|
| 598 |
|
|---|
| 599 |
|
|---|
| 600 |
// For format of these things please check: |
|---|
| 601 |
// |
|---|
| 602 |
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 |
|---|
| 603 |
// |
|---|
| 604 |
// Accept-Charset: iso-8859-5, unicode-1-1;q=0.8 |
|---|
| 605 |
// Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c |
|---|
| 606 |
// Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0 |
|---|
| 607 |
// |
|---|
| 608 |
float Header::GetHeaderAcceptance(const string& field, |
|---|
| 609 |
const string& value, |
|---|
| 610 |
const string& local_wildcard_value, |
|---|
| 611 |
const string& global_wildcard_value) const { |
|---|
| 612 |
int32 len; |
|---|
| 613 |
const char* s = FindField(field, &len); |
|---|
| 614 |
if ( !s ) { |
|---|
| 615 |
return 0.0f; |
|---|
| 616 |
} |
|---|
| 617 |
vector<string> components; |
|---|
| 618 |
strutil::SplitString(strutil::StrTrimCompress(string(s, len)), ",", |
|---|
| 619 |
&components); |
|---|
| 620 |
|
|---|
| 621 |
bool found_local_wildcard = false; |
|---|
| 622 |
float local_wildcard_pref = 0.0f; |
|---|
| 623 |
bool found_global_wildcard = false; |
|---|
| 624 |
float global_wildcard_pref = 0.0f; |
|---|
| 625 |
|
|---|
| 626 |
for ( size_t i = 0; i < components.size(); i++ ) { |
|---|
| 627 |
vector<string> specs; |
|---|
| 628 |
strutil::SplitString(components[i], ";", &specs); |
|---|
| 629 |
float crt_quality = 1.0f; |
|---|
| 630 |
for ( int i = 1; i < specs.size(); i++ ) { |
|---|
| 631 |
if ( strutil::StrPrefix(specs[i].c_str(), "q=") ) { |
|---|
| 632 |
crt_quality = strtof(specs[i].c_str() + 2, NULL); |
|---|
| 633 |
} |
|---|
| 634 |
} |
|---|
| 635 |
if ( strutil::StrCaseEqual(specs[0], value) ) { |
|---|
| 636 |
// We found the value - this is definitive .. |
|---|
| 637 |
return crt_quality; |
|---|
| 638 |
} else if ( !local_wildcard_value.empty() && |
|---|
| 639 |
strutil::StrCaseEqual(specs[0], local_wildcard_value) ) { |
|---|
| 640 |
found_local_wildcard = true; |
|---|
| 641 |
local_wildcard_pref = crt_quality; |
|---|
| 642 |
} else if ( !global_wildcard_value.empty() && |
|---|
| 643 |
strutil::StrCaseEqual(specs[0], global_wildcard_value) ) { |
|---|
| 644 |
found_global_wildcard = true; |
|---|
| 645 |
global_wildcard_pref = crt_quality; |
|---|
| 646 |
} |
|---|
| 647 |
} |
|---|
| 648 |
if ( found_local_wildcard ) return local_wildcard_pref; |
|---|
| 649 |
if ( found_global_wildcard ) return global_wildcard_pref; |
|---|
| 650 |
return 0.0f; |
|---|
| 651 |
} |
|---|
| 652 |
|
|---|
| 653 |
bool Header::IsGzipAcceptableEncoding() const { |
|---|
| 654 |
if ( http_version_ < VERSION_1_0 ) { |
|---|
| 655 |
return false; |
|---|
| 656 |
} |
|---|
| 657 |
return GetHeaderAcceptance(kHeaderAcceptEncoding, kGzip, "", "*") > 0.0f; |
|---|
| 658 |
} |
|---|
| 659 |
|
|---|
| 660 |
bool Header::IsDeflateAcceptableEncoding() const { |
|---|
| 661 |
if ( http_version_ < VERSION_1_0 ) { |
|---|
| 662 |
return false; |
|---|
| 663 |
} |
|---|
| 664 |
return GetHeaderAcceptance(kHeaderAcceptEncoding, kDeflate, "", "*") > 0.0f; |
|---|
| 665 |
} |
|---|
| 666 |
|
|---|
| 667 |
bool Header::IsZippableContentType() const { |
|---|
| 668 |
int len; |
|---|
| 669 |
const char* s = FindField(kHeaderContentType, &len); |
|---|
| 670 |
if ( !s ) { |
|---|
| 671 |
return false; |
|---|
| 672 |
} |
|---|
| 673 |
if ( strutil::StrCasePrefix(s, "text/") ) |
|---|
| 674 |
return true; |
|---|
| 675 |
if ( strutil::StrCasePrefix(s, "application/") ) |
|---|
| 676 |
return true; |
|---|
| 677 |
return false; |
|---|
| 678 |
} |
|---|
| 679 |
|
|---|
| 680 |
int64 Header::DefaultBodyLen() const { |
|---|
| 681 |
if ( first_line_type_ == REQUEST_LINE ) { |
|---|
| 682 |
if ( method_ == METHOD_PUT || |
|---|
| 683 |
method_ == METHOD_POST ) { |
|---|
| 684 |
return kMaxInt64; |
|---|
| 685 |
} |
|---|
| 686 |
} else if ( first_line_type_ == STATUS_LINE ) { |
|---|
| 687 |
return kMaxInt64; |
|---|
| 688 |
} |
|---|
| 689 |
return 0; |
|---|
| 690 |
} |
|---|
| 691 |
} |
|---|