| // Copyright 2019 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 "quic/masque/masque_server_session.h" |
| |
| #include <netdb.h> |
| |
| #include <cstddef> |
| #include <limits> |
| |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/types/optional.h" |
| #include "quic/core/http/spdy_utils.h" |
| #include "quic/core/quic_data_reader.h" |
| #include "quic/core/quic_udp_socket.h" |
| #include "quic/tools/quic_url.h" |
| #include "common/platform/api/quiche_url_utils.h" |
| |
| namespace quic { |
| |
| namespace { |
| // RAII wrapper for QuicUdpSocketFd. |
| class FdWrapper { |
| public: |
| // Takes ownership of |fd| and closes the file descriptor on destruction. |
| explicit FdWrapper(int address_family) { |
| QuicUdpSocketApi socket_api; |
| fd_ = |
| socket_api.Create(address_family, |
| /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, |
| /*send_buffer_size =*/kDefaultSocketReceiveBuffer); |
| } |
| |
| ~FdWrapper() { |
| if (fd_ == kQuicInvalidSocketFd) { |
| return; |
| } |
| QuicUdpSocketApi socket_api; |
| socket_api.Destroy(fd_); |
| } |
| |
| // Hands ownership of the file descriptor to the caller. |
| QuicUdpSocketFd extract_fd() { |
| QuicUdpSocketFd fd = fd_; |
| fd_ = kQuicInvalidSocketFd; |
| return fd; |
| } |
| |
| // Keeps ownership of the file descriptor. |
| QuicUdpSocketFd fd() { return fd_; } |
| |
| // Disallow copy and move. |
| FdWrapper(const FdWrapper&) = delete; |
| FdWrapper(FdWrapper&&) = delete; |
| FdWrapper& operator=(const FdWrapper&) = delete; |
| FdWrapper& operator=(FdWrapper&&) = delete; |
| |
| private: |
| QuicUdpSocketFd fd_; |
| }; |
| |
| std::unique_ptr<QuicBackendResponse> CreateBackendErrorResponse( |
| absl::string_view status, absl::string_view error_details) { |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = status; |
| response_headers["masque-debug-info"] = error_details; |
| auto response = std::make_unique<QuicBackendResponse>(); |
| response->set_response_type(QuicBackendResponse::REGULAR_RESPONSE); |
| response->set_headers(std::move(response_headers)); |
| return response; |
| } |
| |
| } // namespace |
| |
| MasqueServerSession::MasqueServerSession( |
| MasqueMode masque_mode, const QuicConfig& config, |
| const ParsedQuicVersionVector& supported_versions, |
| QuicConnection* connection, QuicSession::Visitor* visitor, Visitor* owner, |
| QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper, |
| const QuicCryptoServerConfig* crypto_config, |
| QuicCompressedCertsCache* compressed_certs_cache, |
| MasqueServerBackend* masque_server_backend) |
| : QuicSimpleServerSession(config, supported_versions, connection, visitor, |
| helper, crypto_config, compressed_certs_cache, |
| masque_server_backend), |
| masque_server_backend_(masque_server_backend), |
| owner_(owner), |
| epoll_server_(epoll_server), |
| compression_engine_(this), |
| masque_mode_(masque_mode) { |
| // Artificially increase the max packet length to 1350 to ensure we can fit |
| // QUIC packets inside DATAGRAM frames. |
| // TODO(b/181606597) Remove this workaround once we use PMTUD. |
| connection->SetMaxPacketLength(kDefaultMaxPacketSize); |
| |
| masque_server_backend_->RegisterBackendClient(connection_id(), this); |
| QUICHE_DCHECK_NE(epoll_server_, nullptr); |
| } |
| |
| void MasqueServerSession::OnMessageReceived(absl::string_view message) { |
| if (masque_mode_ == MasqueMode::kLegacy) { |
| QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length(); |
| QuicConnectionId client_connection_id, server_connection_id; |
| QuicSocketAddress target_server_address; |
| std::vector<char> packet; |
| bool version_present; |
| if (!compression_engine_.DecompressDatagram( |
| message, &client_connection_id, &server_connection_id, |
| &target_server_address, &packet, &version_present)) { |
| return; |
| } |
| |
| QUIC_DVLOG(1) << "Received packet of length " << packet.size() << " for " |
| << target_server_address << " client " |
| << client_connection_id; |
| |
| if (version_present) { |
| if (client_connection_id.length() != kQuicDefaultConnectionIdLength) { |
| QUIC_DLOG(ERROR) |
| << "Dropping long header with invalid client_connection_id " |
| << client_connection_id; |
| return; |
| } |
| owner_->RegisterClientConnectionId(client_connection_id, this); |
| } |
| |
| WriteResult write_result = connection()->writer()->WritePacket( |
| packet.data(), packet.size(), connection()->self_address().host(), |
| target_server_address, nullptr); |
| QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.size() |
| << " bytes to " << target_server_address; |
| return; |
| } |
| QUICHE_DCHECK_EQ(masque_mode_, MasqueMode::kOpen); |
| QuicSpdySession::OnMessageReceived(message); |
| } |
| |
| void MasqueServerSession::OnMessageAcked(QuicMessageId message_id, |
| QuicTime /*receive_timestamp*/) { |
| QUIC_DVLOG(1) << "Received ack for DATAGRAM frame " << message_id; |
| } |
| |
| void MasqueServerSession::OnMessageLost(QuicMessageId message_id) { |
| QUIC_DVLOG(1) << "We believe DATAGRAM frame " << message_id << " was lost"; |
| } |
| |
| void MasqueServerSession::OnConnectionClosed( |
| const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) { |
| QuicSimpleServerSession::OnConnectionClosed(frame, source); |
| QUIC_DLOG(INFO) << "Closing connection for " << connection_id(); |
| masque_server_backend_->RemoveBackendClient(connection_id()); |
| // Clearing this state will close all sockets. |
| connect_udp_server_states_.clear(); |
| } |
| |
| void MasqueServerSession::OnStreamClosed(QuicStreamId stream_id) { |
| connect_udp_server_states_.remove_if( |
| [stream_id](const ConnectUdpServerState& connect_udp) { |
| return connect_udp.stream()->id() == stream_id; |
| }); |
| |
| QuicSimpleServerSession::OnStreamClosed(stream_id); |
| } |
| |
| std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( |
| const std::string& masque_path, |
| const spdy::Http2HeaderBlock& request_headers, |
| const std::string& request_body, |
| QuicSimpleServerBackend::RequestHandler* request_handler) { |
| if (masque_mode_ != MasqueMode::kLegacy) { |
| auto path_pair = request_headers.find(":path"); |
| auto scheme_pair = request_headers.find(":scheme"); |
| auto method_pair = request_headers.find(":method"); |
| auto protocol_pair = request_headers.find(":protocol"); |
| auto authority_pair = request_headers.find(":authority"); |
| if (path_pair == request_headers.end()) { |
| QUIC_DLOG(ERROR) << "MASQUE request is missing :path"; |
| return CreateBackendErrorResponse("400", "Missing :path"); |
| } |
| if (scheme_pair == request_headers.end()) { |
| QUIC_DLOG(ERROR) << "MASQUE request is missing :scheme"; |
| return CreateBackendErrorResponse("400", "Missing :scheme"); |
| } |
| if (method_pair == request_headers.end()) { |
| QUIC_DLOG(ERROR) << "MASQUE request is missing :method"; |
| return CreateBackendErrorResponse("400", "Missing :method"); |
| } |
| if (protocol_pair == request_headers.end()) { |
| QUIC_DLOG(ERROR) << "MASQUE request is missing :protocol"; |
| return CreateBackendErrorResponse("400", "Missing :protocol"); |
| } |
| if (authority_pair == request_headers.end()) { |
| QUIC_DLOG(ERROR) << "MASQUE request is missing :authority"; |
| return CreateBackendErrorResponse("400", "Missing :authority"); |
| } |
| absl::string_view path = path_pair->second; |
| absl::string_view scheme = scheme_pair->second; |
| absl::string_view method = method_pair->second; |
| absl::string_view protocol = protocol_pair->second; |
| absl::string_view authority = authority_pair->second; |
| if (path.empty()) { |
| QUIC_DLOG(ERROR) << "MASQUE request with empty path"; |
| return CreateBackendErrorResponse("400", "Empty path"); |
| } |
| if (scheme.empty()) { |
| return CreateBackendErrorResponse("400", "Empty scheme"); |
| } |
| if (method != "CONNECT") { |
| QUIC_DLOG(ERROR) << "MASQUE request with bad method \"" << method << "\""; |
| return CreateBackendErrorResponse("400", "Bad method"); |
| } |
| if (protocol != "connect-udp") { |
| QUIC_DLOG(ERROR) << "MASQUE request with bad protocol \"" << protocol |
| << "\""; |
| return CreateBackendErrorResponse("400", "Bad protocol"); |
| } |
| absl::optional<QuicDatagramStreamId> flow_id; |
| if (http_datagram_support() == HttpDatagramSupport::kDraft00) { |
| flow_id = SpdyUtils::ParseDatagramFlowIdHeader(request_headers); |
| if (!flow_id.has_value()) { |
| QUIC_DLOG(ERROR) |
| << "MASQUE request with bad or missing DatagramFlowId header"; |
| return CreateBackendErrorResponse( |
| "400", "Bad or missing DatagramFlowId header"); |
| } |
| } |
| // Extract target host and port from path using default template. |
| std::vector<absl::string_view> path_split = absl::StrSplit(path, '/'); |
| if (path_split.size() != 4 || !path_split[0].empty() || |
| path_split[1].empty() || path_split[2].empty() || |
| !path_split[3].empty()) { |
| QUIC_DLOG(ERROR) << "MASQUE request with bad path \"" << path << "\""; |
| return CreateBackendErrorResponse("400", "Bad path"); |
| } |
| absl::optional<std::string> host = quiche::AsciiUrlDecode(path_split[1]); |
| if (!host.has_value()) { |
| QUIC_DLOG(ERROR) << "Failed to decode host \"" << path_split[1] << "\""; |
| return CreateBackendErrorResponse("500", "Failed to decode host"); |
| } |
| absl::optional<std::string> port = quiche::AsciiUrlDecode(path_split[2]); |
| if (!port.has_value()) { |
| QUIC_DLOG(ERROR) << "Failed to decode port \"" << path_split[2] << "\""; |
| return CreateBackendErrorResponse("500", "Failed to decode port"); |
| } |
| |
| // Perform DNS resolution. |
| addrinfo hint = {}; |
| hint.ai_protocol = IPPROTO_UDP; |
| |
| addrinfo* info_list = nullptr; |
| int result = getaddrinfo(host.value().c_str(), port.value().c_str(), &hint, |
| &info_list); |
| if (result != 0) { |
| QUIC_DLOG(ERROR) << "Failed to resolve " << authority << ": " |
| << gai_strerror(result); |
| return CreateBackendErrorResponse("500", "DNS resolution failed"); |
| } |
| |
| QUICHE_CHECK_NE(info_list, nullptr); |
| std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned( |
| info_list, freeaddrinfo); |
| QuicSocketAddress target_server_address(info_list->ai_addr, |
| info_list->ai_addrlen); |
| QUIC_DLOG(INFO) << "Got CONNECT_UDP request on stream ID " |
| << request_handler->stream_id() << " flow_id=" |
| << (flow_id.has_value() ? absl::StrCat(*flow_id) : "none") |
| << " target_server_address=\"" << target_server_address |
| << "\""; |
| |
| FdWrapper fd_wrapper(target_server_address.host().AddressFamilyToInt()); |
| if (fd_wrapper.fd() == kQuicInvalidSocketFd) { |
| QUIC_DLOG(ERROR) << "Socket creation failed"; |
| return CreateBackendErrorResponse("500", "Socket creation failed"); |
| } |
| QuicSocketAddress empty_address(QuicIpAddress::Any6(), 0); |
| if (target_server_address.host().IsIPv4()) { |
| empty_address = QuicSocketAddress(QuicIpAddress::Any4(), 0); |
| } |
| QuicUdpSocketApi socket_api; |
| if (!socket_api.Bind(fd_wrapper.fd(), empty_address)) { |
| QUIC_DLOG(ERROR) << "Socket bind failed"; |
| return CreateBackendErrorResponse("500", "Socket bind failed"); |
| } |
| epoll_server_->RegisterFDForRead(fd_wrapper.fd(), this); |
| |
| absl::optional<QuicDatagramContextId> context_id; |
| QuicSpdyStream* stream = static_cast<QuicSpdyStream*>( |
| GetActiveStream(request_handler->stream_id())); |
| if (stream == nullptr) { |
| QUIC_BUG(bad masque server stream type) |
| << "Unexpected stream type for stream ID " |
| << request_handler->stream_id(); |
| return CreateBackendErrorResponse("500", "Bad stream type"); |
| } |
| if (flow_id.has_value()) { |
| stream->RegisterHttp3DatagramFlowId(*flow_id); |
| } |
| connect_udp_server_states_.push_back( |
| ConnectUdpServerState(stream, context_id, target_server_address, |
| fd_wrapper.extract_fd(), this)); |
| |
| if (http_datagram_support() == HttpDatagramSupport::kDraft00) { |
| // TODO(b/181256914) remove this when we drop support for |
| // draft-ietf-masque-h3-datagram-00 in favor of later drafts. |
| stream->RegisterHttp3DatagramContextId( |
| context_id, DatagramFormatType::UDP_PAYLOAD, |
| /*format_additional_data=*/absl::string_view(), |
| &connect_udp_server_states_.back()); |
| } |
| |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = "200"; |
| if (flow_id.has_value()) { |
| SpdyUtils::AddDatagramFlowIdHeader(&response_headers, *flow_id); |
| } |
| auto response = std::make_unique<QuicBackendResponse>(); |
| response->set_response_type(QuicBackendResponse::INCOMPLETE_RESPONSE); |
| response->set_headers(std::move(response_headers)); |
| response->set_body(""); |
| |
| return response; |
| } |
| |
| QUIC_DLOG(INFO) << "MasqueServerSession handling MASQUE request"; |
| |
| if (masque_path == "init") { |
| if (masque_initialized_) { |
| QUIC_DLOG(ERROR) << "Got second MASQUE init request"; |
| return nullptr; |
| } |
| masque_initialized_ = true; |
| } else if (masque_path == "unregister") { |
| QuicConnectionId connection_id(request_body.data(), request_body.length()); |
| QUIC_DLOG(INFO) << "Received MASQUE request to unregister " |
| << connection_id; |
| owner_->UnregisterClientConnectionId(connection_id); |
| compression_engine_.UnregisterClientConnectionId(connection_id); |
| } else { |
| if (!masque_initialized_) { |
| QUIC_DLOG(ERROR) << "Got MASQUE request before init"; |
| return nullptr; |
| } |
| } |
| |
| // TODO(dschinazi) implement binary protocol sent in response body. |
| const std::string response_body = ""; |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = "200"; |
| auto response = std::make_unique<QuicBackendResponse>(); |
| response->set_response_type(QuicBackendResponse::REGULAR_RESPONSE); |
| response->set_headers(std::move(response_headers)); |
| response->set_body(response_body); |
| |
| return response; |
| } |
| |
| void MasqueServerSession::HandlePacketFromServer( |
| const ReceivedPacketInfo& packet_info) { |
| QUIC_DVLOG(1) << "MasqueServerSession received " << packet_info; |
| if (masque_mode_ == MasqueMode::kLegacy) { |
| compression_engine_.CompressAndSendPacket( |
| packet_info.packet.AsStringPiece(), |
| packet_info.destination_connection_id, packet_info.source_connection_id, |
| packet_info.peer_address); |
| return; |
| } |
| QUIC_LOG(ERROR) << "Ignoring packet from server in " << masque_mode_ |
| << " mode"; |
| } |
| |
| void MasqueServerSession::OnRegistration(QuicEpollServer* /*eps*/, |
| QuicUdpSocketFd fd, int event_mask) { |
| QUIC_DVLOG(1) << "OnRegistration " << fd << " event_mask " << event_mask; |
| } |
| |
| void MasqueServerSession::OnModification(QuicUdpSocketFd fd, int event_mask) { |
| QUIC_DVLOG(1) << "OnModification " << fd << " event_mask " << event_mask; |
| } |
| |
| void MasqueServerSession::OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) { |
| if ((event->in_events & EPOLLIN) == 0) { |
| QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask " |
| << event->in_events; |
| return; |
| } |
| auto it = absl::c_find_if(connect_udp_server_states_, |
| [fd](const ConnectUdpServerState& connect_udp) { |
| return connect_udp.fd() == fd; |
| }); |
| if (it == connect_udp_server_states_.end()) { |
| QUIC_BUG(quic_bug_10974_1) << "Got unexpected event mask " |
| << event->in_events << " on unknown fd " << fd; |
| return; |
| } |
| QuicSocketAddress expected_target_server_address = |
| it->target_server_address(); |
| QUICHE_DCHECK(expected_target_server_address.IsInitialized()); |
| QUIC_DVLOG(1) << "Received readable event on fd " << fd << " (mask " |
| << event->in_events << ") stream ID " << it->stream()->id() |
| << " server " << expected_target_server_address; |
| QuicUdpSocketApi socket_api; |
| BitMask64 packet_info_interested(QuicUdpPacketInfoBit::PEER_ADDRESS); |
| char packet_buffer[kMaxIncomingPacketSize]; |
| char control_buffer[kDefaultUdpPacketControlBufferSize]; |
| while (true) { |
| QuicUdpSocketApi::ReadPacketResult read_result; |
| read_result.packet_buffer = {packet_buffer, sizeof(packet_buffer)}; |
| read_result.control_buffer = {control_buffer, sizeof(control_buffer)}; |
| socket_api.ReadPacket(fd, packet_info_interested, &read_result); |
| if (!read_result.ok) { |
| // Most likely there is nothing left to read, break out of read loop. |
| break; |
| } |
| if (!read_result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) { |
| QUIC_BUG(quic_bug_10974_2) |
| << "Missing peer address when reading from fd " << fd; |
| continue; |
| } |
| if (read_result.packet_info.peer_address() != |
| expected_target_server_address) { |
| QUIC_DLOG(ERROR) << "Ignoring UDP packet on fd " << fd |
| << " from unexpected server address " |
| << read_result.packet_info.peer_address() |
| << " (expected " << expected_target_server_address |
| << ")"; |
| continue; |
| } |
| if (!connection()->connected()) { |
| QUIC_BUG(quic_bug_10974_3) |
| << "Unexpected incoming UDP packet on fd " << fd << " from " |
| << expected_target_server_address |
| << " because MASQUE connection is closed"; |
| return; |
| } |
| // The packet is valid, send it to the client in a DATAGRAM frame. |
| MessageStatus message_status = it->stream()->SendHttp3Datagram( |
| it->context_id(), |
| absl::string_view(read_result.packet_buffer.buffer, |
| read_result.packet_buffer.buffer_len)); |
| QUIC_DVLOG(1) << "Sent UDP packet from " << expected_target_server_address |
| << " of length " << read_result.packet_buffer.buffer_len |
| << " with stream ID " << it->stream()->id() |
| << " and got message status " |
| << MessageStatusToString(message_status); |
| } |
| } |
| |
| void MasqueServerSession::OnUnregistration(QuicUdpSocketFd fd, bool replaced) { |
| QUIC_DVLOG(1) << "OnUnregistration " << fd << " " << (replaced ? "" : "!") |
| << " replaced"; |
| } |
| |
| void MasqueServerSession::OnShutdown(QuicEpollServer* /*eps*/, |
| QuicUdpSocketFd fd) { |
| QUIC_DVLOG(1) << "OnShutdown " << fd; |
| } |
| |
| std::string MasqueServerSession::Name() const { |
| return std::string("MasqueServerSession-") + connection_id().ToString(); |
| } |
| |
| bool MasqueServerSession::OnSettingsFrame(const SettingsFrame& frame) { |
| QUIC_DLOG(INFO) << "Received SETTINGS: " << frame; |
| if (!QuicSimpleServerSession::OnSettingsFrame(frame)) { |
| return false; |
| } |
| if (!SupportsH3Datagram()) { |
| QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP Datagrams"; |
| return false; |
| } |
| QUIC_DLOG(INFO) << "Using HTTP Datagram: " << http_datagram_support(); |
| return true; |
| } |
| |
| MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( |
| QuicSpdyStream* stream, absl::optional<QuicDatagramContextId> context_id, |
| const QuicSocketAddress& target_server_address, QuicUdpSocketFd fd, |
| MasqueServerSession* masque_session) |
| : stream_(stream), |
| context_id_(context_id), |
| target_server_address_(target_server_address), |
| fd_(fd), |
| masque_session_(masque_session) { |
| QUICHE_DCHECK_NE(fd_, kQuicInvalidSocketFd); |
| QUICHE_DCHECK_NE(masque_session_, nullptr); |
| this->stream()->RegisterHttp3DatagramRegistrationVisitor(this); |
| } |
| |
| MasqueServerSession::ConnectUdpServerState::~ConnectUdpServerState() { |
| if (stream() != nullptr) { |
| if (context_registered_) { |
| stream()->UnregisterHttp3DatagramContextId(context_id()); |
| } |
| stream()->UnregisterHttp3DatagramRegistrationVisitor(); |
| } |
| if (fd_ == kQuicInvalidSocketFd) { |
| return; |
| } |
| QuicUdpSocketApi socket_api; |
| QUIC_DLOG(INFO) << "Closing fd " << fd_; |
| masque_session_->epoll_server()->UnregisterFD(fd_); |
| socket_api.Destroy(fd_); |
| } |
| |
| MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( |
| MasqueServerSession::ConnectUdpServerState&& other) { |
| fd_ = kQuicInvalidSocketFd; |
| *this = std::move(other); |
| } |
| |
| MasqueServerSession::ConnectUdpServerState& |
| MasqueServerSession::ConnectUdpServerState::operator=( |
| MasqueServerSession::ConnectUdpServerState&& other) { |
| if (fd_ != kQuicInvalidSocketFd) { |
| QuicUdpSocketApi socket_api; |
| QUIC_DLOG(INFO) << "Closing fd " << fd_; |
| masque_session_->epoll_server()->UnregisterFD(fd_); |
| socket_api.Destroy(fd_); |
| } |
| stream_ = other.stream_; |
| other.stream_ = nullptr; |
| context_id_ = other.context_id_; |
| target_server_address_ = other.target_server_address_; |
| fd_ = other.fd_; |
| masque_session_ = other.masque_session_; |
| other.fd_ = kQuicInvalidSocketFd; |
| context_registered_ = other.context_registered_; |
| other.context_registered_ = false; |
| if (stream() != nullptr) { |
| stream()->MoveHttp3DatagramRegistration(this); |
| if (context_registered_) { |
| stream()->MoveHttp3DatagramContextIdRegistration(context_id(), this); |
| } |
| } |
| return *this; |
| } |
| |
| void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram( |
| QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, |
| absl::string_view payload) { |
| QUICHE_DCHECK_EQ(stream_id, stream()->id()); |
| QUICHE_DCHECK(context_id == context_id_); |
| QuicUdpSocketApi socket_api; |
| QuicUdpPacketInfo packet_info; |
| packet_info.SetPeerAddress(target_server_address_); |
| WriteResult write_result = socket_api.WritePacket( |
| fd_, payload.data(), payload.length(), packet_info); |
| QUIC_DVLOG(1) << "Wrote packet of length " << payload.length() << " to " |
| << target_server_address_ << " with result " << write_result; |
| } |
| |
| void MasqueServerSession::ConnectUdpServerState::OnContextReceived( |
| QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, |
| DatagramFormatType format_type, absl::string_view format_additional_data) { |
| if (stream_id != stream()->id()) { |
| QUIC_BUG(MASQUE server bad datagram context registration) |
| << "Registered stream ID " << stream_id << ", expected " |
| << stream()->id(); |
| return; |
| } |
| if (format_type != DatagramFormatType::UDP_PAYLOAD) { |
| QUIC_DLOG(INFO) << "Ignoring unexpected datagram format type " |
| << DatagramFormatTypeToString(format_type); |
| return; |
| } |
| if (!format_additional_data.empty()) { |
| QUIC_DLOG(ERROR) |
| << "Received non-empty format additional data for context ID " |
| << (context_id_.has_value() ? context_id_.value() : 0) |
| << " on stream ID " << stream()->id(); |
| masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED); |
| return; |
| } |
| if (!context_received_) { |
| context_received_ = true; |
| context_id_ = context_id; |
| } |
| if (context_id != context_id_) { |
| QUIC_DLOG(INFO) |
| << "Ignoring unexpected context ID " |
| << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") |
| << " instead of " |
| << (context_id_.has_value() ? absl::StrCat(context_id_.value()) |
| : "none") |
| << " on stream ID " << stream()->id(); |
| return; |
| } |
| if (context_registered_) { |
| QUIC_BUG(MASQUE server double datagram context registration) |
| << "Try to re-register stream ID " << stream_id << " context ID " |
| << (context_id_.has_value() ? absl::StrCat(context_id_.value()) |
| : "none"); |
| return; |
| } |
| context_registered_ = true; |
| stream()->RegisterHttp3DatagramContextId(context_id_, format_type, |
| format_additional_data, this); |
| } |
| |
| void MasqueServerSession::ConnectUdpServerState::OnContextClosed( |
| QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, |
| ContextCloseCode close_code, absl::string_view close_details) { |
| if (stream_id != stream()->id()) { |
| QUIC_BUG(MASQUE server bad datagram context registration) |
| << "Closed context on stream ID " << stream_id << ", expected " |
| << stream()->id(); |
| return; |
| } |
| if (context_id != context_id_) { |
| QUIC_DLOG(INFO) |
| << "Ignoring unexpected close of context ID " |
| << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") |
| << " instead of " |
| << (context_id_.has_value() ? absl::StrCat(context_id_.value()) |
| : "none") |
| << " on stream ID " << stream()->id(); |
| return; |
| } |
| QUIC_DLOG(INFO) << "Received datagram context close with close code " |
| << close_code << " close details \"" << close_details |
| << "\" on stream ID " << stream()->id() << ", closing stream"; |
| masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED); |
| } |
| |
| } // namespace quic |