blob: 624b97501989a1df8e2100116b3d3172d895959a [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/websockets/websocket_basic_stream_adapters.h"
#include <algorithm>
#include <cstring>
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/io_buffer.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/socket.h"
#include "net/spdy/spdy_buffer.h"
#include "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_stream.h"
#include "net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.h"
#include "net/websockets/websocket_quic_spdy_stream.h"
namespace net {
WebSocketClientSocketHandleAdapter::WebSocketClientSocketHandleAdapter(
std::unique_ptr<ClientSocketHandle> connection)
: connection_(std::move(connection)) {}
WebSocketClientSocketHandleAdapter::~WebSocketClientSocketHandleAdapter() =
default;
int WebSocketClientSocketHandleAdapter::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
return connection_->socket()->Read(buf, buf_len, std::move(callback));
}
int WebSocketClientSocketHandleAdapter::Write(
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) {
return connection_->socket()->Write(buf, buf_len, std::move(callback),
traffic_annotation);
}
void WebSocketClientSocketHandleAdapter::Disconnect() {
connection_->socket()->Disconnect();
}
bool WebSocketClientSocketHandleAdapter::is_initialized() const {
return connection_->is_initialized();
}
WebSocketSpdyStreamAdapter::WebSocketSpdyStreamAdapter(
base::WeakPtr<SpdyStream> stream,
Delegate* delegate,
NetLogWithSource net_log)
: stream_(stream), delegate_(delegate), net_log_(net_log) {
stream_->SetDelegate(this);
}
WebSocketSpdyStreamAdapter::~WebSocketSpdyStreamAdapter() {
if (stream_) {
// DetachDelegate() also cancels the stream.
stream_->DetachDelegate();
}
}
void WebSocketSpdyStreamAdapter::DetachDelegate() {
delegate_ = nullptr;
}
int WebSocketSpdyStreamAdapter::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!read_callback_);
DCHECK_LT(0, buf_len);
DCHECK(!read_buffer_);
read_buffer_ = buf;
// |read_length_| is size_t and |buf_len| is a non-negative int, therefore
// conversion is always valid.
DCHECK(!read_length_);
read_length_ = buf_len;
if (!read_data_.IsEmpty())
return CopySavedReadDataIntoBuffer();
if (!stream_)
return stream_error_;
read_callback_ = std::move(callback);
return ERR_IO_PENDING;
}
int WebSocketSpdyStreamAdapter::Write(
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) {
CHECK(headers_sent_);
DCHECK(!write_callback_);
DCHECK(callback);
DCHECK_LT(0, buf_len);
if (!stream_)
return stream_error_;
stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND);
write_callback_ = std::move(callback);
write_length_ = buf_len;
return ERR_IO_PENDING;
}
void WebSocketSpdyStreamAdapter::Disconnect() {
if (stream_) {
stream_->DetachDelegate();
stream_ = nullptr;
}
}
bool WebSocketSpdyStreamAdapter::is_initialized() const {
return true;
}
// SpdyStream::Delegate methods.
void WebSocketSpdyStreamAdapter::OnHeadersSent() {
headers_sent_ = true;
if (delegate_)
delegate_->OnHeadersSent();
}
void WebSocketSpdyStreamAdapter::OnEarlyHintsReceived(
const spdy::Http2HeaderBlock& headers) {
// This callback should not be called for a WebSocket handshake.
NOTREACHED();
}
void WebSocketSpdyStreamAdapter::OnHeadersReceived(
const spdy::Http2HeaderBlock& response_headers,
const spdy::Http2HeaderBlock* pushed_request_headers) {
if (delegate_)
delegate_->OnHeadersReceived(response_headers);
}
void WebSocketSpdyStreamAdapter::OnDataReceived(
std::unique_ptr<SpdyBuffer> buffer) {
if (!buffer) {
// This is slightly wrong semantically, as it's still possible to write to
// the stream at this point. However, if the server closes the stream
// without waiting for a close frame from us, that means it is not
// interested in a clean shutdown. In which case we don't need to worry
// about sending any remaining data we might have buffered. This results in
// a call to OnClose() which then informs our delegate.
stream_->Close();
return;
}
read_data_.Enqueue(std::move(buffer));
if (read_callback_)
std::move(read_callback_).Run(CopySavedReadDataIntoBuffer());
}
void WebSocketSpdyStreamAdapter::OnDataSent() {
DCHECK(write_callback_);
std::move(write_callback_).Run(write_length_);
}
void WebSocketSpdyStreamAdapter::OnTrailers(
const spdy::Http2HeaderBlock& trailers) {}
void WebSocketSpdyStreamAdapter::OnClose(int status) {
DCHECK_NE(ERR_IO_PENDING, status);
DCHECK_LE(status, 0);
if (status == OK) {
status = ERR_CONNECTION_CLOSED;
}
stream_error_ = status;
stream_ = nullptr;
auto self = weak_factory_.GetWeakPtr();
if (read_callback_) {
DCHECK(read_data_.IsEmpty());
// Might destroy |this|.
std::move(read_callback_).Run(status);
if (!self)
return;
}
if (write_callback_) {
// Might destroy |this|.
std::move(write_callback_).Run(status);
if (!self)
return;
}
// Delay calling delegate_->OnClose() until all buffered data are read.
if (read_data_.IsEmpty() && delegate_) {
// Might destroy |this|.
delegate_->OnClose(status);
}
}
bool WebSocketSpdyStreamAdapter::CanGreaseFrameType() const {
return false;
}
NetLogSource WebSocketSpdyStreamAdapter::source_dependency() const {
return net_log_.source();
}
int WebSocketSpdyStreamAdapter::CopySavedReadDataIntoBuffer() {
DCHECK(read_buffer_);
DCHECK(read_length_);
int rv = read_data_.Dequeue(read_buffer_->data(), read_length_);
read_buffer_ = nullptr;
read_length_ = 0u;
// Stream has been destroyed earlier but delegate_->OnClose() call was
// delayed until all buffered data are read. PostTask so that Read() can
// return beforehand.
if (!stream_ && delegate_ && read_data_.IsEmpty()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&WebSocketSpdyStreamAdapter::CallDelegateOnClose,
weak_factory_.GetWeakPtr()));
}
return rv;
}
void WebSocketSpdyStreamAdapter::CallDelegateOnClose() {
if (delegate_)
delegate_->OnClose(stream_error_);
}
WebSocketQuicStreamAdapter::WebSocketQuicStreamAdapter(
WebSocketQuicSpdyStream* websocket_quic_spdy_stream,
Delegate* delegate)
: websocket_quic_spdy_stream_(websocket_quic_spdy_stream),
delegate_(delegate) {
websocket_quic_spdy_stream_->set_delegate(this);
}
WebSocketQuicStreamAdapter::~WebSocketQuicStreamAdapter() {
if (websocket_quic_spdy_stream_) {
websocket_quic_spdy_stream_->set_delegate(nullptr);
}
}
size_t WebSocketQuicStreamAdapter::WriteHeaders(
spdy::Http2HeaderBlock header_block,
bool fin) {
return websocket_quic_spdy_stream_->WriteHeaders(std::move(header_block), fin,
nullptr);
}
// WebSocketBasicStream::Adapter methods.
int WebSocketQuicStreamAdapter::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
if (!websocket_quic_spdy_stream_) {
return ERR_UNEXPECTED;
}
int rv = websocket_quic_spdy_stream_->Read(buf, buf_len);
if (rv != ERR_IO_PENDING) {
return rv;
}
read_callback_ = std::move(callback);
read_buffer_ = buf;
read_length_ = buf_len;
return ERR_IO_PENDING;
}
int WebSocketQuicStreamAdapter::Write(
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) {
// TODO(momoka): Write implementation.
return OK;
}
void WebSocketQuicStreamAdapter::Disconnect() {
if (websocket_quic_spdy_stream_) {
websocket_quic_spdy_stream_->Reset(quic::QUIC_STREAM_CANCELLED);
}
}
bool WebSocketQuicStreamAdapter::is_initialized() const {
return true;
}
// WebSocketQuicSpdyStream::Delegate methods.
void WebSocketQuicStreamAdapter::OnInitialHeadersComplete(
bool fin,
size_t frame_len,
const quic::QuicHeaderList& quic_header_list) {
spdy::Http2HeaderBlock response_headers;
if (!quic::SpdyUtils::CopyAndValidateHeaders(quic_header_list, nullptr,
&response_headers)) {
DLOG(ERROR) << "Failed to parse header list: "
<< quic_header_list.DebugString();
websocket_quic_spdy_stream_->ConsumeHeaderList();
websocket_quic_spdy_stream_->Reset(quic::QUIC_BAD_APPLICATION_PAYLOAD);
return;
}
websocket_quic_spdy_stream_->ConsumeHeaderList();
delegate_->OnHeadersReceived(response_headers);
}
void WebSocketQuicStreamAdapter::OnBodyAvailable() {
if (!websocket_quic_spdy_stream_->FinishedReadingHeaders()) {
// Buffer the data in the sequencer until the headers have been read.
return;
}
if (!websocket_quic_spdy_stream_->HasBytesToRead()) {
return;
}
if (!read_callback_) {
// Wait for Read() to be called.
return;
}
DCHECK(read_buffer_);
DCHECK_GT(read_length_, 0);
int rv = websocket_quic_spdy_stream_->Read(read_buffer_, read_length_);
if (rv == ERR_IO_PENDING) {
return;
}
read_buffer_ = nullptr;
read_length_ = 0;
std::move(read_callback_).Run(rv);
}
void WebSocketQuicStreamAdapter::ClearStream() {
if (websocket_quic_spdy_stream_) {
websocket_quic_spdy_stream_ = nullptr;
}
}
} // namespace net