OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The rules for parsing content-types were borrowed from Firefox: | 5 // The rules for parsing content-types were borrowed from Firefox: |
6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 | 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 |
7 | 7 |
8 #include "net/http/http_util.h" | 8 #include "net/http/http_util.h" |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
11 | 11 |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/string_piece.h" |
13 #include "base/string_util.h" | 14 #include "base/string_util.h" |
14 | 15 |
15 using std::string; | 16 using std::string; |
16 | 17 |
17 namespace net { | 18 namespace net { |
18 | 19 |
19 //----------------------------------------------------------------------------- | 20 //----------------------------------------------------------------------------- |
20 | 21 |
21 // Return the index of the closing quote of the string, if any. | 22 // Return the index of the closing quote of the string, if any. |
22 static size_t FindStringEnd(const string& line, size_t start, char delim) { | 23 static size_t FindStringEnd(const string& line, size_t start, char delim) { |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 "retry-after", | 212 "retry-after", |
212 "set-cookie" | 213 "set-cookie" |
213 }; | 214 }; |
214 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) { | 215 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) { |
215 if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i])) | 216 if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i])) |
216 return true; | 217 return true; |
217 } | 218 } |
218 return false; | 219 return false; |
219 } | 220 } |
220 | 221 |
| 222 bool HttpUtil::IsLWS(char c) { |
| 223 return strchr(HTTP_LWS, c) != NULL; |
| 224 } |
| 225 |
221 void HttpUtil::TrimLWS(string::const_iterator* begin, | 226 void HttpUtil::TrimLWS(string::const_iterator* begin, |
222 string::const_iterator* end) { | 227 string::const_iterator* end) { |
223 // leading whitespace | 228 // leading whitespace |
224 while (*begin < *end && strchr(HTTP_LWS, (*begin)[0])) | 229 while (*begin < *end && IsLWS((*begin)[0])) |
225 ++(*begin); | 230 ++(*begin); |
226 | 231 |
227 // trailing whitespace | 232 // trailing whitespace |
228 while (*begin < *end && strchr(HTTP_LWS, (*end)[-1])) | 233 while (*begin < *end && IsLWS((*end)[-1])) |
229 --(*end); | 234 --(*end); |
230 } | 235 } |
231 | 236 |
232 int HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len) { | 237 int HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len) { |
233 bool was_lf = false; | 238 bool was_lf = false; |
234 char last_c = '\0'; | 239 char last_c = '\0'; |
235 for (int i = 0; i < buf_len; ++i) { | 240 for (int i = 0; i < buf_len; ++i) { |
236 char c = buf[i]; | 241 char c = buf[i]; |
237 if (c == '\n') { | 242 if (c == '\n') { |
238 if (was_lf) | 243 if (was_lf) |
239 return i + 1; | 244 return i + 1; |
240 was_lf = true; | 245 was_lf = true; |
241 } else if (c != '\r' || last_c != '\n') { | 246 } else if (c != '\r' || last_c != '\n') { |
242 was_lf = false; | 247 was_lf = false; |
243 } | 248 } |
244 last_c = c; | 249 last_c = c; |
245 } | 250 } |
246 return -1; | 251 return -1; |
247 } | 252 } |
248 | 253 |
249 std::string HttpUtil::AssembleRawHeaders(const char* buf, int buf_len) { | 254 // In order for a line to be continuable, it must specify a |
| 255 // non-blank header-name. Line continuations are specifically for |
| 256 // header values -- do not allow headers names to span lines. |
| 257 static bool IsLineSegmentContinuable(const char* begin, const char* end) { |
| 258 if (begin == end) |
| 259 return false; |
| 260 |
| 261 const char* colon = std::find(begin, end, ':'); |
| 262 if (colon == end) |
| 263 return false; |
| 264 |
| 265 const char* name_begin = begin; |
| 266 const char* name_end = colon; |
| 267 |
| 268 // Name can't be empty. |
| 269 if (name_begin == name_end) |
| 270 return false; |
| 271 |
| 272 // Can't start with LWS (this would imply the segment is a continuation) |
| 273 if (HttpUtil::IsLWS(*name_begin)) |
| 274 return false; |
| 275 |
| 276 return true; |
| 277 } |
| 278 |
| 279 // Helper used by AssembleRawHeaders, to find the end of the status line. |
| 280 static const char* FindStatusLineEnd(const char* begin, const char* end) { |
| 281 size_t i = StringPiece(begin, end - begin).find_first_of("\r\n"); |
| 282 if (i == StringPiece::npos) |
| 283 return end; |
| 284 return begin + i; |
| 285 } |
| 286 |
| 287 std::string HttpUtil::AssembleRawHeaders(const char* input_begin, |
| 288 int input_len) { |
250 std::string raw_headers; | 289 std::string raw_headers; |
| 290 raw_headers.reserve(input_len); |
251 | 291 |
252 // TODO(darin): | 292 const char* input_end = input_begin + input_len; |
253 // - Handle header line continuations. | |
254 // - Be careful about CRs that appear spuriously mid header line. | |
255 | 293 |
256 int line_start = 0; | 294 // Copy the status line. |
257 for (int i = 0; i < buf_len; ++i) { | 295 const char* status_line_end = FindStatusLineEnd(input_begin, input_end); |
258 char c = buf[i]; | 296 raw_headers.append(input_begin, status_line_end); |
259 if (c == '\r' || c == '\n') { | 297 |
260 if (line_start != i) { | 298 // After the status line, every subsequent line is a header line segment. |
261 // (line_start,i) is a header line. | 299 // Should a segment start with LWS, it is a continuation of the previous |
262 raw_headers.append(buf + line_start, buf + i); | 300 // line's field-value. |
263 raw_headers.push_back('\0'); | 301 |
264 } | 302 // TODO(ericroman): is this too permissive? (delimits on [\r\n]+) |
265 line_start = i + 1; | 303 CStringTokenizer lines(status_line_end, input_end, "\r\n"); |
266 } | 304 |
| 305 // This variable is true when the previous line was continuable. |
| 306 bool can_append_continuation = false; |
| 307 |
| 308 while (lines.GetNext()) { |
| 309 const char* line_begin = lines.token_begin(); |
| 310 const char* line_end = lines.token_end(); |
| 311 |
| 312 bool is_continuation = can_append_continuation && IsLWS(*line_begin); |
| 313 |
| 314 // Terminate the previous line. |
| 315 if (!is_continuation) |
| 316 raw_headers.push_back('\0'); |
| 317 |
| 318 // Copy the raw data to output. |
| 319 raw_headers.append(line_begin, line_end); |
| 320 |
| 321 // Check if the current line can be continued. |
| 322 if (!is_continuation) |
| 323 can_append_continuation = IsLineSegmentContinuable(line_begin, line_end); |
267 } | 324 } |
268 raw_headers.push_back('\0'); | |
269 | 325 |
| 326 raw_headers.append("\0\0", 2); |
270 return raw_headers; | 327 return raw_headers; |
271 } | 328 } |
272 | 329 |
273 // BNF from section 4.2 of RFC 2616: | 330 // BNF from section 4.2 of RFC 2616: |
274 // | 331 // |
275 // message-header = field-name ":" [ field-value ] | 332 // message-header = field-name ":" [ field-value ] |
276 // field-name = token | 333 // field-name = token |
277 // field-value = *( field-content | LWS ) | 334 // field-value = *( field-content | LWS ) |
278 // field-content = <the OCTETs making up the field-value | 335 // field-content = <the OCTETs making up the field-value |
279 // and consisting of either *TEXT or combinations | 336 // and consisting of either *TEXT or combinations |
280 // of token, separators, and quoted-string> | 337 // of token, separators, and quoted-string> |
281 // | 338 // |
282 | 339 |
283 HttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin, | 340 HttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin, |
284 string::const_iterator headers_end, | 341 string::const_iterator headers_end, |
285 const std::string& line_delimiter) | 342 const std::string& line_delimiter) |
286 : lines_(headers_begin, headers_end, line_delimiter) { | 343 : lines_(headers_begin, headers_end, line_delimiter) { |
287 } | 344 } |
288 | 345 |
289 bool HttpUtil::HeadersIterator::GetNext() { | 346 bool HttpUtil::HeadersIterator::GetNext() { |
290 while (lines_.GetNext()) { | 347 while (lines_.GetNext()) { |
291 name_begin_ = lines_.token_begin(); | 348 name_begin_ = lines_.token_begin(); |
292 values_end_ = lines_.token_end(); | 349 values_end_ = lines_.token_end(); |
293 | 350 |
294 string::const_iterator colon = find(name_begin_, values_end_, ':'); | 351 string::const_iterator colon = find(name_begin_, values_end_, ':'); |
295 if (colon == values_end_) | 352 if (colon == values_end_) |
296 continue; // skip malformed header | 353 continue; // skip malformed header |
297 | 354 |
298 name_end_ = colon; | 355 name_end_ = colon; |
| 356 |
| 357 // If the name starts with LWS, it is an invalid line. |
| 358 // Leading LWS implies a line continuation, and these should have |
| 359 // already been joined by AssembleRawHeaders(). |
| 360 if (name_begin_ == name_end_ || IsLWS(*name_begin_)) |
| 361 continue; |
| 362 |
299 TrimLWS(&name_begin_, &name_end_); | 363 TrimLWS(&name_begin_, &name_end_); |
300 if (name_begin_ == name_end_) | 364 if (name_begin_ == name_end_) |
301 continue; // skip malformed header | 365 continue; // skip malformed header |
302 | 366 |
303 values_begin_ = colon + 1; | 367 values_begin_ = colon + 1; |
304 TrimLWS(&values_begin_, &values_end_); | 368 TrimLWS(&values_begin_, &values_end_); |
305 | 369 |
306 // if we got a header name, then we are done. | 370 // if we got a header name, then we are done. |
307 return true; | 371 return true; |
308 } | 372 } |
(...skipping 16 matching lines...) Expand all Loading... |
325 | 389 |
326 // bypass empty values. | 390 // bypass empty values. |
327 if (value_begin_ != value_end_) | 391 if (value_begin_ != value_end_) |
328 return true; | 392 return true; |
329 } | 393 } |
330 return false; | 394 return false; |
331 } | 395 } |
332 | 396 |
333 } // namespace net | 397 } // namespace net |
334 | 398 |
OLD | NEW |