// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/quic/test_tools/http_message.h" #include #include "base/logging.h" #include "base/strings/string_number_conversions.h" using base::StringPiece; using std::string; using std::vector; namespace net { namespace tools { namespace test { namespace { // const char kContentEncoding[] = "content-encoding"; const char kContentLength[] = "content-length"; const char kTransferCoding[] = "transfer-encoding"; // Both kHTTPVersionString and kMethodString arrays are constructed to match // the enum values defined in Version and Method of HTTPMessage. const char* const kHTTPVersionString[] = {"", "HTTP/0.9", "HTTP/1.0", "HTTP/1.1"}; const char* const kMethodString[] = { "", "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", "MKCOL", "UNLOCK", }; // Returns true if the message represents a complete request or response. // Messages are considered complete if: // - Transfer-Encoding: chunked is present and message has a final chunk. // - Content-Length header is present and matches the message body length. // - Neither Transfer-Encoding nor Content-Length is present and message // is tagged as complete. bool IsCompleteMessage(const HTTPMessage& message) { const BalsaHeaders* headers = message.headers(); StringPiece content_length = headers->GetHeader(kContentLength); if (!content_length.empty()) { int parsed_content_length; if (!base::StringToInt(content_length, &parsed_content_length)) { return false; } return (message.body().size() == (uint)parsed_content_length); } else { // Assume messages without transfer coding or content-length are // tagged correctly. return message.has_complete_message(); } } } // namespace HTTPMessage::Method HTTPMessage::StringToMethod(StringPiece str) { // Skip the first element of the array since it is empty string. for (unsigned long i = 1; i < arraysize(kMethodString); ++i) { if (strncmp(str.data(), kMethodString[i], str.length()) == 0) { return static_cast(i); } } return HttpConstants::UNKNOWN_METHOD; } HTTPMessage::Version HTTPMessage::StringToVersion(StringPiece str) { // Skip the first element of the array since it is empty string. for (unsigned long i = 1; i < arraysize(kHTTPVersionString); ++i) { if (strncmp(str.data(), kHTTPVersionString[i], str.length()) == 0) { return static_cast(i); } } return HttpConstants::HTTP_UNKNOWN; } const char* HTTPMessage::MethodToString(Method method) { CHECK_LT(static_cast(method), arraysize(kMethodString)); return kMethodString[method]; } const char* HTTPMessage::VersionToString(Version version) { CHECK_LT(static_cast(version), arraysize(kHTTPVersionString)); return kHTTPVersionString[version]; } HTTPMessage::HTTPMessage() : is_request_(true) { InitializeFields(); } HTTPMessage::HTTPMessage(Version ver, Method request, const string& path) : is_request_(true) { InitializeFields(); if (ver != HttpConstants::HTTP_0_9) { headers()->SetRequestVersion(VersionToString(ver)); } headers()->SetRequestMethod(MethodToString(request)); headers()->SetRequestUri(path); } HTTPMessage::~HTTPMessage() {} void HTTPMessage::InitializeFields() { has_complete_message_ = true; skip_message_validation_ = false; } void HTTPMessage::AddHeader(const string& header, const string& value) { headers()->AppendHeader(header, value); } void HTTPMessage::RemoveHeader(const string& header) { headers()->RemoveAllOfHeader(header); } void HTTPMessage::ReplaceHeader(const string& header, const string& value) { headers()->ReplaceOrAppendHeader(header, value); } void HTTPMessage::AddBody(const string& body, bool add_content_length) { body_ = body; // Remove any transfer-encoding that was left by a previous body. RemoveHeader(kTransferCoding); if (add_content_length) { ReplaceHeader(kContentLength, base::SizeTToString(body.size())); } else { RemoveHeader(kContentLength); } } void HTTPMessage::ValidateMessage() const { if (skip_message_validation_) { return; } vector transfer_encodings; headers()->GetAllOfHeader(kTransferCoding, &transfer_encodings); CHECK_GE(1ul, transfer_encodings.size()); for (vector::iterator it = transfer_encodings.begin(); it != transfer_encodings.end(); ++it) { CHECK(base::EqualsCaseInsensitiveASCII("identity", *it) || base::EqualsCaseInsensitiveASCII("chunked", *it)) << *it; } vector content_lengths; headers()->GetAllOfHeader(kContentLength, &content_lengths); CHECK_GE(1ul, content_lengths.size()); CHECK_EQ(has_complete_message_, IsCompleteMessage(*this)); } } // namespace test } // namespace tools } // namespace net