• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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