mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
267 lines
9.1 KiB
C++
267 lines
9.1 KiB
C++
// 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 "net/quic/core/quic_spdy_stream.h"
|
|
#include "net/quic/core/spdy_utils.h"
|
|
#include "net/quic/platform/api/quic_bug_tracker.h"
|
|
#include "net/quic/platform/api/quic_flags.h"
|
|
#include "net/quic/platform/api/quic_logging.h"
|
|
#include "net/quic/platform/api/quic_map_util.h"
|
|
#include "net/quic/platform/api/quic_text_utils.h"
|
|
#include "net/spdy/core/spdy_protocol.h"
|
|
#include "net/tools/quic/quic_http_response_cache.h"
|
|
#include "net/tools/quic/quic_simple_server_session.h"
|
|
|
|
using std::string;
|
|
|
|
namespace net {
|
|
|
|
QuicSimpleServerStream::QuicSimpleServerStream(
|
|
QuicStreamId id,
|
|
QuicSpdySession* session,
|
|
QuicHttpResponseCache* response_cache)
|
|
: QuicSpdyServerStreamBase(id, session),
|
|
content_length_(-1),
|
|
response_cache_(response_cache) {}
|
|
|
|
QuicSimpleServerStream::~QuicSimpleServerStream() {}
|
|
|
|
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_)) {
|
|
QUIC_DVLOG(1) << "Invalid headers";
|
|
SendErrorResponse();
|
|
}
|
|
ConsumeHeaderList();
|
|
}
|
|
|
|
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;
|
|
}
|
|
QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
|
|
<< " bytes.";
|
|
body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
|
|
|
|
if (content_length_ >= 0 &&
|
|
body_.size() > static_cast<uint64_t>(content_length_)) {
|
|
QUIC_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;
|
|
}
|
|
|
|
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;
|
|
QUIC_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, QuicHeaderList());
|
|
}
|
|
|
|
void QuicSimpleServerStream::SendResponse() {
|
|
if (request_headers_.empty()) {
|
|
QUIC_DVLOG(1) << "Request headers empty.";
|
|
SendErrorResponse();
|
|
return;
|
|
}
|
|
|
|
if (content_length_ > 0 &&
|
|
static_cast<uint64_t>(content_length_) != body_.size()) {
|
|
QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
|
|
<< body_.size() << ").";
|
|
SendErrorResponse();
|
|
return;
|
|
}
|
|
|
|
if (!QuicContainsKey(request_headers_, ":authority") ||
|
|
!QuicContainsKey(request_headers_, ":path")) {
|
|
QUIC_DVLOG(1) << "Request headers do not contain :authority or :path.";
|
|
SendErrorResponse();
|
|
return;
|
|
}
|
|
|
|
// Find response in cache. If not found, send error response.
|
|
const QuicHttpResponseCache::Response* response = nullptr;
|
|
auto authority = request_headers_.find(":authority");
|
|
auto path = request_headers_.find(":path");
|
|
if (authority != request_headers_.end() && path != request_headers_.end()) {
|
|
response = response_cache_->GetResponse(authority->second, path->second);
|
|
}
|
|
if (response == nullptr) {
|
|
QUIC_DVLOG(1) << "Response not found in cache.";
|
|
SendNotFoundResponse();
|
|
return;
|
|
}
|
|
|
|
if (response->response_type() == QuicHttpResponseCache::CLOSE_CONNECTION) {
|
|
QUIC_DVLOG(1) << "Special response: closing connection.";
|
|
CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
|
|
return;
|
|
}
|
|
|
|
if (response->response_type() == QuicHttpResponseCache::IGNORE_REQUEST) {
|
|
QUIC_DVLOG(1) << "Special response: ignoring request.";
|
|
return;
|
|
}
|
|
|
|
// Examing response status, if it was not pure integer as typical h2
|
|
// response status, send error response. Notice that
|
|
// QuicHttpResponseCache push urls are strictly authority + path only,
|
|
// scheme is not included (see |QuicHttpResponseCache::GetKey()|).
|
|
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)) {
|
|
auto status = response_headers.find(":status");
|
|
if (status == response_headers.end()) {
|
|
QUIC_LOG(WARNING)
|
|
<< ":status not present in response from cache for request "
|
|
<< request_url;
|
|
} else {
|
|
QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
|
|
<< status->second << " 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) {
|
|
QUIC_LOG(WARNING) << "Response to server push request " << request_url
|
|
<< " result in response code " << response_code;
|
|
Reset(QUIC_STREAM_CANCELLED);
|
|
return;
|
|
}
|
|
}
|
|
std::list<QuicHttpResponseCache::ServerPushInfo> resources =
|
|
response_cache_->GetServerPushResources(request_url);
|
|
QUIC_DVLOG(1) << "Stream " << id() << " found " << resources.size()
|
|
<< " push resources.";
|
|
|
|
if (!resources.empty()) {
|
|
QuicSimpleServerSession* session =
|
|
static_cast<QuicSimpleServerSession*>(spdy_session());
|
|
session->PromisePushResources(request_url, resources, id(),
|
|
request_headers_);
|
|
}
|
|
|
|
QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
|
|
SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
|
|
response->trailers().Clone());
|
|
}
|
|
|
|
void QuicSimpleServerStream::SendNotFoundResponse() {
|
|
QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
|
|
SpdyHeaderBlock headers;
|
|
headers[":status"] = "404";
|
|
headers["content-length"] =
|
|
QuicTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
|
|
SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
|
|
}
|
|
|
|
void QuicSimpleServerStream::SendErrorResponse() {
|
|
QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
|
|
SpdyHeaderBlock headers;
|
|
headers[":status"] = "500";
|
|
headers["content-length"] =
|
|
QuicTextUtils::Uint64ToString(strlen(kErrorResponseBody));
|
|
SendHeadersAndBody(std::move(headers), kErrorResponseBody);
|
|
}
|
|
|
|
void QuicSimpleServerStream::SendHeadersAndBody(
|
|
SpdyHeaderBlock response_headers,
|
|
QuicStringPiece body) {
|
|
SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
|
|
SpdyHeaderBlock());
|
|
}
|
|
|
|
void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
|
|
SpdyHeaderBlock response_headers,
|
|
QuicStringPiece body,
|
|
SpdyHeaderBlock response_trailers) {
|
|
// Send the headers, with a FIN if there's nothing else to send.
|
|
bool send_fin = (body.empty() && response_trailers.empty());
|
|
QUIC_DLOG(INFO) << "Stream " << id() << " 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 no trailers to send.
|
|
send_fin = response_trailers.empty();
|
|
QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
|
|
<< ") with size: " << body.size();
|
|
if (!body.empty() || send_fin) {
|
|
WriteOrBufferData(body, send_fin, nullptr);
|
|
}
|
|
if (send_fin) {
|
|
// Nothing else to send.
|
|
return;
|
|
}
|
|
|
|
// Send the trailers. A FIN is always sent with trailers.
|
|
QUIC_DLOG(INFO) << "Stream " << id() << " 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
|