| 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 "net/http/http_request.h" |
|---|
| 33 |
#include "common/base/errno.h" |
|---|
| 34 |
|
|---|
| 35 |
DEFINE_bool(http_log_detail_errors, |
|---|
| 36 |
true, |
|---|
| 37 |
"Log internal http errors"); |
|---|
| 38 |
namespace http { |
|---|
| 39 |
|
|---|
| 40 |
//////////////////////////////////////////////////////////////////////////////// |
|---|
| 41 |
|
|---|
| 42 |
Request::Request(bool strict_headers, |
|---|
| 43 |
common::ByteOrder client_byte_order, int32 client_block_size, |
|---|
| 44 |
common::ByteOrder server_byte_order, int32 server_block_size) |
|---|
| 45 |
: client_data_(client_byte_order, client_block_size), |
|---|
| 46 |
client_header_(strict_headers), |
|---|
| 47 |
server_data_(server_byte_order, server_block_size), |
|---|
| 48 |
server_header_(strict_headers), |
|---|
| 49 |
url_(NULL), |
|---|
| 50 |
in_chunk_encoding_(false), |
|---|
| 51 |
deflate_zwrapper_(NULL), |
|---|
| 52 |
gzip_state_begin_(true), |
|---|
| 53 |
gzip_zwrapper_(NULL), |
|---|
| 54 |
server_use_gzip_encoding_(true), |
|---|
| 55 |
compress_option_(COMPRESS_NONE) { |
|---|
| 56 |
} |
|---|
| 57 |
|
|---|
| 58 |
Request::~Request() { |
|---|
| 59 |
delete gzip_zwrapper_; |
|---|
| 60 |
delete deflate_zwrapper_; |
|---|
| 61 |
delete url_; |
|---|
| 62 |
} |
|---|
| 63 |
|
|---|
| 64 |
URL* Request::InitializeUrlFromClientRequest(const URL* absolute_root) { |
|---|
| 65 |
if ( url_ != NULL ) { |
|---|
| 66 |
delete url_; |
|---|
| 67 |
url_ = NULL; |
|---|
| 68 |
} |
|---|
| 69 |
if ( client_header_.parse_error() < http::Header::READ_NO_REQUEST_URI ) { |
|---|
| 70 |
url_ = new URL(absolute_root->Resolve(client_header_.uri())); |
|---|
| 71 |
} |
|---|
| 72 |
return url_; |
|---|
| 73 |
} |
|---|
| 74 |
|
|---|
| 75 |
void Request::AppendClientRequest(io::MemoryStream* out, int64 max_chunk_size) { |
|---|
| 76 |
CHECK(!in_chunk_encoding_); |
|---|
| 77 |
CHECK_EQ(client_header_.first_line_type(), http::Header::REQUEST_LINE) |
|---|
| 78 |
<< " Make sure you prepared your request properly !"; |
|---|
| 79 |
DCHECK_NE(client_header_.method(), METHOD_UNKNOWN); |
|---|
| 80 |
DCHECK_NE(client_header_.http_version(), VERSION_UNKNOWN); |
|---|
| 81 |
|
|---|
| 82 |
const bool zippable_content = client_header_.IsZippableContentType(); |
|---|
| 83 |
if ( client_header_.IsGzipContentEncoding() && zippable_content ) { |
|---|
| 84 |
CHECK_GE(client_header_.http_version(), VERSION_1_0); |
|---|
| 85 |
delete gzip_zwrapper_; |
|---|
| 86 |
gzip_state_begin_ = true; |
|---|
| 87 |
gzip_zwrapper_ = new io::ZlibGzipEncodeWrapper(); |
|---|
| 88 |
client_header_.SetContentEncoding("gzip"); |
|---|
| 89 |
compress_option_ = COMPRESS_GZIP; |
|---|
| 90 |
} else if ( client_header_.IsDeflateAcceptableEncoding() && |
|---|
| 91 |
zippable_content ) { |
|---|
| 92 |
CHECK_GE(client_header_.http_version(), VERSION_1_0); |
|---|
| 93 |
delete deflate_zwrapper_; |
|---|
| 94 |
deflate_zwrapper_ = new io::ZlibDeflateWrapper(); |
|---|
| 95 |
client_header_.SetContentEncoding("deflate"); |
|---|
| 96 |
compress_option_ = COMPRESS_DEFLATE; |
|---|
| 97 |
} else { |
|---|
| 98 |
client_header_.SetContentEncoding(NULL); |
|---|
| 99 |
compress_option_ = COMPRESS_NONE; |
|---|
| 100 |
} |
|---|
| 101 |
|
|---|
| 102 |
if ( server_use_gzip_encoding_ ) { |
|---|
| 103 |
// We can accept gzip, and deflate but not much else.. |
|---|
| 104 |
client_header_.AddField(kHeaderAcceptEncoding, "gzip, deflate", true); |
|---|
| 105 |
} else { |
|---|
| 106 |
client_header_.ClearField(kHeaderAcceptEncoding); |
|---|
| 107 |
} |
|---|
| 108 |
|
|---|
| 109 |
if ( client_header_.IsChunkedTransfer() ) { |
|---|
| 110 |
in_chunk_encoding_ = true; |
|---|
| 111 |
CHECK_GE(client_header_.http_version(), VERSION_1_1); |
|---|
| 112 |
client_header_.AppendToStream(out); |
|---|
| 113 |
while ( !client_data_.IsEmpty() ) { |
|---|
| 114 |
AppendClientChunk(out, max_chunk_size); |
|---|
| 115 |
} |
|---|
| 116 |
} else { |
|---|
| 117 |
// We do not know anything else - clear the transfer encoding |
|---|
| 118 |
client_header_.SetChunkedTransfer(false); |
|---|
| 119 |
// Append the body (compressed if necessary) to a temp |
|---|
| 120 |
io::MemoryStream ms; |
|---|
| 121 |
io::MemoryStream* source = &ms; |
|---|
| 122 |
switch ( compress_option_ ) { |
|---|
| 123 |
case COMPRESS_GZIP: |
|---|
| 124 |
gzip_zwrapper_->Encode(&client_data_, out); |
|---|
| 125 |
break; |
|---|
| 126 |
case COMPRESS_DEFLATE: |
|---|
| 127 |
deflate_zwrapper_->Deflate(&client_data_, out); |
|---|
| 128 |
break; |
|---|
| 129 |
case COMPRESS_NONE: |
|---|
| 130 |
source = &client_data_; |
|---|
| 131 |
break; // no temp compression |
|---|
| 132 |
} |
|---|
| 133 |
// Set the content length from the data size |
|---|
| 134 |
if ( !client_data_.IsEmpty() || |
|---|
| 135 |
client_header_.method() == METHOD_POST || |
|---|
| 136 |
client_header_.method() == METHOD_PUT ) { |
|---|
| 137 |
client_header_.AddField( |
|---|
| 138 |
kHeaderContentLength, |
|---|
| 139 |
strutil::IntToString(client_data_.Size()), |
|---|
| 140 |
true); // replace ! |
|---|
| 141 |
} |
|---|
| 142 |
// Append the header data and the body |
|---|
| 143 |
client_header_.AppendToStream(out); |
|---|
| 144 |
out->MarkerSet(); |
|---|
| 145 |
out->MarkerRestore(); |
|---|
| 146 |
out->AppendStream(source); |
|---|
| 147 |
} |
|---|
| 148 |
} |
|---|
| 149 |
|
|---|
| 150 |
void Request::AppendServerReply(io::MemoryStream* out, |
|---|
| 151 |
bool streaming, |
|---|
| 152 |
bool do_chunks, |
|---|
| 153 |
int64 max_chunk_size) { |
|---|
| 154 |
CHECK(!in_chunk_encoding_); |
|---|
| 155 |
CHECK_EQ(server_header_.first_line_type(), http::Header::STATUS_LINE) |
|---|
| 156 |
<< " Make sure you prepared your request properly !"; |
|---|
| 157 |
DCHECK_NE(server_header_.status_code(), UNKNOWN); |
|---|
| 158 |
if ( server_header_.http_version() == VERSION_UNKNOWN ) { |
|---|
| 159 |
server_header_.set_http_version(VERSION_1_1); |
|---|
| 160 |
} |
|---|
| 161 |
|
|---|
| 162 |
if ( server_header_.http_version() > client_header_.http_version() ) { |
|---|
| 163 |
LOG_WARNING << " Downgrading server verion: " |
|---|
| 164 |
<< GetHttpVersionName(client_header_.http_version()); |
|---|
| 165 |
server_header_.set_http_version(VERSION_1_0); |
|---|
| 166 |
} |
|---|
| 167 |
const bool zippable_content = server_header_.IsZippableContentType(); |
|---|
| 168 |
if ( server_use_gzip_encoding_ && zippable_content ) { |
|---|
| 169 |
if ( client_header_.IsGzipAcceptableEncoding() ) { |
|---|
| 170 |
delete gzip_zwrapper_; |
|---|
| 171 |
gzip_zwrapper_ = new io::ZlibGzipEncodeWrapper(); |
|---|
| 172 |
server_header_.SetContentEncoding("gzip"); |
|---|
| 173 |
compress_option_ = COMPRESS_GZIP; |
|---|
| 174 |
} else if ( client_header_.IsDeflateAcceptableEncoding() ) { |
|---|
| 175 |
delete deflate_zwrapper_; |
|---|
| 176 |
deflate_zwrapper_ = new io::ZlibDeflateWrapper(); |
|---|
| 177 |
server_header_.SetContentEncoding("deflate"); |
|---|
| 178 |
compress_option_ = COMPRESS_DEFLATE; |
|---|
| 179 |
} else { |
|---|
| 180 |
server_header_.SetContentEncoding(NULL); |
|---|
| 181 |
} |
|---|
| 182 |
} else { |
|---|
| 183 |
server_header_.SetContentEncoding(NULL); |
|---|
| 184 |
compress_option_ = COMPRESS_NONE; |
|---|
| 185 |
} |
|---|
| 186 |
|
|---|
| 187 |
if ( streaming ) { |
|---|
| 188 |
if (do_chunks && client_header_.http_version() >= VERSION_1_1) { |
|---|
| 189 |
server_header_.SetChunkedTransfer(true); |
|---|
| 190 |
CHECK_GT(client_header_.http_version(), VERSION_1_0); |
|---|
| 191 |
} else { |
|---|
| 192 |
server_header_.SetChunkedTransfer(false); |
|---|
| 193 |
} |
|---|
| 194 |
server_header_.AppendToStream(out); |
|---|
| 195 |
if ( !NoServerBodyTransmitted() ) { |
|---|
| 196 |
in_chunk_encoding_ = true; |
|---|
| 197 |
while ( !server_data_.IsEmpty() ) { |
|---|
| 198 |
// Let them close the chunks by themselves |
|---|
| 199 |
AppendServerChunk(out, do_chunks, max_chunk_size); |
|---|
| 200 |
} |
|---|
| 201 |
} |
|---|
| 202 |
} else { |
|---|
| 203 |
server_header_.SetChunkedTransfer(false); |
|---|
| 204 |
const HttpReturnCode code = server_header_.status_code(); |
|---|
| 205 |
io::MemoryStream ms; |
|---|
| 206 |
io::MemoryStream* source = &ms; |
|---|
| 207 |
// Append the body (compressed if necessary) to a temp |
|---|
| 208 |
switch ( compress_option_ ) { |
|---|
| 209 |
case COMPRESS_GZIP: |
|---|
| 210 |
gzip_zwrapper_->Encode(&server_data_, &ms); |
|---|
| 211 |
break; |
|---|
| 212 |
case COMPRESS_DEFLATE: |
|---|
| 213 |
deflate_zwrapper_->Deflate(&server_data_, &ms); |
|---|
| 214 |
break; |
|---|
| 215 |
case COMPRESS_NONE: |
|---|
| 216 |
source = &server_data_; |
|---|
| 217 |
break; |
|---|
| 218 |
} |
|---|
| 219 |
// Set the content length from the compressed stuff. |
|---|
| 220 |
if ( !( (code >= 100 && code < 200) || |
|---|
| 221 |
code == NO_CONTENT || code == NOT_MODIFIED) ) { |
|---|
| 222 |
server_header_.AddField(kHeaderContentLength, |
|---|
| 223 |
strutil::IntToString(source->Size()), |
|---|
| 224 |
true); // replace ! |
|---|
| 225 |
} |
|---|
| 226 |
// Append the header data and the body |
|---|
| 227 |
server_header_.AppendToStream(out); |
|---|
| 228 |
out->AppendStream(source); |
|---|
| 229 |
} |
|---|
| 230 |
} |
|---|
| 231 |
|
|---|
| 232 |
namespace { |
|---|
| 233 |
static void BufferAppendChunk(io::MemoryStream* in, |
|---|
| 234 |
io::MemoryStream* out, |
|---|
| 235 |
bool add_decorations, |
|---|
| 236 |
int32 max_size) { |
|---|
| 237 |
int32 size = min(max_size, in->Size()); |
|---|
| 238 |
if ( add_decorations ) { |
|---|
| 239 |
string first_line = strutil::StringPrintf("0%x\r\n", |
|---|
| 240 |
static_cast<int>(size)); |
|---|
| 241 |
out->Write(first_line); |
|---|
| 242 |
} |
|---|
| 243 |
out->AppendStream(in, size); |
|---|
| 244 |
if ( add_decorations ) { |
|---|
| 245 |
out->Write("\r\n"); |
|---|
| 246 |
} |
|---|
| 247 |
} |
|---|
| 248 |
} |
|---|
| 249 |
|
|---|
| 250 |
bool Request::AppendClientChunk(io::MemoryStream* out, |
|---|
| 251 |
int64 max_chunk_size) { |
|---|
| 252 |
return AppendChunkHelper( |
|---|
| 253 |
&client_header_, &client_data_, out, |
|---|
| 254 |
client_header_.http_version() >= VERSION_1_1, max_chunk_size); |
|---|
| 255 |
} |
|---|
| 256 |
bool Request::AppendServerChunk(io::MemoryStream* out, |
|---|
| 257 |
bool do_chunks, |
|---|
| 258 |
int64 max_chunk_size) { |
|---|
| 259 |
CHECK(!NoServerBodyTransmitted()) |
|---|
| 260 |
<< " Code:" << server_header_.status_code() |
|---|
| 261 |
<< " Method:" << client_header_.method(); |
|---|
| 262 |
return AppendChunkHelper(&server_header_, &server_data_, out, |
|---|
| 263 |
do_chunks && (client_header_.http_version() >= VERSION_1_1), |
|---|
| 264 |
max_chunk_size); |
|---|
| 265 |
} |
|---|
| 266 |
|
|---|
| 267 |
bool Request::AppendChunkHelper(const http::Header* src_header, |
|---|
| 268 |
io::MemoryStream* src_data, |
|---|
| 269 |
io::MemoryStream* out, |
|---|
| 270 |
bool add_decorations, |
|---|
| 271 |
int64 max_chunk_size) { |
|---|
| 272 |
const bool is_empty = src_data->IsEmpty(); |
|---|
| 273 |
if ( compress_option_ != COMPRESS_NONE ) { |
|---|
| 274 |
io::MemoryStream tmp; |
|---|
| 275 |
if ( compress_option_ == COMPRESS_GZIP ) { |
|---|
| 276 |
if ( gzip_state_begin_ ) { |
|---|
| 277 |
gzip_zwrapper_->BeginEncoding(&tmp); |
|---|
| 278 |
gzip_state_begin_ = false; |
|---|
| 279 |
} |
|---|
| 280 |
gzip_zwrapper_->ContinueEncoding(src_data, &tmp); |
|---|
| 281 |
if ( is_empty ) { |
|---|
| 282 |
gzip_zwrapper_->EndEncoding(&tmp); |
|---|
| 283 |
} |
|---|
| 284 |
} else { |
|---|
| 285 |
int32 size = src_data->Size(); |
|---|
| 286 |
if ( !is_empty ) size++; // basically do not flush the state .. |
|---|
| 287 |
deflate_zwrapper_->DeflateSize(src_data, &tmp, &size); |
|---|
| 288 |
CHECK(size == 1 || (size == 0 && is_empty)) |
|---|
| 289 |
<< "Strange zlib behaviour. Left size: " << size; |
|---|
| 290 |
} |
|---|
| 291 |
if ( tmp.IsEmpty() ) { |
|---|
| 292 |
if ( is_empty ) { |
|---|
| 293 |
// Turns out that the flush resulted in an empty body.. |
|---|
| 294 |
in_chunk_encoding_ = false; |
|---|
| 295 |
if ( add_decorations ) { |
|---|
| 296 |
out->Write("0\r\n\r\n"); |
|---|
| 297 |
} |
|---|
| 298 |
return true; |
|---|
| 299 |
} else { |
|---|
| 300 |
// The zlib did not produce anything this call - just skip it for |
|---|
| 301 |
// the next time ... |
|---|
| 302 |
return false; |
|---|
| 303 |
} |
|---|
| 304 |
} else { |
|---|
| 305 |
max_chunk_size = (max_chunk_size <= 0) ? kMaxInt32 : max_chunk_size; |
|---|
| 306 |
while (!tmp.IsEmpty()) { |
|---|
| 307 |
BufferAppendChunk(&tmp, out, add_decorations, max_chunk_size); |
|---|
| 308 |
} |
|---|
| 309 |
if ( is_empty ) { |
|---|
| 310 |
// If it was a flush, put the end of stream out.. |
|---|
| 311 |
in_chunk_encoding_ = false; |
|---|
| 312 |
if ( add_decorations ) { |
|---|
| 313 |
out->Write("0\r\n\r\n"); |
|---|
| 314 |
} |
|---|
| 315 |
return true; |
|---|
| 316 |
} |
|---|
| 317 |
} |
|---|
| 318 |
} else { |
|---|
| 319 |
// The last chunk (empty) => append empty trailed |
|---|
| 320 |
if ( is_empty ) { |
|---|
| 321 |
in_chunk_encoding_ = false; |
|---|
| 322 |
if ( add_decorations ) { |
|---|
| 323 |
out->Write("0\r\n\r\n"); |
|---|
| 324 |
} |
|---|
| 325 |
return true; |
|---|
| 326 |
} else { |
|---|
| 327 |
max_chunk_size = (max_chunk_size <= 0) ? kMaxInt32 : max_chunk_size; |
|---|
| 328 |
while (!src_data->IsEmpty()) { |
|---|
| 329 |
BufferAppendChunk(src_data, out, add_decorations, max_chunk_size); |
|---|
| 330 |
} |
|---|
| 331 |
return false; |
|---|
| 332 |
} |
|---|
| 333 |
} |
|---|
| 334 |
return false; |
|---|
| 335 |
} |
|---|
| 336 |
|
|---|
| 337 |
//////////////////////////////////////////////////////////////////////////////// |
|---|
| 338 |
|
|---|
| 339 |
#define LOG_HTTP LOG_INFO_IF(dlog_level_) << name() << ": " |
|---|
| 340 |
#define LOG_HTTP_ERR LOG_ERROR_IF(FLAGS_http_log_detail_errors) \ |
|---|
| 341 |
<< name() << ": " |
|---|
| 342 |
|
|---|
| 343 |
RequestParser::RequestParser( |
|---|
| 344 |
const char* name, |
|---|
| 345 |
int32 max_header_size, |
|---|
| 346 |
int64 max_body_size, |
|---|
| 347 |
int64 max_chunk_size, |
|---|
| 348 |
int64 max_num_chunks, |
|---|
| 349 |
bool accept_wrong_method, |
|---|
| 350 |
bool accept_wrong_version, |
|---|
| 351 |
bool accept_no_content_length, |
|---|
| 352 |
http::Header::ParseError worst_accepted_header_error) |
|---|
| 353 |
: max_header_size_(max_header_size), |
|---|
| 354 |
max_body_size_(max_body_size), |
|---|
| 355 |
max_chunk_size_(max_chunk_size), |
|---|
| 356 |
max_num_chunks_(max_num_chunks), |
|---|
| 357 |
accept_wrong_method_(accept_wrong_method), |
|---|
| 358 |
accept_wrong_version_(accept_wrong_version), |
|---|
| 359 |
accept_no_content_length_(accept_no_content_length), |
|---|
| 360 |
worst_accepted_header_error_(worst_accepted_header_error), |
|---|
| 361 |
name_(name), |
|---|
| 362 |
dlog_level_(false), |
|---|
| 363 |
inflate_zwrapper_(NULL), |
|---|
| 364 |
gzip_zwrapper_(NULL) { |
|---|
| 365 |
Clear(); |
|---|
| 366 |
} |
|---|
| 367 |
|
|---|
| 368 |
RequestParser::~RequestParser() { |
|---|
| 369 |
Clear(); |
|---|
| 370 |
} |
|---|
| 371 |
|
|---|
| 372 |
const char* RequestParser::ParseStateName(ParseState state) { |
|---|
| 373 |
switch ( state ) { |
|---|
| 374 |
CONSIDER(STATE_INITIALIZED); |
|---|
| 375 |
CONSIDER(STATE_HEADER_READING); |
|---|
| 376 |
CONSIDER(STATE_END_OF_HEADER); |
|---|
| 377 |
CONSIDER(STATE_END_OF_HEADER_FINAL); |
|---|
| 378 |
CONSIDER(STATE_BODY_READING); |
|---|
| 379 |
CONSIDER(STATE_BODY_END); |
|---|
| 380 |
CONSIDER(STATE_CHUNK_HEAD_READING); |
|---|
| 381 |
CONSIDER(STATE_CHUNK_READING); |
|---|
| 382 |
CONSIDER(STATE_END_OF_CHUNK); |
|---|
| 383 |
CONSIDER(STATE_LAST_CHUNK_READ); |
|---|
| 384 |
CONSIDER(STATE_END_OF_TRAIL_HEADER); |
|---|
| 385 |
CONSIDER(ERROR_HEADER_BAD); |
|---|
| 386 |
CONSIDER(ERROR_HEADER_BAD_CONTENT_LEN); |
|---|
| 387 |
CONSIDER(ERROR_HEADER_TOO_LONG); |
|---|
| 388 |
CONSIDER(ERROR_HEADER_LINE); |
|---|
| 389 |
CONSIDER(ERROR_CONTENT_TOO_LONG); |
|---|
| 390 |
CONSIDER(ERROR_TRANSFER_ENCODING_UNKNOWN); |
|---|
| 391 |
CONSIDER(ERROR_CONTENT_ENCODING_UNKNOWN); |
|---|
| 392 |
CONSIDER(ERROR_CONTENT_GZIP_TOO_LONG); |
|---|
| 393 |
CONSIDER(ERROR_CONTENT_GZIP_ERROR); |
|---|
| 394 |
CONSIDER(ERROR_CONTENT_GZIP_UNFINISHED); |
|---|
| 395 |
CONSIDER(ERROR_CHUNK_HEADER_TOO_LONG); |
|---|
| 396 |
CONSIDER(ERROR_CHUNK_TOO_LONG); |
|---|
| 397 |
CONSIDER(ERROR_CHUNK_TOO_MANY); |
|---|
| 398 |
CONSIDER(ERROR_CHUNK_TRAIL_HEADER); |
|---|
| 399 |
CONSIDER(ERROR_CHUNK_BAD_CHUNK_LENGTH); |
|---|
| 400 |
CONSIDER(ERROR_CHUNK_BAD_CHUNK_TERMINATION); |
|---|
| 401 |
CONSIDER(ERROR_CHUNK_BIGGER_THEN_DECLARED); |
|---|
| 402 |
CONSIDER(ERROR_CHUNK_UNFINISHED_GZIP_CONTENT); |
|---|
| 403 |
CONSIDER(ERROR_CHUNK_CONTINUED_FINISHED_GZIP_CONTENT); |
|---|
| 404 |
CONSIDER(ERROR_CHUNK_CONTENT_GZIP_TOO_LONG); |
|---|
| 405 |
CONSIDER(ERROR_CHUNK_CONTENT_GZIP_ERROR); |
|---|
| 406 |
CONSIDER(ERROR_CHUNK_TRAILER_TOO_LONG); |
|---|
| 407 |
// Not real states: |
|---|
| 408 |
// CONSIDER(FIRST_FINAL_STATE); |
|---|
| 409 |
// CONSIDER(FIRST_ERROR_STATE); |
|---|
| 410 |
default: |
|---|
| 411 |
return "INVALD_STATE"; |
|---|
| 412 |
} |
|---|
| 413 |
return "INVALID_STATE"; // keep g++ happy |
|---|
| 414 |
} |
|---|
| 415 |
|
|---|
| 416 |
string RequestParser::ReadStateName(int32 read_state) { |
|---|
| 417 |
vector<string> states; |
|---|
| 418 |
if ( read_state & HEADER_READ ) { |
|---|
| 419 |
states.push_back("HEADER_READ"); |
|---|
| 420 |
} |
|---|
| 421 |
if ( read_state & BODY_READING ) { |
|---|
| 422 |
states.push_back("BODY_READING"); |
|---|
| 423 |
} |
|---|
| 424 |
if ( read_state & CHUNKED_BODY_READING ) { |
|---|
| 425 |
states.push_back("CHUNKED_BODY_READING"); |
|---|
| 426 |
} |
|---|
| 427 |
if ( read_state & CHUNKED_TRAILER_READING ) { |
|---|
| 428 |
states.push_back("CHUNKED_TRAILER_READING"); |
|---|
| 429 |
} |
|---|
| 430 |
if ( read_state & BODY_FINISHED ) { |
|---|
| 431 |
states.push_back("BODY_FINISHED"); |
|---|
| 432 |
} |
|---|
| 433 |
if ( read_state & CHUNKS_FINISHED ) { |
|---|
| 434 |
states.push_back("CHUNKS_FINISHED"); |
|---|
| 435 |
} |
|---|
| 436 |
if ( read_state & REQUEST_FINISHED ) { |
|---|
| 437 |
states.push_back("REQUEST_FINISHED"); |
|---|
| 438 |
} |
|---|
| 439 |
if ( states.empty() ) return "NONE"; |
|---|
| 440 |
return strutil::JoinStrings(states, " | "); |
|---|
| 441 |
} |
|---|
| 442 |
|
|---|
| 443 |
// Call this before starting to parse a new request - and you better do it ! |
|---|
| 444 |
void RequestParser::Clear() { |
|---|
| 445 |
LOG_HTTP << " Clear parse state."; |
|---|
| 446 |
parse_state_ = STATE_INITIALIZED; |
|---|
| 447 |
body_size_to_read_ = 0LL; |
|---|
| 448 |
chunk_size_to_read_ = 0LL; |
|---|
| 449 |
num_chunks_read_ = 0; |
|---|
| 450 |
next_chunk_expectation_ = EXPECT_CHUNK_NONE; |
|---|
| 451 |
partial_data_.Clear(); |
|---|
| 452 |
trail_header_.Clear(); |
|---|
| 453 |
delete inflate_zwrapper_; |
|---|
| 454 |
inflate_zwrapper_ = NULL; |
|---|
| 455 |
delete gzip_zwrapper_; |
|---|
| 456 |
gzip_zwrapper_ = NULL; |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
////////////////////////////////////////////////////////////////////// |
|---|
| 460 |
// |
|---|
| 461 |
// ParseClientRequest |
|---|
| 462 |
// |
|---|
| 463 |
int32 RequestParser::ParseClientRequest(io::MemoryStream* in, Request* req) { |
|---|
| 464 |
CHECK(!InFinalState()); |
|---|
| 465 |
if ( parse_state_ == STATE_INITIALIZED ) { |
|---|
| 466 |
// CONTINUE - Initial state |
|---|
| 467 |
req->client_data()->Clear(); |
|---|
| 468 |
req->client_header()->Clear(); |
|---|
| 469 |
set_parse_state(STATE_HEADER_READING); |
|---|
| 470 |
} |
|---|
| 471 |
|
|---|
| 472 |
////////////////////////////////////////////////////////////////////// |
|---|
| 473 |
// |
|---|
| 474 |
// Header reading for client |
|---|
| 475 |
// |
|---|
| 476 |
if ( parse_state_ == STATE_HEADER_READING ) { |
|---|
| 477 |
if ( !req->client_header()->ParseHttpRequest(in) ) { |
|---|
| 478 |
if ( in->Size() + req->client_header()->bytes_parsed() > |
|---|
| 479 |
max_header_size_ ) { |
|---|
| 480 |
// ERROR - header too big |
|---|
| 481 |
LOG_HTTP_ERR << " Header too long. Got at least: " |
|---|
| 482 |
<< in->Size() + req->client_header()->bytes_parsed() |
|---|
| 483 |
<< " max: " << max_header_size_; |
|---|
| 484 |
set_parse_state(ERROR_HEADER_TOO_LONG); |
|---|
| 485 |
return REQUEST_FINISHED; |
|---|
| 486 |
} |
|---|
| 487 |
// Clear any error w/ the first line |
|---|
| 488 |
req->server_header()->set_first_line_type(Header::REQUEST_LINE); |
|---|
| 489 |
// CONTINUE - with header parsing next call |
|---|
| 490 |
return 0; |
|---|
| 491 |
} else if ( req->client_header()->bytes_parsed() > max_header_size_ ) { |
|---|
| 492 |
// ERROR - header too big |
|---|
| 493 |
LOG_HTTP_ERR << " Header too long. Parsed: " |
|---|
| 494 |
<< in->Size() + req->client_header()->bytes_parsed() |
|---|
| 495 |
<< " max: " << max_header_size_; |
|---|
| 496 |
set_parse_state(ERROR_HEADER_TOO_LONG); |
|---|
| 497 |
return REQUEST_FINISHED; |
|---|
| 498 |
} |
|---|
| 499 |
// Header parsed at this point (w/ errors or not..) |
|---|
| 500 |
if ( (req->client_header()->http_version() == VERSION_UNKNOWN && |
|---|
| 501 |
!accept_wrong_version_) || |
|---|
| 502 |
(req->client_header()->method() == METHOD_UNKNOWN && |
|---|
| 503 |
!accept_wrong_method_) ) { |
|---|
| 504 |
// ERROR - Invalid data in the first line (and unaccepted) |
|---|
| 505 |
LOG_HTTP_ERR << " Invalid Header line. Method: " |
|---|
| 506 |
<< req->client_header()->method() |
|---|
| 507 |
<< " Version: " << req->client_header()->http_version(); |
|---|
| 508 |
set_parse_state(ERROR_HEADER_LINE); |
|---|
| 509 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 510 |
} |
|---|
| 511 |
if ( req->client_header()->parse_error() > worst_accepted_header_error_ ) { |
|---|
| 512 |
// ERROR - unacceptale error in header parsing |
|---|
| 513 |
LOG_HTTP_ERR << " Bad Header Found: " |
|---|
| 514 |
<< req->client_header()->ParseErrorName(); |
|---|
| 515 |
set_parse_state(ERROR_HEADER_BAD); |
|---|
| 516 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 517 |
} |
|---|
| 518 |
// CONTINUE - header parsed good enough |
|---|
| 519 |
set_parse_state(STATE_END_OF_HEADER); |
|---|
| 520 |
// Give guys a chance to check things after the header was parsed.. |
|---|
| 521 |
return HEADER_READ | CONTINUE; |
|---|
| 522 |
} |
|---|
| 523 |
// CONTINUE - w/ the parsing of the message payload |
|---|
| 524 |
return ParsePayloadInternal(in, req->client_header(), req->client_data()); |
|---|
| 525 |
} |
|---|
| 526 |
|
|---|
| 527 |
|
|---|
| 528 |
////////////////////////////////////////////////////////////////////// |
|---|
| 529 |
// |
|---|
| 530 |
// ParseServerReply |
|---|
| 531 |
// |
|---|
| 532 |
int32 RequestParser::ParseServerReply(io::MemoryStream* in, Request* req) { |
|---|
| 533 |
CHECK(!InFinalState()); |
|---|
| 534 |
if ( parse_state_ == STATE_INITIALIZED ) { |
|---|
| 535 |
// CONTINUE - Initial state |
|---|
| 536 |
req->server_data()->Clear(); |
|---|
| 537 |
req->server_header()->Clear(); |
|---|
| 538 |
set_parse_state(STATE_HEADER_READING); |
|---|
| 539 |
} |
|---|
| 540 |
////////////////////////////////////////////////////////////////////// |
|---|
| 541 |
// |
|---|
| 542 |
// Header reading for server |
|---|
| 543 |
// |
|---|
| 544 |
if ( parse_state_ == STATE_HEADER_READING ) { |
|---|
| 545 |
if ( !req->server_header()->ParseHttpReply(in) ) { |
|---|
| 546 |
if ( in->Size() + req->server_header()->bytes_parsed() > |
|---|
| 547 |
max_header_size_ ) { |
|---|
| 548 |
// ERROR - header too big |
|---|
| 549 |
LOG_HTTP_ERR << " Header too long. Got at least: " |
|---|
| 550 |
<< in->Size() + req->server_header()->bytes_parsed() |
|---|
| 551 |
<< " on max: " << max_header_size_; |
|---|
| 552 |
set_parse_state(ERROR_HEADER_TOO_LONG); |
|---|
| 553 |
return REQUEST_FINISHED; |
|---|
| 554 |
} |
|---|
| 555 |
// Clear any error w/ the first line |
|---|
| 556 |
req->server_header()->set_first_line_type(Header::STATUS_LINE); |
|---|
| 557 |
// CONTINUE - with header parsing next call |
|---|
| 558 |
return 0; |
|---|
| 559 |
} else if ( req->server_header()->bytes_parsed() > max_header_size_ ) { |
|---|
| 560 |
// ERROR - header too big |
|---|
| 561 |
LOG_HTTP_ERR << " Header too long. Parsed: " |
|---|
| 562 |
<< req->server_header()->bytes_parsed() |
|---|
| 563 |
<< " on max: " << max_header_size_; |
|---|
| 564 |
set_parse_state(ERROR_HEADER_TOO_LONG); |
|---|
| 565 |
return REQUEST_FINISHED; |
|---|
| 566 |
} |
|---|
| 567 |
if ( req->server_header()->parse_error() > worst_accepted_header_error_ ) { |
|---|
| 568 |
// ERROR - unacceptable error in the header |
|---|
| 569 |
LOG_HTTP_ERR << " Error in headers found: " |
|---|
| 570 |
<< req->client_header()->ParseErrorName(); |
|---|
| 571 |
set_parse_state(ERROR_HEADER_BAD); |
|---|
| 572 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 573 |
} |
|---|
| 574 |
if ( req->NoServerBodyTransmitted() ) { |
|---|
| 575 |
// FINAL - We don't have a body in these cases ! |
|---|
| 576 |
set_parse_state(STATE_END_OF_HEADER_FINAL); |
|---|
| 577 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 578 |
} |
|---|
| 579 |
// TODO(cpopescu): "multipart/byteranges" |
|---|
| 580 |
|
|---|
| 581 |
// CONTINUE - header parsed is good enough |
|---|
| 582 |
set_parse_state(STATE_END_OF_HEADER); |
|---|
| 583 |
|
|---|
| 584 |
// Give guys a chance to check things after the header was parsed.. |
|---|
| 585 |
return HEADER_READ | CONTINUE; |
|---|
| 586 |
} |
|---|
| 587 |
// CONTINUE - w/ the parsing of the message payload |
|---|
| 588 |
return ParsePayloadInternal(in, req->server_header(), req->server_data()); |
|---|
| 589 |
} |
|---|
| 590 |
|
|---|
| 591 |
////////////////////////////////////////////////////////////////////// |
|---|
| 592 |
// |
|---|
| 593 |
// ParsePayloadInternal - parses the message payload (body) |
|---|
| 594 |
// |
|---|
| 595 |
int32 RequestParser::ParsePayloadInternal(io::MemoryStream* in, |
|---|
| 596 |
http::Header* header, |
|---|
| 597 |
io::MemoryStream* out) { |
|---|
| 598 |
CHECK_GE(parse_state_, STATE_END_OF_HEADER); |
|---|
| 599 |
|
|---|
| 600 |
////////////////////////////////////////////////////////////////////// |
|---|
| 601 |
// |
|---|
| 602 |
// Detrmine the trasmission mode for the body |
|---|
| 603 |
// |
|---|
| 604 |
if ( parse_state_ == STATE_END_OF_HEADER ) { |
|---|
| 605 |
if ( !IsKnownContentEncoding(header) ) { |
|---|
| 606 |
// ERROR - we don't know this content encoding (identity & gzip accepted) |
|---|
| 607 |
LOG_HTTP_ERR << " Unknown content encoding in header: " |
|---|
| 608 |
<< header->ToString(); |
|---|
| 609 |
set_parse_state(ERROR_CONTENT_ENCODING_UNKNOWN); |
|---|
| 610 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 611 |
} |
|---|
| 612 |
if ( !IsKnownTransferEncoding(header) ) { |
|---|
| 613 |
// ERROR - we don't know this transfer encoding |
|---|
| 614 |
// (identity & chunk accepted) |
|---|
| 615 |
LOG_HTTP_ERR << " Unknown transfer encoding in header: " |
|---|
| 616 |
<< header->ToString(); |
|---|
| 617 |
set_parse_state(ERROR_TRANSFER_ENCODING_UNKNOWN); |
|---|
| 618 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 619 |
} |
|---|
| 620 |
if ( !header->IsChunkedTransfer() ) { |
|---|
| 621 |
// Identity transder encoding => expect Content Lenght (in this state) |
|---|
| 622 |
string content_length_str; |
|---|
| 623 |
if ( !header->FindField(kHeaderContentLength, &content_length_str) ) { |
|---|
| 624 |
body_size_to_read_ = header->DefaultBodyLen(); |
|---|
| 625 |
if ( !accept_no_content_length_ && body_size_to_read_ > 0 ) { |
|---|
| 626 |
// ERROR - There is no content length header |
|---|
| 627 |
set_parse_state(STATE_END_OF_HEADER_FINAL); |
|---|
| 628 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 629 |
} |
|---|
| 630 |
if (max_body_size_ >= 0) { |
|---|
| 631 |
if ( body_size_to_read_ > max_body_size_ ) { |
|---|
| 632 |
body_size_to_read_ = max_body_size_; |
|---|
| 633 |
} |
|---|
| 634 |
} |
|---|
| 635 |
// CONTINUE -> read as much as we consider it makes sense.. |
|---|
| 636 |
set_parse_state(STATE_BODY_READING); |
|---|
| 637 |
} else { |
|---|
| 638 |
errno = 0; // essential as strtol would not set a 0 errno |
|---|
| 639 |
const int32 content_length = strtol(content_length_str.c_str(), |
|---|
| 640 |
NULL, 10); |
|---|
| 641 |
if ( errno || content_length < 0 ) { |
|---|
| 642 |
LOG_HTTP << " Content-Length found: [" << content_length_str << "]."; |
|---|
| 643 |
// ERROR - Badly specified content length |
|---|
| 644 |
LOG_HTTP_ERR << " Bad Content Length in header: [" |
|---|
| 645 |
<< content_length_str << "]"; |
|---|
| 646 |
set_parse_state(ERROR_HEADER_BAD_CONTENT_LEN); |
|---|
| 647 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 648 |
} |
|---|
| 649 |
// CONTINUE -> expect identity encoded transfer w/ a content length |
|---|
| 650 |
body_size_to_read_ = static_cast<int64>(content_length); |
|---|
| 651 |
set_parse_state(STATE_BODY_READING); |
|---|
| 652 |
} |
|---|
| 653 |
} else { |
|---|
| 654 |
// CONTINUE -> expect chunked encoding transfer |
|---|
| 655 |
set_parse_state(STATE_CHUNK_HEAD_READING); |
|---|
| 656 |
} |
|---|
| 657 |
} |
|---|
| 658 |
|
|---|
| 659 |
if ( parse_state_ == STATE_BODY_READING ) { |
|---|
| 660 |
// CONTINUE - Body reading - regular - Content-Length specified |
|---|
| 661 |
const int32 result = ParseBodyInternal(in, header, out); |
|---|
| 662 |
return result; |
|---|
| 663 |
} |
|---|
| 664 |
// We better be in a chunks reading state :) |
|---|
| 665 |
CHECK_GE(parse_state_, STATE_CHUNK_HEAD_READING) |
|---|
| 666 |
<< " In error state - this is a bug: " << ParseStateName(); |
|---|
| 667 |
return ParseChunksInternal(in, header, out); |
|---|
| 668 |
} |
|---|
| 669 |
|
|---|
| 670 |
////////////////////////////////////////////////////////////////////// |
|---|
| 671 |
// |
|---|
| 672 |
// ParseBodyInternal - parses the body for non chunked transfers |
|---|
| 673 |
// |
|---|
| 674 |
int32 RequestParser::ParseBodyInternal(io::MemoryStream* in, |
|---|
| 675 |
http::Header* header, |
|---|
| 676 |
io::MemoryStream* out) { |
|---|
| 677 |
CHECK_EQ(parse_state_, STATE_BODY_READING); |
|---|
| 678 |
if (max_body_size_ >= 0) { |
|---|
| 679 |
if ( body_size_to_read_ > max_body_size_ ) { |
|---|
| 680 |
// ERROR - body too big .. |
|---|
| 681 |
LOG_HTTP_ERR << " Content too long. Got " << body_size_to_read_ |
|---|
| 682 |
<< " on a max of: " << max_body_size_; |
|---|
| 683 |
set_parse_state(ERROR_CONTENT_TOO_LONG); |
|---|
| 684 |
return HEADER_READ | REQUEST_FINISHED; |
|---|
| 685 |
} |
|---|
| 686 |
} |
|---|
| 687 |
// copy the payload from in to our internal buffer. |
|---|
| 688 |
const int64 to_read = min(body_size_to_read_, |
|---|
| 689 |
static_cast<int64>(in->Size())); |
|---|
| 690 |
partial_data_.AppendStreamNonDestructive(in, to_read); |
|---|
| 691 |
in->Skip(to_read); |
|---|
| 692 |
body_size_to_read_ -= to_read; |
|---|
| 693 |
bool is_gzipped = header->IsGzipContentEncoding(); |
|---|
| 694 |
bool is_deflated = header->IsDeflateContentEncoding(); |
|---|
| 695 |
if ( inflate_zwrapper_ != NULL && is_gzipped ) { |
|---|
| 696 |
// One converted gzip to deflate (declared deflate but is gzip.. |
|---|
| 697 |
is_deflated = true; |
|---|
| 698 |
is_gzipped = false; |
|---|
| 699 |
} |
|---|
| 700 |
|
|---|
| 701 |
LOG_HTTP << " Reading body data:" << to_read |
|---|
| 702 |
<< " left:" << body_size_to_read_ |
|---|
| 703 |
<< " partial_data_.Size()=" << partial_data_.Size() |
|---|
| 704 |
<< " is_gzipped: " << is_gzipped; |
|---|
| 705 |
|
|---|
| 706 |
if ( is_gzipped ) { |
|---|
| 707 |
/////////////////////////////////////////////////////////////////// |
|---|
| 708 |
// |
|---|
| 709 |
// GZIP - content encoding |
|---|
| 710 |
// |
|---|
| 711 |
CHECK(!is_deflated); |
|---|
| 712 |
|
|---|
| 713 |
bool maybe_try_deflate = gzip_zwrapper_ == NULL; |
|---|
| 714 |
if ( gzip_zwrapper_ == NULL ) { |
|---|
| 715 |
gzip_zwrapper_ = new io::ZlibGzipDecodeWrapper(); |
|---|
| 716 |
} |
|---|
| 717 |
int32 consumed_size = 0; |
|---|
| 718 |
do { |
|---|
| 719 |
partial_data_.MarkerSet(); |
|---|
| 720 |
io::MemoryStream tmp; |
|---|
| 721 |
const int32 initial_size = partial_data_.Size(); |
|---|
| 722 |
const int zerr = gzip_zwrapper_->Decode(&partial_data_, &tmp); |
|---|
| 723 |
consumed_size = initial_size - partial_data_.Size(); |
|---|
| 724 |
// TODO(cosming) TODO: this is a test, remove |
|---|
| 725 |
DLOG_DEBUG << "GZip Decoder zerr=" |
|---|
| 726 |
<< zerr << " consumed_size=" << consumed_size |
|---|
| 727 |
<< " remaining=" << partial_data_.Size(); |
|---|
| 728 |
if ( zerr == Z_STREAM_END ) { |
|---|
| 729 |
partial_data_.MarkerClear(); |
|---|
| 730 |
out->AppendStream(&tmp); |
|---|
| 731 |
if ( body_size_to_read_ == 0 && partial_data_.IsEmpty() ) { |
|---|
| 732 |
// FINAL - state w/ body decompressed ok |
|---|
| 733 |
set_parse_state(STATE_BODY_END); |
|---|
| 734 |
return HEADER_READ | BODY_FINISHED | REQUEST_FINISHED; |
|---|
| 735 |
} |
|---|
| 736 |
// CONTINUE |
|---|
| 737 |
} else if ( zerr != Z_OK ) { |
|---|
| 738 |
if ( !maybe_try_deflate ) { |
|---|
| 739 |
LOG_HTTP << " ZLib error found in gzip encoding: " << zerr; |
|---|
| 740 |
partial_data_.MarkerClear(); |
|---|
| 741 |
// ERROR - error data in compressed body |
|---|
| 742 |
LOG_HTTP_ERR << " Error in gzipped content: " << zerr; |
|---|
| 743 |
return HEADER_READ | BODY_READING | REQUEST_FINISHED; |
|---|
| 744 |
} |
|---|
| 745 |
LOG_HTTP << " ZLib error found in gzip encoding - trying deflate."; |
|---|
| 746 |
partial_data_.MarkerRestore(); |
|---|
| 747 |
is_deflated = true; |
|---|
| 748 |
// CONTINUE w/ deflate |
|---|
| 749 |
} else { |
|---|
| 750 |
partial_data_.MarkerClear(); |
|---|
| 751 |
out->AppendStream(&tmp); |
|---|
| 752 |
if ( body_size_to_read_ == 0 ) { |
|---|
| 753 |
// ERROR - no more data left in body, but zip did not finish |
|---|
| 754 |
LOG_HTTP_ERR << " Error in gzipped content: Unfinished gzip"; |
|---|
| 755 |
set_parse_state(ERROR_CONTENT_GZIP_UNFINISHED); |
|---|
| 756 |
return HEADER_READ | BODY_FINISHED | REQUEST_FINISHED; |
|---|
| 757 |
} |
|---|
| 758 |
} |
|---|
| 759 |
} while ( !is_deflated && consumed_size > 0 && !partial_data_.IsEmpty() ); |
|---|
| 760 |
} |
|---|
| 761 |
if ( is_deflated ) { |
|---|
| 762 |
/////////////////////////////////////////////////////////////////// |
|---|
| 763 |
// |
|---|
| 764 |
// DEFLATE - content encoding |
|---|
| 765 |
// |
|---|
| 766 |
if ( inflate_zwrapper_ == NULL ) { |
|---|
| 767 |
inflate_zwrapper_ = new io::ZlibInflateWrapper(); |
|---|
| 768 |
} |
|---|
| 769 |
const int zerr = inflate_zwrapper_->InflateSize(&partial_data_, out); |
|---|
| 770 |
if ( zerr == Z_STREAM_END ) { |
|---|
| 771 |
if ( body_size_to_read_ > 0 ) { |
|---|
| 772 |
LOG_HTTP_ERR << " ZLib EOS when left in body: " << body_size_to_read_; |
|---|
| 773 |
// ERROR - End of gzipped data, but body continues |
|---|
| 774 |
set_parse_state(ERROR_CONTENT_GZIP_TOO_LONG); |
|---|
| 775 |
return HEADER_READ | BODY_READING | REQUEST_FINISHED; |
|---|
| 776 |
} |
|---|
| 777 |
// FINAL - state w/ body decompressed ok |
|---|
| 778 |
set_parse_state(STATE_BODY_END); |
|---|
| 779 |
return HEADER_READ | BODY_FINISHED | REQUEST_FINISHED; |
|---|
| 780 |
} |
|---|
| 781 |
if ( zerr != Z_OK ) { |
|---|
| 782 |
LOG_HTTP_ERR << " ZLib error found: " << zerr; |
|---|
| 783 |
// ERROR - error data in compressed body |
|---|
| 784 |
set_parse_state(ERROR_CONTENT_GZIP_ERROR); |
|---|
| 785 |
return HEADER_READ | BODY_READING | REQUEST_FINISHED; |
|---|
| 786 |
} |
|---|
| 787 |
} else if ( !is_gzipped ) { |
|---|
| 788 |
/////////////////////////////////////////////////////////////////// |
|---|
| 789 |
// |
|---|
| 790 |
// IDENTITY - content encoding |
|---|
| 791 |
// |
|---|
| 792 |
out->AppendStream(&partial_data_); |
|---|
| 793 |
if ( body_size_to_read_ == 0 ) { |
|---|
| 794 |
// FINAL - state w/ body set OK |
|---|
| 795 |
set_parse_state(STATE_BODY_END); |
|---|
| 796 |
return HEADER_READ | BODY_FINISHED | REQUEST_FINISHED; |
|---|
| 797 |
} |
|---|
| 798 |
} |
|---|
| 799 |
// CONTINUE - with the compressed body |
|---|
| 800 |
DCHECK_EQ(parse_state_, STATE_BODY_READING); |
|---|
| 801 |
return HEADER_READ | BODY_READING; |
|---|
| 802 |
} |
|---|
| 803 |
|
|---|
| 804 |
|
|---|
| 805 |
////////////////////////////////////////////////////////////////////// |
|---|
| 806 |
// |
|---|
| 807 |
// ParseChunksInternal - parses the body for chunked transfers |
|---|
| 808 |
// |
|---|
| 809 |
|
|---|
| 810 |
// A small cut-and-paste from the protocol RFC: |
|---|
| 811 |
// |
|---|
| 812 |
// Chunked-Body = *chunk |
|---|
| 813 |
// last-chunk |
|---|
| 814 |
// trailer |
|---|
| 815 |
// CRLF |
|---|
| 816 |
// |
|---|
| 817 |
// chunk = chunk-size [ chunk-extension ] CRLF |
|---|
| 818 |
// chunk-data CRLF |
|---|
| 819 |
// chunk-size = 1*HEX |
|---|
| 820 |
// last-chunk = 1*("0") [ chunk-extension ] CRLF |
|---|
| 821 |
// |
|---|
| 822 |
// chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) |
|---|
| 823 |
// chunk-ext-name = token |
|---|
| 824 |
// chunk-ext-val = token | quoted-string |
|---|
| 825 |
// chunk-data = chunk-size(OCTET) |
|---|
| 826 |
// trailer = *(entity-header CRLF) |
|---|
| 827 |
// |
|---|
| 828 |
int32 RequestParser::ParseChunksInternal(io::MemoryStream* in, |
|---|
| 829 |
http::Header* header, |
|---|
| 830 |
io::MemoryStream* out) { |
|---|
| 831 |
do { |
|---|
| 832 |
////////////////////////////////////////////////////////////////////// |
|---|
| 833 |
// |
|---|
| 834 |
// STATE_CHUNK_HEAD_READING ----------> Read the chunk header |
|---|
| 835 |
// |
|---|
| 836 |
if ( parse_state_ == STATE_CHUNK_HEAD_READING ) { |
|---|
| 837 |
string line; |
|---|
| 838 |
if ( !in->ReadCRLFLine(&line) ) { |
|---|
| 839 |
if ( in->Size() > max_chunk_size_ ) { |
|---|
| 840 |
// ERROR - got too much in the first chunk line |
|---|
| 841 |
LOG_HTTP_ERR << " Chumk header too long. Have at least: " |
|---|
| 842 |
<< in->Size() << " on a max of: " << max_chunk_size_; |
|---|
| 843 |
set_parse_state(ERROR_CHUNK_HEADER_TOO_LONG); |
|---|
| 844 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 845 |
} |
|---|
| 846 |
// CONTINUE - waiting for more data in the first chunk line |
|---|
| 847 |
return HEADER_READ | CHUNKED_BODY_READING; |
|---|
| 848 |
} |
|---|
| 849 |
// Got one more chunk header !! - Cut the last CRLF |
|---|
| 850 |
DCHECK_GE(line.size(), 2); // at least CRLF |
|---|
| 851 |
line.resize(line.size() - 2); |
|---|
| 852 |
// We ignore the chunk extension (if any) |
|---|
| 853 |
errno = 0; // essential as strtol would not set a 0 errno |
|---|
| 854 |
const int32 chunk_length = strtol(line.c_str(), NULL, 16); |
|---|
| 855 |
if ( errno || chunk_length < 0 ) { |
|---|
| 856 |
LOG_HTTP_ERR << " Invalid chunk len specification : " << line; |
|---|
| 857 |
// ERROR - Badly specified chunk lenght |
|---|
| 858 |
set_parse_state(ERROR_CHUNK_BAD_CHUNK_LENGTH); |
|---|
| 859 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 860 |
} |
|---|
| 861 |
if ( chunk_length == 0 ) { |
|---|
| 862 |
if ( next_chunk_expectation_ == EXPECT_CHUNK_NON_EMPTY ) { |
|---|
| 863 |
// ERROR - wanted a non empty chunk - got an empty one .. |
|---|
| 864 |
LOG_HTTP_ERR << " Unfinished gzip content. When expecting chunk " |
|---|
| 865 |
<< " got end of stream"; |
|---|
| 866 |
set_parse_state(ERROR_CHUNK_UNFINISHED_GZIP_CONTENT); |
|---|
| 867 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 868 |
} |
|---|
| 869 |
next_chunk_expectation_ = EXPECT_CHUNK_NONE; |
|---|
| 870 |
// Last chunk signaled !! |
|---|
| 871 |
set_parse_state(STATE_LAST_CHUNK_READ); |
|---|
| 872 |
continue; |
|---|
| 873 |
} else { |
|---|
| 874 |
LOG_HTTP << " Got a chunk size of: " << chunk_length |
|---|
| 875 |
<< " original hex: " << line; |
|---|
| 876 |
if ( next_chunk_expectation_ == EXPECT_CHUNK_EMPTY ) { |
|---|
| 877 |
// ERROR - wanted an end of body (empty chunk) - got some content |
|---|
| 878 |
LOG_HTTP_ERR << " Long gzip content. When expecting no chunk " |
|---|
| 879 |
<< " got extra data."; |
|---|
| 880 |
set_parse_state(ERROR_CHUNK_CONTINUED_FINISHED_GZIP_CONTENT); |
|---|
| 881 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 882 |
} |
|---|
| 883 |
if ( chunk_length > max_chunk_size_ ) { |
|---|
| 884 |
// ERROR - Chunk too long signaled |
|---|
| 885 |
LOG_HTTP_ERR << " Chunk to long obtained: " << chunk_length |
|---|
| 886 |
<< " when max is: " << max_chunk_size_; |
|---|
| 887 |
set_parse_state(ERROR_CHUNK_TOO_LONG); |
|---|
| 888 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 889 |
} |
|---|
| 890 |
next_chunk_expectation_ = EXPECT_CHUNK_NONE; |
|---|
| 891 |
num_chunks_read_++; |
|---|
| 892 |
if ( max_num_chunks_ >= 0 && num_chunks_read_ > max_num_chunks_ ) { |
|---|
| 893 |
// ERROR - Too many chunks transmitted |
|---|
| 894 |
LOG_HTTP_ERR << " Too many chunks in request. Got: " |
|---|
| 895 |
<< num_chunks_read_ |
|---|
| 896 |
<< " when max is: " << max_num_chunks_; |
|---|
| 897 |
set_parse_state(ERROR_CHUNK_TOO_MANY); |
|---|
| 898 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 899 |
} |
|---|
| 900 |
// CONTINUE - reading the chunk data ! |
|---|
| 901 |
chunk_size_to_read_ = chunk_length; |
|---|
| 902 |
set_parse_state(STATE_CHUNK_READING); |
|---|
| 903 |
} |
|---|
| 904 |
} else if ( parse_state_ == STATE_END_OF_CHUNK ) { |
|---|
| 905 |
////////////////////////////////////////////////////////////////////// |
|---|
| 906 |
// |
|---|
| 907 |
// STATE_END_OF_CHUNK -----------> Read the \r\n chunk termiantion |
|---|
| 908 |
// |
|---|
| 909 |
string line; |
|---|
| 910 |
if ( !in->ReadCRLFLine(&line) ) { |
|---|
| 911 |
if ( in->Size() > strlen("\r\n") ) { |
|---|
| 912 |
LOG_HTTP_ERR << " Got at the end of chunks invalid leftover data: " |
|---|
| 913 |
<< in->Size(); |
|---|
| 914 |
set_parse_state(ERROR_CHUNK_BAD_CHUNK_TERMINATION); |
|---|
| 915 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 916 |
} |
|---|
| 917 |
// CONTINUE - waiting for more data in the chunk termination |
|---|
| 918 |
return HEADER_READ | CHUNKED_BODY_READING; |
|---|
| 919 |
} |
|---|
| 920 |
// Got one more chunk header !! - Cut the last CRLF |
|---|
| 921 |
DCHECK_GE(line.size(), 2); // at least CRLF |
|---|
| 922 |
line.resize(line.size() - 2); |
|---|
| 923 |
if ( !line.empty() ) { |
|---|
| 924 |
LOG_HTTP_ERR |
|---|
| 925 |
<< " Got a non empty line at end of chunk: " |
|---|
| 926 |
<< strutil::PrintableDataBuffer( |
|---|
| 927 |
reinterpret_cast<const uint8*>(line.data()), line.size()); |
|---|
| 928 |
// ERROR: got some chars before chunk termination.. |
|---|
| 929 |
set_parse_state(ERROR_CHUNK_BIGGER_THEN_DECLARED); |
|---|
| 930 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 931 |
} |
|---|
| 932 |
// CONTINUE - read the next chunck header |
|---|
| 933 |
set_parse_state(STATE_CHUNK_HEAD_READING); |
|---|
| 934 |
} else if ( parse_state_ == STATE_CHUNK_READING ) { |
|---|
| 935 |
////////////////////////////////////////////////////////////////////// |
|---|
| 936 |
// |
|---|
| 937 |
// STATE_CHUNK_READING -----------> Pull Chunk content |
|---|
| 938 |
// |
|---|
| 939 |
if ( in->IsEmpty() ) { |
|---|
| 940 |
return HEADER_READ | CHUNKED_BODY_READING; |
|---|
| 941 |
} |
|---|
| 942 |
const int64 to_read = min(chunk_size_to_read_, |
|---|
| 943 |
static_cast<int64>(in->Size())); |
|---|
| 944 |
partial_data_.AppendStreamNonDestructive(in, to_read); |
|---|
| 945 |
in->Skip(to_read); |
|---|
| 946 |
|
|---|
| 947 |
bool is_gzipped = header->IsGzipContentEncoding(); |
|---|
| 948 |
bool is_deflated = header->IsDeflateContentEncoding(); |
|---|
| 949 |
if ( is_gzipped && inflate_zwrapper_ != NULL ) { |
|---|
| 950 |
// one gzip converted to deflate (normally bad server) |
|---|
| 951 |
is_deflated = true; |
|---|
| 952 |
is_gzipped = false; |
|---|
| 953 |
} |
|---|
| 954 |
|
|---|
| 955 |
chunk_size_to_read_ -= to_read; |
|---|
| 956 |
LOG_HTTP << " Reading chunk data: " << to_read |
|---|
| 957 |
<< " left: " << chunk_size_to_read_ |
|---|
| 958 |
<< " is_gzipped: " << is_gzipped |
|---|
| 959 |
<< " is_deflate: " << is_deflated; |
|---|
| 960 |
|
|---|
| 961 |
if ( chunk_size_to_read_ == 0 ) { |
|---|
| 962 |
set_parse_state(STATE_END_OF_CHUNK); |
|---|
| 963 |
} |
|---|
| 964 |
if ( is_gzipped ) { |
|---|
| 965 |
/////////////////////////////////////////////////////////////////// |
|---|
| 966 |
// |
|---|
| 967 |
// GZIP - content encoding |
|---|
| 968 |
// |
|---|
| 969 |
// |
|---|
| 970 |
// TODO(cpopescu): protect from chunks too long after |
|---|
| 971 |
// decompression !!! |
|---|
| 972 |
// |
|---|
| 973 |
CHECK(!is_deflated); |
|---|
| 974 |
if ( partial_data_.Size() < |
|---|
| 975 |
io::ZlibGzipDecodeWrapper::kMinGzipDataSize ) { |
|---|
| 976 |
if ( in->Size() > io::ZlibGzipDecodeWrapper::kMinGzipDataSize ) |
|---|
| 977 |
continue; |
|---|
| 978 |
return HEADER_READ | BODY_READING; |
|---|
| 979 |
} |
|---|
| 980 |
bool maybe_try_deflate = gzip_zwrapper_ == NULL; |
|---|
| 981 |
if ( gzip_zwrapper_ == NULL ) { |
|---|
| 982 |
gzip_zwrapper_ = new io::ZlibGzipDecodeWrapper(); |
|---|
| 983 |
} |
|---|
| 984 |
int32 consumed_size = 0; |
|---|
| 985 |
do { |
|---|
| 986 |
partial_data_.MarkerSet(); |
|---|
| 987 |
io::MemoryStream tmp; |
|---|
| 988 |
const int32 initial_size = partial_data_.Size(); |
|---|
| 989 |
const int zerr = gzip_zwrapper_->Decode(&partial_data_, &tmp); |
|---|
| 990 |
consumed_size = initial_size - partial_data_.Size(); |
|---|
| 991 |
if ( zerr == Z_STREAM_END ) { |
|---|
| 992 |
partial_data_.MarkerClear(); |
|---|
| 993 |
out->AppendStream(&tmp); |
|---|
| 994 |
// CONTINUE if ( partial_data_.IsEmpty() ) { |
|---|
| 995 |
// We have more data to process.. |
|---|
| 996 |
} else if ( zerr != Z_OK ) { |
|---|
| 997 |
if ( !maybe_try_deflate ) { |
|---|
| 998 |
LOG_HTTP_ERR << " ZLib error found in gzip encoding: " << zerr; |
|---|
| 999 |
partial_data_.MarkerClear(); |
|---|
| 1000 |
// ERROR - error data in compressed body |
|---|
| 1001 |
set_parse_state(ERROR_CHUNK_CONTENT_GZIP_ERROR); |
|---|
| 1002 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 1003 |
} |
|---|
| 1004 |
LOG_HTTP << " ZLib error found in gzip encoding - trying deflate."; |
|---|
| 1005 |
partial_data_.MarkerRestore(); |
|---|
| 1006 |
is_deflated = true; |
|---|
| 1007 |
} else { |
|---|
| 1008 |
partial_data_.MarkerClear(); |
|---|
| 1009 |
out->AppendStream(&tmp); |
|---|
| 1010 |
if ( chunk_size_to_read_ == 0 ) { |
|---|
| 1011 |
// CONTINUE - no more data left in the chunk, |
|---|
| 1012 |
// but zip did not finish - we expect to continue !! |
|---|
| 1013 |
next_chunk_expectation_ = EXPECT_CHUNK_NON_EMPTY; |
|---|
| 1014 |
} |
|---|
| 1015 |
} |
|---|
| 1016 |
} while ( !is_deflated && consumed_size > 0 && |
|---|
| 1017 |
!partial_data_.IsEmpty() ); |
|---|
| 1018 |
} |
|---|
| 1019 |
if ( is_deflated ) { |
|---|
| 1020 |
/////////////////////////////////////////////////////////////////// |
|---|
| 1021 |
// |
|---|
| 1022 |
// DEFLATE - content encoding |
|---|
| 1023 |
// |
|---|
| 1024 |
if ( inflate_zwrapper_ == NULL ) { |
|---|
| 1025 |
inflate_zwrapper_ = new io::ZlibInflateWrapper(); |
|---|
| 1026 |
} |
|---|
| 1027 |
const int zerr = inflate_zwrapper_->InflateSize(&partial_data_, out); |
|---|
| 1028 |
if ( zerr == Z_STREAM_END ) { |
|---|
| 1029 |
if ( chunk_size_to_read_ > 0 ) { |
|---|
| 1030 |
LOG_HTTP_ERR << " ZLib EOS when left in chunk: " |
|---|
| 1031 |
<< chunk_size_to_read_; |
|---|
| 1032 |
// ERROR - End of gzipped data, but chunk continues |
|---|
| 1033 |
set_parse_state(ERROR_CHUNK_CONTENT_GZIP_TOO_LONG); |
|---|
| 1034 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 1035 |
} |
|---|
| 1036 |
// FINAL - state w/ body decompressed ok - but we also expect |
|---|
| 1037 |
// no chunks to follow |
|---|
| 1038 |
next_chunk_expectation_ = EXPECT_CHUNK_EMPTY; |
|---|
| 1039 |
} else if ( zerr != Z_OK ) { |
|---|
| 1040 |
// ERROR - error data in compressed body |
|---|
| 1041 |
LOG_HTTP_ERR << " ZLib error found: " << zerr; |
|---|
| 1042 |
set_parse_state(ERROR_CHUNK_CONTENT_GZIP_ERROR); |
|---|
| 1043 |
return HEADER_READ | CHUNKED_BODY_READING | REQUEST_FINISHED; |
|---|
| 1044 |
} else if ( chunk_size_to_read_ == 0 ) { |
|---|
| 1045 |
// CONTINUE - no more data left in the chunk, but zip did not finish |
|---|
| 1046 |
// - we expect to continue !! |
|---|
| 1047 |
next_chunk_expectation_ = EXPECT_CHUNK_NON_EMPTY; |
|---|
| 1048 |
} |
|---|
| 1049 |
} else if ( !is_gzipped ) { |
|---|
| 1050 |
/////////////////////////////////////////////////////////////////// |
|---|
| 1051 |
// |
|---|
| 1052 |
// IDENTITY - content encoding |
|---|
| 1053 |
// |
|---|
| 1054 |
out->AppendStream(&partial_data_); |
|---|
| 1055 |
} |
|---|
| 1056 |
} else if ( parse_state_ == STATE_LAST_CHUNK_READ ) { |
|---|
| 1057 |
////////////////////////////////////////////////////////////////////// |
|---|
| 1058 |
// |
|---|
| 1059 |
// STATE_LAST_CHUNK_READ -----------> Parse the trail header |
|---|
| 1060 |
// |
|---|
| 1061 |
return ParseTrailHeader(in, header, out); |
|---|
| 1062 |
} else { |
|---|
| 1063 |
LOG_FATAL << " Got in a wrong state - this is a bug: " |
|---|
| 1064 |
<< ParseStateName(); |
|---|
| 1065 |
} |
|---|
| 1066 |
} while (true); // should exit w/ a return in the code above.. |
|---|
| 1067 |
return 0; // keep g++ happy |
|---|
| 1068 |
} |
|---|
| 1069 |
|
|---|
| 1070 |
////////////////////////////////////////////////////////////////////// |
|---|
| 1071 |
// |
|---|
| 1072 |
// ParseTrailHeader - helper for parsing the trailer of a chunked |
|---|
| 1073 |
// transmission |
|---|
| 1074 |
// |
|---|
| 1075 |
int32 RequestParser::ParseTrailHeader(io::MemoryStream* in, |
|---|
| 1076 |
http::Header* header, |
|---|
| 1077 |
io::MemoryStream* out) { |
|---|
| 1078 |
CHECK_EQ(parse_state_, STATE_LAST_CHUNK_READ); |
|---|
| 1079 |
if ( !trail_header_.ReadHeaderFields(in) ) { |
|---|
| 1080 |
if ( trail_header_.bytes_parsed() + in->Size() > max_header_size_ ) { |
|---|
| 1081 |
// ERROR : trail header too long. |
|---|
| 1082 |
LOG_HTTP_ERR << " Chunk trailer has header too long. Got at least: " |
|---|
| 1083 |
<< trail_header_.bytes_parsed() + in->Size() |
|---|
| 1084 |
<< " on a max of: " << max_header_size_; |
|---|
| 1085 |
set_parse_state(ERROR_CHUNK_TRAILER_TOO_LONG); |
|---|
| 1086 |
return HEADER_READ | CHUNKED_TRAILER_READING | REQUEST_FINISHED; |
|---|
| 1087 |
} |
|---|
| 1088 |
return HEADER_READ | CHUNKED_TRAILER_READING; |
|---|
| 1089 |
} |
|---|
| 1090 |
// CONTINUE - got the trailing header |
|---|
| 1091 |
if ( trail_header_.parse_error() > worst_accepted_header_error_ ) { |
|---|
| 1092 |
// ERROR - unacceptale error in trailed header parsing |
|---|
| 1093 |
LOG_HTTP_ERR << " Error in trail headers found: " |
|---|
| 1094 |
<< trail_header_.ParseErrorName(); |
|---|
| 1095 |
set_parse_state(ERROR_CHUNK_TRAIL_HEADER); |
|---|
| 1096 |
return HEADER_READ | CHUNKED_TRAILER_READING | REQUEST_FINISHED; |
|---|
| 1097 |
} |
|---|
| 1098 |
// Copy all trailing header in the provided header |
|---|
| 1099 |
// |
|---|
| 1100 |
// TODO(cpopescu): it is correct this way ?? |
|---|
| 1101 |
// |
|---|
| 1102 |
header->CopyHeaderFields(trail_header_, false); |
|---|
| 1103 |
set_parse_state(STATE_END_OF_TRAIL_HEADER); |
|---|
| 1104 |
return HEADER_READ | CHUNKS_FINISHED | REQUEST_FINISHED; |
|---|
| 1105 |
} |
|---|
| 1106 |
|
|---|
| 1107 |
////////////////////////////////////////////////////////////////////// |
|---|
| 1108 |
|
|---|
| 1109 |
|
|---|
| 1110 |
static const char kGzip[] = "gzip"; |
|---|
| 1111 |
static const char kDeflate[] = "deflate"; |
|---|
| 1112 |
static const char kIdentity[] = "identity"; |
|---|
| 1113 |
static const char kChunked[] = "chunked"; |
|---|
| 1114 |
|
|---|
| 1115 |
bool RequestParser::IsKnownContentEncoding(const http::Header* header) { |
|---|
| 1116 |
int len; |
|---|
| 1117 |
const char* s = header->FindField(kHeaderContentEncoding, &len); |
|---|
| 1118 |
if ( !s ) return true; |
|---|
| 1119 |
string s_trim = strutil::StrTrim(string(s, len)); |
|---|
| 1120 |
if ( s_trim.empty() || |
|---|
| 1121 |
strutil::StrCasePrefix(s_trim.c_str(), kGzip) || |
|---|
| 1122 |
strutil::StrCasePrefix(s_trim.c_str(), kDeflate) || |
|---|
| 1123 |
strutil::StrCasePrefix(s_trim.c_str(), kIdentity) ) { |
|---|
| 1124 |
return true; |
|---|
| 1125 |
} |
|---|
| 1126 |
LOG_WARNING << " Unknown Content Encoding found: [" << s_trim << "]."; |
|---|
| 1127 |
return false; |
|---|
| 1128 |
} |
|---|
| 1129 |
|
|---|
| 1130 |
bool RequestParser::IsKnownTransferEncoding(const http::Header* header) { |
|---|
| 1131 |
int len; |
|---|
| 1132 |
const char* s = header->FindField(kHeaderTransferEncoding, &len); |
|---|
| 1133 |
if ( !s ) return true; |
|---|
| 1134 |
string s_trim = strutil::StrTrim(string(s, len)); |
|---|
| 1135 |
if ( s_trim.empty() || |
|---|
| 1136 |
strutil::StrCasePrefix(s_trim.c_str(), kChunked) || |
|---|
| 1137 |
strutil::StrCasePrefix(s_trim.c_str(), kIdentity) ) { |
|---|
| 1138 |
return true; |
|---|
| 1139 |
} |
|---|
| 1140 |
LOG_WARNING << " Unknown Transfer Encoding found: [" << s_trim << "]."; |
|---|
| 1141 |
return false; |
|---|
| 1142 |
} |
|---|
| 1143 |
} |
|---|