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
13using base::StringPiece;
14using std::string;
15using std::vector;
16
17namespace net {
18namespace tools {
19namespace test {
20
21namespace {
22
23//const char* kContentEncoding = "content-encoding";
24const char* kContentLength = "content-length";
25const 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.
29const char* kHTTPVersionString[] = {
30  "",
31  "HTTP/0.9",
32  "HTTP/1.0",
33  "HTTP/1.1"
34};
35
36const 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.
56bool 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
74HTTPMessage::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
84HTTPMessage::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
94const char* HTTPMessage::MethodToString(Method method) {
95  CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString));
96  return kMethodString[method];
97}
98
99const char* HTTPMessage::VersionToString(Version version) {
100  CHECK_LT(static_cast<size_t>(version), arraysize(kHTTPVersionString));
101  return kHTTPVersionString[version];
102}
103
104HTTPMessage::HTTPMessage()
105    : is_request_(true) {
106  InitializeFields();
107}
108
109HTTPMessage::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
119HTTPMessage::~HTTPMessage() {
120}
121
122void HTTPMessage::InitializeFields() {
123  has_complete_message_ = true;
124  skip_message_validation_ = false;
125}
126
127void HTTPMessage::AddHeader(const string& header, const string& value) {
128  headers()->AppendHeader(header, value);
129}
130
131void HTTPMessage::RemoveHeader(const string& header) {
132  headers()->RemoveAllOfHeader(header);
133}
134
135void HTTPMessage::ReplaceHeader(const string& header, const string& value) {
136  headers()->ReplaceOrAppendHeader(header, value);
137}
138
139void 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
150void 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