Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: net/http/http_util.cc

Issue 458: [new http] Normalize line continuations in response headers. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/http/http_util.h ('k') | net/http/http_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « net/http/http_util.h ('k') | net/http/http_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698