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* kHTTPVersionString[] = {
30 "",
31 "HTTP/0.9",
32 "HTTP/1.0",
33 "HTTP/1.1"
34 };
35
36 const char* 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.
IsCompleteMessage(const HTTPMessage & message)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
StringToMethod(StringPiece str)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
StringToVersion(StringPiece str)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
MethodToString(Method method)94 const char* HTTPMessage::MethodToString(Method method) {
95 CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString));
96 return kMethodString[method];
97 }
98
VersionToString(Version version)99 const char* HTTPMessage::VersionToString(Version version) {
100 CHECK_LT(static_cast<size_t>(version), arraysize(kHTTPVersionString));
101 return kHTTPVersionString[version];
102 }
103
HTTPMessage()104 HTTPMessage::HTTPMessage()
105 : is_request_(true) {
106 InitializeFields();
107 }
108
HTTPMessage(Version ver,Method request,const string & path)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
~HTTPMessage()119 HTTPMessage::~HTTPMessage() {
120 }
121
InitializeFields()122 void HTTPMessage::InitializeFields() {
123 has_complete_message_ = true;
124 skip_message_validation_ = false;
125 }
126
AddHeader(const string & header,const string & value)127 void HTTPMessage::AddHeader(const string& header, const string& value) {
128 headers()->AppendHeader(header, value);
129 }
130
RemoveHeader(const string & header)131 void HTTPMessage::RemoveHeader(const string& header) {
132 headers()->RemoveAllOfHeader(header);
133 }
134
ReplaceHeader(const string & header,const string & value)135 void HTTPMessage::ReplaceHeader(const string& header, const string& value) {
136 headers()->ReplaceOrAppendHeader(header, value);
137 }
138
AddBody(const string & body,bool add_content_length)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
ValidateMessage() const150 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
175