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