Make the toy QUIC server send responses of arbitrary length based a number specified in the path. gfe-relnote: n/a - Tools-only code PiperOrigin-RevId: 274840831 Change-Id: Ia22584f9ae202896f268a47172e15e8d1a9a75f8
diff --git a/quic/tools/quic_backend_response.h b/quic/tools/quic_backend_response.h index b578915..f0b7b89 100644 --- a/quic/tools/quic_backend_response.h +++ b/quic/tools/quic_backend_response.h
@@ -42,6 +42,8 @@ STOP_SENDING, // Acts like INCOMPLETE_RESPONSE in that the entire // response is not sent. After sending what is sent, // the server will send a STOP_SENDING. + GENERATE_BYTES // Sends a response with a length equal to the number + // of bytes in the URL path. }; QuicBackendResponse();
diff --git a/quic/tools/quic_memory_cache_backend.cc b/quic/tools/quic_memory_cache_backend.cc index fb3dd1f..9fe9dc5 100644 --- a/quic/tools/quic_memory_cache_backend.cc +++ b/quic/tools/quic_memory_cache_backend.cc
@@ -136,6 +136,15 @@ auto it = responses_.find(GetKey(host, path)); if (it == responses_.end()) { + uint64_t ignored = 0; + if (generate_bytes_response_) { + if (QuicTextUtils::StringToUint64( + QuicStringPiece(&path[1], path.size() - 1), &ignored)) { + // The actual parsed length is ignored here and will be recomputed + // by the caller. + return generate_bytes_response_.get(); + } + } QUIC_DVLOG(1) << "Get response for resource failed: host " << host << " path " << path; if (default_response_) { @@ -271,10 +280,23 @@ MaybeAddServerPushResources(resource_file->host(), resource_file->path(), push_resources); } + cache_initialized_ = true; return true; } +void QuicMemoryCacheBackend::GenerateDynamicResponses() { + QuicWriterMutexLock lock(&response_mutex_); + // Add a generate bytes response. + spdy::SpdyHeaderBlock response_headers; + response_headers[":version"] = "HTTP/1.1"; + response_headers[":status"] = "200"; + generate_bytes_response_ = std::make_unique<QuicBackendResponse>(); + generate_bytes_response_->set_headers(std::move(response_headers)); + generate_bytes_response_->set_response_type( + QuicBackendResponse::GENERATE_BYTES); +} + bool QuicMemoryCacheBackend::IsBackendInitialized() const { return cache_initialized_; }
diff --git a/quic/tools/quic_memory_cache_backend.h b/quic/tools/quic_memory_cache_backend.h index 8e202c0..e0ff400 100644 --- a/quic/tools/quic_memory_cache_backend.h +++ b/quic/tools/quic_memory_cache_backend.h
@@ -137,6 +137,10 @@ // |cache_cirectory| can be generated using `wget -p --save-headers <url>`. void InitializeFromDirectory(const std::string& cache_directory); + // Once called, URLs which have a numeric path will send a dynamically + // generated response of that many bytes. + void GenerateDynamicResponses(); + // Find all the server push resources associated with |request_url|. std::list<QuicBackendResponse::ServerPushInfo> GetServerPushResources( std::string request_url); @@ -183,6 +187,10 @@ std::unique_ptr<QuicBackendResponse> default_response_ QUIC_GUARDED_BY(response_mutex_); + // The generate bytes response, if set. + std::unique_ptr<QuicBackendResponse> generate_bytes_response_ + QUIC_GUARDED_BY(response_mutex_); + // A map from request URL to associated server push responses (if any). std::multimap<std::string, QuicBackendResponse::ServerPushInfo> server_push_resources_ QUIC_GUARDED_BY(response_mutex_);
diff --git a/quic/tools/quic_simple_server_stream.cc b/quic/tools/quic_simple_server_stream.cc index 5badfe1..f821a5d 100644 --- a/quic/tools/quic_simple_server_stream.cc +++ b/quic/tools/quic_simple_server_stream.cc
@@ -29,6 +29,7 @@ QuicSimpleServerBackend* quic_simple_server_backend) : QuicSpdyServerStreamBase(id, session, type), content_length_(-1), + generate_bytes_length_(0), quic_simple_server_backend_(quic_simple_server_backend) { DCHECK(quic_simple_server_backend_); } @@ -40,6 +41,7 @@ QuicSimpleServerBackend* quic_simple_server_backend) : QuicSpdyServerStreamBase(pending, session, type), content_length_(-1), + generate_bytes_length_(0), quic_simple_server_backend_(quic_simple_server_backend) { DCHECK(quic_simple_server_backend_); } @@ -260,11 +262,46 @@ return; } + if (response->response_type() == QuicBackendResponse::GENERATE_BYTES) { + QUIC_DVLOG(1) << "Stream " << id() << " sending a generate bytes response."; + std::string path = request_headers_[":path"].as_string().substr(1); + if (!QuicTextUtils::StringToUint64(path, &generate_bytes_length_)) { + QUIC_LOG(ERROR) << "Path is not a number."; + SendNotFoundResponse(); + return; + } + SpdyHeaderBlock headers = response->headers().Clone(); + headers["content-length"] = + QuicTextUtils::Uint64ToString(generate_bytes_length_); + + WriteHeaders(std::move(headers), false, nullptr); + + WriteGeneratedBytes(); + + return; + } + QUIC_DVLOG(1) << "Stream " << id() << " sending response."; SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(), response->trailers().Clone()); } +void QuicSimpleServerStream::OnCanWrite() { + QuicSpdyStream::OnCanWrite(); + WriteGeneratedBytes(); +} + +void QuicSimpleServerStream::WriteGeneratedBytes() { + static size_t kChunkSize = 1024; + while (!HasBufferedData() && generate_bytes_length_ > 0) { + size_t len = std::min<size_t>(kChunkSize, generate_bytes_length_); + std::string data(len, 'a'); + generate_bytes_length_ -= len; + bool fin = generate_bytes_length_ == 0; + WriteOrBufferBody(data, fin); + } +} + void QuicSimpleServerStream::SendNotFoundResponse() { QUIC_DVLOG(1) << "Stream " << id() << " sending not found response."; SpdyHeaderBlock headers;
diff --git a/quic/tools/quic_simple_server_stream.h b/quic/tools/quic_simple_server_stream.h index ace1571..594ca02 100644 --- a/quic/tools/quic_simple_server_stream.h +++ b/quic/tools/quic_simple_server_stream.h
@@ -38,6 +38,7 @@ void OnTrailingHeadersComplete(bool fin, size_t frame_len, const QuicHeaderList& header_list) override; + void OnCanWrite() override; // QuicStream implementation called by the sequencer when there is // data (or a FIN) to be read. @@ -88,12 +89,17 @@ const std::string& body() { return body_; } + // Writes the body bytes for the GENERATE_BYTES response type. + void WriteGeneratedBytes(); + // The parsed headers received from the client. spdy::SpdyHeaderBlock request_headers_; int64_t content_length_; std::string body_; private: + uint64_t generate_bytes_length_; + QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned. };
diff --git a/quic/tools/quic_toy_server.cc b/quic/tools/quic_toy_server.cc index 0b0cd3d..fdb1bb6 100644 --- a/quic/tools/quic_toy_server.cc +++ b/quic/tools/quic_toy_server.cc
@@ -26,6 +26,13 @@ "construction to seed the cache. Cache directory can be " "generated using `wget -p --save-headers <url>`"); +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, + generate_dynamic_responses, + false, + "If true, then URLs which have a numeric path will send a dynamically " + "generated response of that many bytes."); + DEFINE_QUIC_COMMAND_LINE_FLAG(bool, quic_ietf_draft, false, @@ -37,6 +44,9 @@ std::unique_ptr<quic::QuicSimpleServerBackend> QuicToyServer::MemoryCacheBackendFactory::CreateBackend() { auto memory_cache_backend = std::make_unique<QuicMemoryCacheBackend>(); + if (GetQuicFlag(FLAGS_generate_dynamic_responses)) { + memory_cache_backend->GenerateDynamicResponses(); + } if (!GetQuicFlag(FLAGS_quic_response_cache_dir).empty()) { memory_cache_backend->InitializeBackend( GetQuicFlag(FLAGS_quic_response_cache_dir));