root/net/tools/quic/test_tools/http_message.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IsCompleteMessage
  2. StringToMethod
  3. StringToVersion
  4. MethodToString
  5. VersionToString
  6. InitializeFields
  7. AddHeader
  8. RemoveHeader
  9. ReplaceHeader
  10. AddBody
  11. ValidateMessage

// 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 <vector>

#include "base/basictypes.h"
#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* kHTTPVersionString[] = {
  "",
  "HTTP/0.9",
  "HTTP/1.0",
  "HTTP/1.1"
};

const char* 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<HTTPMessage::Method>(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<HTTPMessage::Version>(i);
    }
  }
  return HttpConstants::HTTP_UNKNOWN;
}

const char* HTTPMessage::MethodToString(Method method) {
  CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString));
  return kMethodString[method];
}

const char* HTTPMessage::VersionToString(Version version) {
  CHECK_LT(static_cast<size_t>(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::IntToString(body.size()));
  } else {
    RemoveHeader(kContentLength);
  }
}

void HTTPMessage::ValidateMessage() const {
  if (skip_message_validation_) {
    return;
  }

  vector<StringPiece> transfer_encodings;
  headers()->GetAllOfHeader(kTransferCoding, &transfer_encodings);
  CHECK_GE(1ul, transfer_encodings.size());
  for (vector<StringPiece>::iterator it = transfer_encodings.begin();
       it != transfer_encodings.end();
       ++it) {
    CHECK(StringPieceUtils::EqualIgnoreCase("identity", *it) ||
          StringPieceUtils::EqualIgnoreCase("chunked", *it)) << *it;
  }

  vector<StringPiece> 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

/* [<][>][^][v][top][bottom][index][help] */