Index: net/http/http_util.cc |
=================================================================== |
--- net/http/http_util.cc (revision 1706) |
+++ net/http/http_util.cc (working copy) |
@@ -10,6 +10,7 @@ |
#include <algorithm> |
#include "base/logging.h" |
+#include "base/string_piece.h" |
#include "base/string_util.h" |
using std::string; |
@@ -218,14 +219,18 @@ |
return false; |
} |
+bool HttpUtil::IsLWS(char c) { |
+ return strchr(HTTP_LWS, c) != NULL; |
+} |
+ |
void HttpUtil::TrimLWS(string::const_iterator* begin, |
string::const_iterator* end) { |
// leading whitespace |
- while (*begin < *end && strchr(HTTP_LWS, (*begin)[0])) |
+ while (*begin < *end && IsLWS((*begin)[0])) |
++(*begin); |
// trailing whitespace |
- while (*begin < *end && strchr(HTTP_LWS, (*end)[-1])) |
+ while (*begin < *end && IsLWS((*end)[-1])) |
--(*end); |
} |
@@ -246,27 +251,79 @@ |
return -1; |
} |
-std::string HttpUtil::AssembleRawHeaders(const char* buf, int buf_len) { |
+// In order for a line to be continuable, it must specify a |
+// non-blank header-name. Line continuations are specifically for |
+// header values -- do not allow headers names to span lines. |
+static bool IsLineSegmentContinuable(const char* begin, const char* end) { |
+ if (begin == end) |
+ return false; |
+ |
+ const char* colon = std::find(begin, end, ':'); |
+ if (colon == end) |
+ return false; |
+ |
+ const char* name_begin = begin; |
+ const char* name_end = colon; |
+ |
+ // Name can't be empty. |
+ if (name_begin == name_end) |
+ return false; |
+ |
+ // Can't start with LWS (this would imply the segment is a continuation) |
+ if (HttpUtil::IsLWS(*name_begin)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+// Helper used by AssembleRawHeaders, to find the end of the status line. |
+static const char* FindStatusLineEnd(const char* begin, const char* end) { |
+ size_t i = StringPiece(begin, end - begin).find_first_of("\r\n"); |
+ if (i == StringPiece::npos) |
+ return end; |
+ return begin + i; |
+} |
+ |
+std::string HttpUtil::AssembleRawHeaders(const char* input_begin, |
+ int input_len) { |
std::string raw_headers; |
+ raw_headers.reserve(input_len); |
- // TODO(darin): |
- // - Handle header line continuations. |
- // - Be careful about CRs that appear spuriously mid header line. |
+ const char* input_end = input_begin + input_len; |
- int line_start = 0; |
- for (int i = 0; i < buf_len; ++i) { |
- char c = buf[i]; |
- if (c == '\r' || c == '\n') { |
- if (line_start != i) { |
- // (line_start,i) is a header line. |
- raw_headers.append(buf + line_start, buf + i); |
- raw_headers.push_back('\0'); |
- } |
- line_start = i + 1; |
- } |
+ // Copy the status line. |
+ const char* status_line_end = FindStatusLineEnd(input_begin, input_end); |
+ raw_headers.append(input_begin, status_line_end); |
+ |
+ // After the status line, every subsequent line is a header line segment. |
+ // Should a segment start with LWS, it is a continuation of the previous |
+ // line's field-value. |
+ |
+ // TODO(ericroman): is this too permissive? (delimits on [\r\n]+) |
+ CStringTokenizer lines(status_line_end, input_end, "\r\n"); |
+ |
+ // This variable is true when the previous line was continuable. |
+ bool can_append_continuation = false; |
+ |
+ while (lines.GetNext()) { |
+ const char* line_begin = lines.token_begin(); |
+ const char* line_end = lines.token_end(); |
+ |
+ bool is_continuation = can_append_continuation && IsLWS(*line_begin); |
+ |
+ // Terminate the previous line. |
+ if (!is_continuation) |
+ raw_headers.push_back('\0'); |
+ |
+ // Copy the raw data to output. |
+ raw_headers.append(line_begin, line_end); |
+ |
+ // Check if the current line can be continued. |
+ if (!is_continuation) |
+ can_append_continuation = IsLineSegmentContinuable(line_begin, line_end); |
} |
- raw_headers.push_back('\0'); |
+ raw_headers.append("\0\0", 2); |
return raw_headers; |
} |
@@ -296,6 +353,13 @@ |
continue; // skip malformed header |
name_end_ = colon; |
+ |
+ // If the name starts with LWS, it is an invalid line. |
+ // Leading LWS implies a line continuation, and these should have |
+ // already been joined by AssembleRawHeaders(). |
+ if (name_begin_ == name_end_ || IsLWS(*name_begin_)) |
+ continue; |
+ |
TrimLWS(&name_begin_, &name_end_); |
if (name_begin_ == name_end_) |
continue; // skip malformed header |