blob: dda6a420d180bfe726b08222959a97537835c6bc [file] [log] [blame]
// Copyright (c) 2012 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/quic_simple_server_stream.h"
#include <list>
#include <utility>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "net/quic/quic_bug_tracker.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_spdy_stream.h"
#include "net/quic/spdy_utils.h"
#include "net/spdy/spdy_protocol.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/quic_simple_server_session.h"
using base::StringPiece;
using base::StringToInt;
using std::string;
namespace net {
QuicSimpleServerStream::QuicSimpleServerStream(QuicStreamId id,
QuicSpdySession* session)
: QuicSpdyStream(id, session), content_length_(-1) {}
QuicSimpleServerStream::~QuicSimpleServerStream() {}
void QuicSimpleServerStream::OnInitialHeadersComplete(bool fin,
size_t frame_len) {
QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len);
if (!SpdyUtils::ParseHeaders(decompressed_headers().data(),
decompressed_headers().length(),
&content_length_, &request_headers_)) {
DVLOG(1) << "Invalid headers";
SendErrorResponse();
}
MarkHeadersConsumed(decompressed_headers().length());
}
void QuicSimpleServerStream::OnInitialHeadersComplete(
bool fin,
size_t frame_len,
const QuicHeaderList& header_list) {
QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
&request_headers_)) {
DVLOG(1) << "Invalid headers";
SendErrorResponse();
}
ConsumeHeaderList();
}
void QuicSimpleServerStream::OnTrailingHeadersComplete(bool fin,
size_t frame_len) {
QUIC_BUG << "Server does not support receiving Trailers.";
SendErrorResponse();
}
void QuicSimpleServerStream::OnTrailingHeadersComplete(
bool fin,
size_t frame_len,
const QuicHeaderList& header_list) {
QUIC_BUG << "Server does not support receiving Trailers.";
SendErrorResponse();
}
void QuicSimpleServerStream::OnDataAvailable() {
while (HasBytesToRead()) {
struct iovec iov;
if (GetReadableRegions(&iov, 1) == 0) {
// No more data to read.
break;
}
DVLOG(1) << "Processed " << iov.iov_len << " bytes for stream " << id();
body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
if (content_length_ >= 0 &&
body_.size() > static_cast<uint64_t>(content_length_)) {
DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
<< content_length_ << ").";
SendErrorResponse();
return;
}
MarkConsumed(iov.iov_len);
}
if (!sequencer()->IsClosed()) {
sequencer()->SetUnblocked();
return;
}
// If the sequencer is closed, then all the body, including the fin, has been
// consumed.
OnFinRead();
if (write_side_closed() || fin_buffered()) {
return;
}
if (request_headers_.empty()) {
DVLOG(1) << "Request headers empty.";
SendErrorResponse();
return;
}
if (content_length_ > 0 &&
static_cast<uint64_t>(content_length_) != body_.size()) {
DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
<< body_.size() << ").";
SendErrorResponse();
return;
}
SendResponse();
}
void QuicSimpleServerStream::PushResponse(
SpdyHeaderBlock push_request_headers) {
if (id() % 2 != 0) {
QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
return;
}
// Change the stream state to emulate a client request.
request_headers_ = std::move(push_request_headers);
content_length_ = 0;
DVLOG(1) << "Stream " << id() << ": Ready to receive server push response.";
// Set as if stream decompresed the headers and received fin.
QuicSpdyStream::OnInitialHeadersComplete(/*fin=*/true, 0);
}
void QuicSimpleServerStream::SendResponse() {
if (!ContainsKey(request_headers_, ":authority") ||
!ContainsKey(request_headers_, ":path")) {
DVLOG(1) << "Request headers do not contain :authority or :path.";
SendErrorResponse();
return;
}
// Find response in cache. If not found, send error response.
const QuicInMemoryCache::Response* response =
QuicInMemoryCache::GetInstance()->GetResponse(
request_headers_[":authority"], request_headers_[":path"]);
if (response == nullptr) {
DVLOG(1) << "Response not found in cache.";
SendNotFoundResponse();
return;
}
if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
DVLOG(1) << "Special response: closing connection.";
CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
return;
}
if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
DVLOG(1) << "Special response: ignoring request.";
return;
}
// Examing response status, if it was not pure integer as typical h2 response
// status, send error response.
string request_url = request_headers_[":authority"].as_string() +
request_headers_[":path"].as_string();
int response_code;
const SpdyHeaderBlock& response_headers = response->headers();
if (!ParseHeaderStatusCode(response_headers, &response_code)) {
LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
<< response_headers.GetHeader(":status") << " for request "
<< request_url;
SendErrorResponse();
return;
}
if (id() % 2 == 0) {
// A server initiated stream is only used for a server push response,
// and only 200 and 30X response codes are supported for server push.
// This behavior mirrors the HTTP/2 implementation.
bool is_redirection = response_code / 100 == 3;
if (response_code != 200 && !is_redirection) {
LOG(WARNING) << "Response to server push request " << request_url
<< " result in response code " << response_code;
Reset(QUIC_STREAM_CANCELLED);
return;
}
}
std::list<QuicInMemoryCache::ServerPushInfo> resources =
QuicInMemoryCache::GetInstance()->GetServerPushResources(request_url);
DVLOG(1) << "Found " << resources.size() << " push resources for stream "
<< id();
if (!resources.empty()) {
QuicSimpleServerSession* session =
static_cast<QuicSimpleServerSession*>(spdy_session());
session->PromisePushResources(request_url, resources, id(),
request_headers_);
}
DVLOG(1) << "Sending response for stream " << id();
SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
response->trailers().Clone());
}
void QuicSimpleServerStream::SendNotFoundResponse() {
DVLOG(1) << "Sending not found response for stream " << id();
SpdyHeaderBlock headers;
headers[":status"] = "404";
headers["content-length"] = base::IntToString(strlen(kNotFoundResponseBody));
SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
}
void QuicSimpleServerStream::SendErrorResponse() {
DVLOG(1) << "Sending error response for stream " << id();
SpdyHeaderBlock headers;
headers[":status"] = "500";
headers["content-length"] = base::UintToString(strlen(kErrorResponseBody));
SendHeadersAndBody(std::move(headers), kErrorResponseBody);
}
void QuicSimpleServerStream::SendHeadersAndBody(
SpdyHeaderBlock response_headers,
StringPiece body) {
SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
SpdyHeaderBlock());
}
void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
SpdyHeaderBlock response_headers,
StringPiece body,
SpdyHeaderBlock response_trailers) {
// This server only supports SPDY and HTTP, and neither handles bidirectional
// streaming.
if (!reading_stopped()) {
StopReading();
}
// Send the headers, with a FIN if there's nothing else to send.
bool send_fin = (body.empty() && response_trailers.empty());
DVLOG(1) << "Writing headers (fin = " << send_fin
<< ") : " << response_headers.DebugString();
WriteHeaders(std::move(response_headers), send_fin, nullptr);
if (send_fin) {
// Nothing else to send.
return;
}
// Send the body, with a FIN if there's nothing else to send.
send_fin = response_trailers.empty();
DVLOG(1) << "Writing body (fin = " << send_fin
<< ") with size: " << body.size();
WriteOrBufferData(body, send_fin, nullptr);
if (send_fin) {
// Nothing else to send.
return;
}
// Send the trailers. A FIN is always sent with trailers.
DVLOG(1) << "Writing trailers (fin = true): "
<< response_trailers.DebugString();
WriteTrailers(std::move(response_trailers), nullptr);
}
const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
const char* const QuicSimpleServerStream::kNotFoundResponseBody =
"file not found";
} // namespace net