OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/tools/quic/test_tools/http_message.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 | |
12 using base::StringPiece; | |
13 using std::string; | |
14 using std::vector; | |
15 | |
16 namespace net { | |
17 namespace test { | |
18 | |
19 namespace { | |
20 | |
21 // const char kContentEncoding[] = "content-encoding"; | |
22 const char kContentLength[] = "content-length"; | |
23 const char kTransferCoding[] = "transfer-encoding"; | |
24 | |
25 // Both kHTTPVersionString and kMethodString arrays are constructed to match | |
26 // the enum values defined in Version and Method of HTTPMessage. | |
27 const char* const kHTTPVersionString[] = {"", "HTTP/0.9", "HTTP/1.0", | |
28 "HTTP/1.1"}; | |
29 | |
30 const char* const kMethodString[] = { | |
31 "", "OPTIONS", "GET", "HEAD", "POST", "PUT", | |
32 "DELETE", "TRACE", "CONNECT", "MKCOL", "UNLOCK", | |
33 }; | |
34 | |
35 // Returns true if the message represents a complete request or response. | |
36 // Messages are considered complete if: | |
37 // - Transfer-Encoding: chunked is present and message has a final chunk. | |
38 // - Content-Length header is present and matches the message body length. | |
39 // - Neither Transfer-Encoding nor Content-Length is present and message | |
40 // is tagged as complete. | |
41 bool IsCompleteMessage(const HTTPMessage& message) { | |
42 const BalsaHeaders* headers = message.headers(); | |
43 StringPiece content_length = headers->GetHeader(kContentLength); | |
44 if (!content_length.empty()) { | |
45 int parsed_content_length; | |
46 if (!base::StringToInt(content_length, &parsed_content_length)) { | |
47 return false; | |
48 } | |
49 return (message.body().size() == (uint)parsed_content_length); | |
50 } else { | |
51 // Assume messages without transfer coding or content-length are | |
52 // tagged correctly. | |
53 return message.has_complete_message(); | |
54 } | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 HTTPMessage::Method HTTPMessage::StringToMethod(StringPiece str) { | |
60 // Skip the first element of the array since it is empty string. | |
61 for (unsigned long i = 1; i < arraysize(kMethodString); ++i) { | |
62 if (strncmp(str.data(), kMethodString[i], str.length()) == 0) { | |
63 return static_cast<HTTPMessage::Method>(i); | |
64 } | |
65 } | |
66 return HttpConstants::UNKNOWN_METHOD; | |
67 } | |
68 | |
69 HTTPMessage::Version HTTPMessage::StringToVersion(StringPiece str) { | |
70 // Skip the first element of the array since it is empty string. | |
71 for (unsigned long i = 1; i < arraysize(kHTTPVersionString); ++i) { | |
72 if (strncmp(str.data(), kHTTPVersionString[i], str.length()) == 0) { | |
73 return static_cast<HTTPMessage::Version>(i); | |
74 } | |
75 } | |
76 return HttpConstants::HTTP_UNKNOWN; | |
77 } | |
78 | |
79 const char* HTTPMessage::MethodToString(Method method) { | |
80 CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString)); | |
81 return kMethodString[method]; | |
82 } | |
83 | |
84 const char* HTTPMessage::VersionToString(Version version) { | |
85 CHECK_LT(static_cast<size_t>(version), arraysize(kHTTPVersionString)); | |
86 return kHTTPVersionString[version]; | |
87 } | |
88 | |
89 HTTPMessage::HTTPMessage() : is_request_(true) { | |
90 InitializeFields(); | |
91 } | |
92 | |
93 HTTPMessage::HTTPMessage(Version ver, Method request, const string& path) | |
94 : is_request_(true) { | |
95 InitializeFields(); | |
96 if (ver != HttpConstants::HTTP_0_9) { | |
97 headers()->SetRequestVersion(VersionToString(ver)); | |
98 } | |
99 headers()->SetRequestMethod(MethodToString(request)); | |
100 headers()->SetRequestUri(path); | |
101 } | |
102 | |
103 HTTPMessage::~HTTPMessage() {} | |
104 | |
105 void HTTPMessage::InitializeFields() { | |
106 has_complete_message_ = true; | |
107 skip_message_validation_ = false; | |
108 } | |
109 | |
110 void HTTPMessage::AddHeader(const string& header, const string& value) { | |
111 headers()->AppendHeader(header, value); | |
112 } | |
113 | |
114 void HTTPMessage::RemoveHeader(const string& header) { | |
115 headers()->RemoveAllOfHeader(header); | |
116 } | |
117 | |
118 void HTTPMessage::ReplaceHeader(const string& header, const string& value) { | |
119 headers()->ReplaceOrAppendHeader(header, value); | |
120 } | |
121 | |
122 void HTTPMessage::AddBody(const string& body, bool add_content_length) { | |
123 body_ = body; | |
124 // Remove any transfer-encoding that was left by a previous body. | |
125 RemoveHeader(kTransferCoding); | |
126 if (add_content_length) { | |
127 ReplaceHeader(kContentLength, base::SizeTToString(body.size())); | |
128 } else { | |
129 RemoveHeader(kContentLength); | |
130 } | |
131 } | |
132 | |
133 void HTTPMessage::ValidateMessage() const { | |
134 if (skip_message_validation_) { | |
135 return; | |
136 } | |
137 | |
138 vector<StringPiece> transfer_encodings; | |
139 headers()->GetAllOfHeader(kTransferCoding, &transfer_encodings); | |
140 CHECK_GE(1ul, transfer_encodings.size()); | |
141 for (vector<StringPiece>::iterator it = transfer_encodings.begin(); | |
142 it != transfer_encodings.end(); ++it) { | |
143 CHECK(base::EqualsCaseInsensitiveASCII("identity", *it) || | |
144 base::EqualsCaseInsensitiveASCII("chunked", *it)) | |
145 << *it; | |
146 } | |
147 | |
148 vector<StringPiece> content_lengths; | |
149 headers()->GetAllOfHeader(kContentLength, &content_lengths); | |
150 CHECK_GE(1ul, content_lengths.size()); | |
151 | |
152 CHECK_EQ(has_complete_message_, IsCompleteMessage(*this)); | |
153 } | |
154 | |
155 } // namespace test | |
156 } // namespace net | |
OLD | NEW |