blob: 2a105c2e5bf53d891a346065a4dee2d271c5a326 [file] [log] [blame]
vasilvv2a2cecc2021-03-12 15:38:24 -08001// Copyright 2021 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "quic/core/http/web_transport_http3.h"
6
7#include <memory>
8
vasilvv316f3762021-03-31 13:49:30 -07009#include "absl/strings/string_view.h"
vasilvv2a2cecc2021-03-12 15:38:24 -080010#include "quic/core/http/quic_spdy_session.h"
11#include "quic/core/http/quic_spdy_stream.h"
vasilvv316f3762021-03-31 13:49:30 -070012#include "quic/core/quic_data_reader.h"
13#include "quic/core/quic_data_writer.h"
14#include "quic/core/quic_stream.h"
15#include "quic/core/quic_types.h"
vasilvvc39b6e52021-03-16 17:42:41 -070016#include "quic/core/quic_utils.h"
vasilvv316f3762021-03-31 13:49:30 -070017#include "quic/core/quic_versions.h"
18#include "quic/platform/api/quic_bug_tracker.h"
vasilvvc39b6e52021-03-16 17:42:41 -070019#include "common/platform/api/quiche_logging.h"
vasilvv2a2cecc2021-03-12 15:38:24 -080020
vasilvv316f3762021-03-31 13:49:30 -070021#define ENDPOINT \
22 (session_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
23
vasilvv2a2cecc2021-03-12 15:38:24 -080024namespace quic {
25
26namespace {
danzh51117902021-03-17 07:47:26 -070027class QUIC_NO_EXPORT NoopWebTransportVisitor : public WebTransportVisitor {
vasilvv2a2cecc2021-03-12 15:38:24 -080028 void OnSessionReady() override {}
29 void OnIncomingBidirectionalStreamAvailable() override {}
30 void OnIncomingUnidirectionalStreamAvailable() override {}
31 void OnDatagramReceived(absl::string_view /*datagram*/) override {}
32 void OnCanCreateNewOutgoingBidirectionalStream() override {}
33 void OnCanCreateNewOutgoingUnidirectionalStream() override {}
34};
35} // namespace
36
37WebTransportHttp3::WebTransportHttp3(QuicSpdySession* session,
38 QuicSpdyStream* connect_stream,
vasilvv8101e022021-04-06 23:14:06 -070039 WebTransportSessionId id,
40 QuicDatagramFlowId flow_id)
vasilvv2a2cecc2021-03-12 15:38:24 -080041 : session_(session),
42 connect_stream_(connect_stream),
43 id_(id),
vasilvv8101e022021-04-06 23:14:06 -070044 flow_id_(flow_id),
vasilvvc39b6e52021-03-16 17:42:41 -070045 visitor_(std::make_unique<NoopWebTransportVisitor>()) {
46 QUICHE_DCHECK(session_->SupportsWebTransport());
47 QUICHE_DCHECK(IsValidWebTransportSessionId(id, session_->version()));
48 QUICHE_DCHECK_EQ(connect_stream_->id(), id);
vasilvv8101e022021-04-06 23:14:06 -070049 session_->RegisterHttp3FlowId(flow_id, this);
vasilvvc39b6e52021-03-16 17:42:41 -070050}
vasilvv2a2cecc2021-03-12 15:38:24 -080051
vasilvv316f3762021-03-31 13:49:30 -070052void WebTransportHttp3::AssociateStream(QuicStreamId stream_id) {
53 streams_.insert(stream_id);
54
55 ParsedQuicVersion version = session_->version();
56 if (QuicUtils::IsOutgoingStreamId(version, stream_id,
57 session_->perspective())) {
58 return;
59 }
60 if (QuicUtils::IsBidirectionalStreamId(stream_id, version)) {
61 incoming_bidirectional_streams_.push_back(stream_id);
62 visitor_->OnIncomingBidirectionalStreamAvailable();
63 } else {
64 incoming_unidirectional_streams_.push_back(stream_id);
65 visitor_->OnIncomingUnidirectionalStreamAvailable();
66 }
67}
68
69void WebTransportHttp3::CloseAllAssociatedStreams() {
70 // Copy the stream list before iterating over it, as calls to ResetStream()
71 // can potentially mutate the |session_| list.
72 std::vector<QuicStreamId> streams(streams_.begin(), streams_.end());
73 streams_.clear();
74 for (QuicStreamId id : streams) {
75 session_->ResetStream(id, QUIC_STREAM_WEBTRANSPORT_SESSION_GONE);
76 }
vasilvv8101e022021-04-06 23:14:06 -070077 session_->UnregisterHttp3FlowId(flow_id_);
vasilvv316f3762021-03-31 13:49:30 -070078}
79
80void WebTransportHttp3::HeadersReceived(const spdy::SpdyHeaderBlock& headers) {
81 if (session_->perspective() == Perspective::IS_CLIENT) {
82 auto it = headers.find(":status");
83 if (it == headers.end() || it->second != "200") {
84 QUIC_DVLOG(1) << ENDPOINT
85 << "Received WebTransport headers from server without "
86 "status 200, rejecting.";
87 return;
88 }
89 }
90
91 QUIC_DVLOG(1) << ENDPOINT << "WebTransport session " << id_ << " ready.";
vasilvv2a2cecc2021-03-12 15:38:24 -080092 ready_ = true;
93 visitor_->OnSessionReady();
vasilvv316f3762021-03-31 13:49:30 -070094 session_->ProcessBufferedWebTransportStreamsForSession(this);
vasilvv2a2cecc2021-03-12 15:38:24 -080095}
96
97WebTransportStream* WebTransportHttp3::AcceptIncomingBidirectionalStream() {
vasilvvb7b2d4f2021-04-05 18:07:13 -070098 while (!incoming_bidirectional_streams_.empty()) {
99 QuicStreamId id = incoming_bidirectional_streams_.front();
100 incoming_bidirectional_streams_.pop_front();
101 QuicSpdyStream* stream = session_->GetOrCreateSpdyDataStream(id);
102 if (stream == nullptr) {
103 // Skip the streams that were reset in between the time they were
104 // receieved and the time the client has polled for them.
105 continue;
106 }
107 return stream->web_transport_stream();
108 }
vasilvv2a2cecc2021-03-12 15:38:24 -0800109 return nullptr;
110}
vasilvv316f3762021-03-31 13:49:30 -0700111
vasilvv2a2cecc2021-03-12 15:38:24 -0800112WebTransportStream* WebTransportHttp3::AcceptIncomingUnidirectionalStream() {
vasilvv316f3762021-03-31 13:49:30 -0700113 while (!incoming_unidirectional_streams_.empty()) {
114 QuicStreamId id = incoming_unidirectional_streams_.front();
115 incoming_unidirectional_streams_.pop_front();
116 QuicStream* stream = session_->GetOrCreateStream(id);
117 if (stream == nullptr) {
118 // Skip the streams that were reset in between the time they were
119 // receieved and the time the client has polled for them.
120 continue;
121 }
122 return static_cast<WebTransportHttp3UnidirectionalStream*>(stream)
123 ->interface();
124 }
vasilvv2a2cecc2021-03-12 15:38:24 -0800125 return nullptr;
126}
127
128bool WebTransportHttp3::CanOpenNextOutgoingBidirectionalStream() {
vasilvvb7b2d4f2021-04-05 18:07:13 -0700129 return session_->CanOpenOutgoingBidirectionalWebTransportStream(id_);
vasilvv2a2cecc2021-03-12 15:38:24 -0800130}
131bool WebTransportHttp3::CanOpenNextOutgoingUnidirectionalStream() {
vasilvv316f3762021-03-31 13:49:30 -0700132 return session_->CanOpenOutgoingUnidirectionalWebTransportStream(id_);
vasilvv2a2cecc2021-03-12 15:38:24 -0800133}
134WebTransportStream* WebTransportHttp3::OpenOutgoingBidirectionalStream() {
vasilvvb7b2d4f2021-04-05 18:07:13 -0700135 QuicSpdyStream* stream =
136 session_->CreateOutgoingBidirectionalWebTransportStream(this);
137 if (stream == nullptr) {
138 // If stream cannot be created due to flow control or other errors, return
139 // nullptr.
140 return nullptr;
141 }
142 return stream->web_transport_stream();
vasilvv2a2cecc2021-03-12 15:38:24 -0800143}
vasilvv316f3762021-03-31 13:49:30 -0700144
vasilvv2a2cecc2021-03-12 15:38:24 -0800145WebTransportStream* WebTransportHttp3::OpenOutgoingUnidirectionalStream() {
vasilvv316f3762021-03-31 13:49:30 -0700146 WebTransportHttp3UnidirectionalStream* stream =
147 session_->CreateOutgoingUnidirectionalWebTransportStream(this);
148 if (stream == nullptr) {
149 // If stream cannot be created due to flow control, return nullptr.
150 return nullptr;
151 }
152 return stream->interface();
vasilvv2a2cecc2021-03-12 15:38:24 -0800153}
154
vasilvv8101e022021-04-06 23:14:06 -0700155MessageStatus WebTransportHttp3::SendOrQueueDatagram(QuicMemSlice datagram) {
156 return session_->SendHttp3Datagram(
157 flow_id_, absl::string_view(datagram.data(), datagram.length()));
vasilvv2a2cecc2021-03-12 15:38:24 -0800158}
vasilvv8101e022021-04-06 23:14:06 -0700159
vasilvv2a2cecc2021-03-12 15:38:24 -0800160void WebTransportHttp3::SetDatagramMaxTimeInQueue(
vasilvv8101e022021-04-06 23:14:06 -0700161 QuicTime::Delta max_time_in_queue) {
162 session_->SetMaxTimeInQueueForFlowId(flow_id_, max_time_in_queue);
163}
164
165void WebTransportHttp3::OnHttp3Datagram(QuicDatagramFlowId flow_id,
166 absl::string_view payload) {
167 QUICHE_DCHECK_EQ(flow_id, flow_id_);
168 visitor_->OnDatagramReceived(payload);
vasilvv2a2cecc2021-03-12 15:38:24 -0800169}
170
vasilvv316f3762021-03-31 13:49:30 -0700171WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream(
172 PendingStream* pending,
173 QuicSpdySession* session)
174 : QuicStream(pending, session, READ_UNIDIRECTIONAL, /*is_static=*/false),
175 session_(session),
176 adapter_(session, this, sequencer()),
177 needs_to_send_preamble_(false) {}
178
179WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream(
180 QuicStreamId id,
181 QuicSpdySession* session,
182 WebTransportSessionId session_id)
183 : QuicStream(id, session, /*is_static=*/false, WRITE_UNIDIRECTIONAL),
184 session_(session),
185 adapter_(session, this, sequencer()),
186 session_id_(session_id),
187 needs_to_send_preamble_(true) {}
188
189void WebTransportHttp3UnidirectionalStream::WritePreamble() {
190 if (!needs_to_send_preamble_ || !session_id_.has_value()) {
191 QUIC_BUG(WebTransportHttp3UnidirectionalStream duplicate preamble)
192 << ENDPOINT << "Sending preamble on stream ID " << id()
193 << " at the wrong time.";
194 OnUnrecoverableError(QUIC_INTERNAL_ERROR,
195 "Attempting to send a WebTransport unidirectional "
196 "stream preamble at the wrong time.");
197 return;
198 }
199
200 QuicConnection::ScopedPacketFlusher flusher(session_->connection());
201 char buffer[sizeof(uint64_t) * 2]; // varint62, varint62
202 QuicDataWriter writer(sizeof(buffer), buffer);
203 bool success = true;
204 success = success && writer.WriteVarInt62(kWebTransportUnidirectionalStream);
205 success = success && writer.WriteVarInt62(*session_id_);
206 QUICHE_DCHECK(success);
207 WriteOrBufferData(absl::string_view(buffer, writer.length()), /*fin=*/false,
208 /*ack_listener=*/nullptr);
209 QUIC_DVLOG(1) << ENDPOINT << "Sent stream type and session ID ("
210 << *session_id_ << ") on WebTransport stream " << id();
211 needs_to_send_preamble_ = false;
212}
213
214bool WebTransportHttp3UnidirectionalStream::ReadSessionId() {
215 iovec iov;
216 if (!sequencer()->GetReadableRegion(&iov)) {
217 return false;
218 }
219 QuicDataReader reader(static_cast<const char*>(iov.iov_base), iov.iov_len);
220 WebTransportSessionId session_id;
221 uint8_t session_id_length = reader.PeekVarInt62Length();
222 if (!reader.ReadVarInt62(&session_id)) {
223 // If all of the data has been received, and we still cannot associate the
224 // stream with a session, consume all of the data so that the stream can
225 // be closed.
226 if (sequencer()->NumBytesConsumed() + sequencer()->NumBytesBuffered() >=
227 sequencer()->close_offset()) {
228 QUIC_DLOG(WARNING)
229 << ENDPOINT << "Failed to associate WebTransport stream " << id()
230 << " with a session because the stream ended prematurely.";
231 sequencer()->MarkConsumed(sequencer()->NumBytesBuffered());
232 }
233 return false;
234 }
235 sequencer()->MarkConsumed(session_id_length);
236 session_id_ = session_id;
237 session_->AssociateIncomingWebTransportStreamWithSession(session_id, id());
238 return true;
239}
240
241void WebTransportHttp3UnidirectionalStream::OnDataAvailable() {
242 if (!session_id_.has_value()) {
243 if (!ReadSessionId()) {
244 return;
245 }
246 }
247
248 adapter_.OnDataAvailable();
249}
250
251void WebTransportHttp3UnidirectionalStream::OnCanWriteNewData() {
252 adapter_.OnCanWriteNewData();
253}
254
255void WebTransportHttp3UnidirectionalStream::OnClose() {
256 QuicStream::OnClose();
257
258 if (!session_id_.has_value()) {
259 return;
260 }
261 WebTransportHttp3* session = session_->GetWebTransportSession(*session_id_);
262 if (session == nullptr) {
263 QUIC_DLOG(WARNING) << ENDPOINT << "WebTransport stream " << id()
264 << " attempted to notify parent session " << *session_id_
265 << ", but the session could not be found.";
266 return;
267 }
268 session->OnStreamClosed(id());
269}
270
vasilvv2a2cecc2021-03-12 15:38:24 -0800271} // namespace quic