Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 11 | #include "pc/data_channel_controller.h" |
| 12 | |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 13 | #include <cstdint> |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 14 | #include <utility> |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 15 | |
Tommi | 51edb56 | 2023-03-14 08:23:51 | [diff] [blame] | 16 | #include "absl/algorithm/container.h" |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 17 | #include "absl/types/optional.h" |
Harald Alvestrand | 5761e7b | 2021-01-29 14:45:08 | [diff] [blame] | 18 | #include "api/peer_connection_interface.h" |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 19 | #include "api/priority.h" |
Harald Alvestrand | 5761e7b | 2021-01-29 14:45:08 | [diff] [blame] | 20 | #include "api/rtc_error.h" |
Harald Alvestrand | 5b84f38 | 2022-02-08 10:49:09 | [diff] [blame] | 21 | #include "pc/peer_connection_internal.h" |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 22 | #include "pc/sctp_utils.h" |
Harald Alvestrand | 5761e7b | 2021-01-29 14:45:08 | [diff] [blame] | 23 | #include "rtc_base/logging.h" |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 24 | |
| 25 | namespace webrtc { |
| 26 | |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 27 | DataChannelController::~DataChannelController() { |
Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 28 | RTC_DCHECK(sctp_data_channels_n_.empty()) |
| 29 | << "Missing call to TeardownDataChannelTransport_n?"; |
| 30 | RTC_DCHECK(!signaling_safety_.flag()->alive()) |
| 31 | << "Missing call to PrepareForShutdown?"; |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 32 | } |
Harald Alvestrand | 9e5aeb9 | 2022-05-11 09:35:36 | [diff] [blame] | 33 | |
Philipp Hancke | 522380f | 2023-05-09 07:41:03 | [diff] [blame] | 34 | bool DataChannelController::HasDataChannels() const { |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 35 | RTC_DCHECK_RUN_ON(signaling_thread()); |
| 36 | return channel_usage_ == DataChannelUsage::kInUse; |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 37 | } |
| 38 | |
Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 39 | bool DataChannelController::HasUsedDataChannels() const { |
| 40 | RTC_DCHECK_RUN_ON(signaling_thread()); |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 41 | return channel_usage_ != DataChannelUsage::kNeverUsed; |
Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 42 | } |
| 43 | |
Tommi | 1fabbac | 2023-03-21 13:48:51 | [diff] [blame] | 44 | RTCError DataChannelController::SendData( |
| 45 | StreamId sid, |
| 46 | const SendDataParams& params, |
| 47 | const rtc::CopyOnWriteBuffer& payload) { |
Tommi | add7ac0 | 2023-04-12 10:01:10 | [diff] [blame] | 48 | RTC_DCHECK_RUN_ON(network_thread()); |
| 49 | if (!data_channel_transport_) { |
| 50 | RTC_LOG(LS_ERROR) << "SendData called before transport is ready"; |
| 51 | return RTCError(RTCErrorType::INVALID_STATE); |
| 52 | } |
| 53 | return data_channel_transport_->SendData(sid.stream_id_int(), params, |
| 54 | payload); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 55 | } |
| 56 | |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 57 | void DataChannelController::AddSctpDataStream(StreamId sid, |
| 58 | PriorityValue priority) { |
Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 59 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | add7ac0 | 2023-04-12 10:01:10 | [diff] [blame] | 60 | if (data_channel_transport_) { |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 61 | data_channel_transport_->OpenChannel(sid.stream_id_int(), priority); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 62 | } |
| 63 | } |
| 64 | |
Tommi | 4c84222 | 2023-03-21 10:35:24 | [diff] [blame] | 65 | void DataChannelController::RemoveSctpDataStream(StreamId sid) { |
Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 66 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | add7ac0 | 2023-04-12 10:01:10 | [diff] [blame] | 67 | if (data_channel_transport_) { |
| 68 | data_channel_transport_->CloseChannel(sid.stream_id_int()); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 69 | } |
| 70 | } |
| 71 | |
Tommi | d2afbaf | 2023-03-02 09:51:16 | [diff] [blame] | 72 | void DataChannelController::OnChannelStateChanged( |
| 73 | SctpDataChannel* channel, |
| 74 | DataChannelInterface::DataState state) { |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 75 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | eec1810 | 2023-06-22 08:13:52 | [diff] [blame] | 76 | |
| 77 | // Stash away the internal id here in case `OnSctpDataChannelClosed` ends up |
| 78 | // releasing the last reference to the channel. |
| 79 | const int channel_id = channel->internal_id(); |
| 80 | |
Tommi | d2afbaf | 2023-03-02 09:51:16 | [diff] [blame] | 81 | if (state == DataChannelInterface::DataState::kClosed) |
| 82 | OnSctpDataChannelClosed(channel); |
| 83 | |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 84 | DataChannelUsage channel_usage = sctp_data_channels_n_.empty() |
| 85 | ? DataChannelUsage::kHaveBeenUsed |
| 86 | : DataChannelUsage::kInUse; |
| 87 | signaling_thread()->PostTask(SafeTask( |
Tommi | eec1810 | 2023-06-22 08:13:52 | [diff] [blame] | 88 | signaling_safety_.flag(), [this, channel_id, state, channel_usage] { |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 89 | RTC_DCHECK_RUN_ON(signaling_thread()); |
| 90 | channel_usage_ = channel_usage; |
| 91 | pc_->OnSctpDataChannelStateChanged(channel_id, state); |
| 92 | })); |
Tommi | d2afbaf | 2023-03-02 09:51:16 | [diff] [blame] | 93 | } |
| 94 | |
Victor Boivie | fea41f5 | 2024-03-11 15:43:31 | [diff] [blame] | 95 | size_t DataChannelController::buffered_amount(StreamId sid) const { |
| 96 | RTC_DCHECK_RUN_ON(network_thread()); |
| 97 | if (!data_channel_transport_) { |
| 98 | return 0; |
| 99 | } |
| 100 | return data_channel_transport_->buffered_amount(sid.stream_id_int()); |
| 101 | } |
| 102 | |
Victor Boivie | cdecc4e | 2024-03-18 12:47:34 | [diff] [blame] | 103 | size_t DataChannelController::buffered_amount_low_threshold( |
| 104 | StreamId sid) const { |
| 105 | RTC_DCHECK_RUN_ON(network_thread()); |
| 106 | if (!data_channel_transport_) { |
| 107 | return 0; |
| 108 | } |
| 109 | return data_channel_transport_->buffered_amount_low_threshold( |
| 110 | sid.stream_id_int()); |
| 111 | } |
| 112 | |
| 113 | void DataChannelController::SetBufferedAmountLowThreshold(StreamId sid, |
| 114 | size_t bytes) { |
| 115 | RTC_DCHECK_RUN_ON(network_thread()); |
| 116 | if (!data_channel_transport_) { |
| 117 | return; |
| 118 | } |
| 119 | data_channel_transport_->SetBufferedAmountLowThreshold(sid.stream_id_int(), |
| 120 | bytes); |
| 121 | } |
| 122 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 123 | void DataChannelController::OnDataReceived( |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 124 | int channel_id, |
| 125 | DataMessageType type, |
| 126 | const rtc::CopyOnWriteBuffer& buffer) { |
| 127 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 128 | |
Tommi | 4e1c957 | 2023-03-15 11:36:20 | [diff] [blame] | 129 | if (HandleOpenMessage_n(channel_id, type, buffer)) |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 130 | return; |
| 131 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 132 | auto it = absl::c_find_if(sctp_data_channels_n_, [&](const auto& c) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 133 | return c->sid_n().has_value() && c->sid_n()->stream_id_int() == channel_id; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 134 | }); |
| 135 | |
| 136 | if (it != sctp_data_channels_n_.end()) |
| 137 | (*it)->OnDataReceived(type, buffer); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 138 | } |
| 139 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 140 | void DataChannelController::OnChannelClosing(int channel_id) { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 141 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 142 | auto it = absl::c_find_if(sctp_data_channels_n_, [&](const auto& c) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 143 | return c->sid_n().has_value() && c->sid_n()->stream_id_int() == channel_id; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 144 | }); |
| 145 | |
| 146 | if (it != sctp_data_channels_n_.end()) |
| 147 | (*it)->OnClosingProcedureStartedRemotely(); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 148 | } |
| 149 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 150 | void DataChannelController::OnChannelClosed(int channel_id) { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 151 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 152 | StreamId sid(channel_id); |
| 153 | sid_allocator_.ReleaseSid(sid); |
| 154 | auto it = absl::c_find_if(sctp_data_channels_n_, |
Tommi | 1158bde | 2023-03-30 10:01:56 | [diff] [blame] | 155 | [&](const auto& c) { return c->sid_n() == sid; }); |
Tommi | 51edb56 | 2023-03-14 08:23:51 | [diff] [blame] | 156 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 157 | if (it != sctp_data_channels_n_.end()) { |
| 158 | rtc::scoped_refptr<SctpDataChannel> channel = std::move(*it); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 159 | sctp_data_channels_n_.erase(it); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 160 | channel->OnClosingProcedureComplete(); |
| 161 | } |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 162 | } |
| 163 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 164 | void DataChannelController::OnReadyToSend() { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 165 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 166 | auto copy = sctp_data_channels_n_; |
| 167 | for (const auto& channel : copy) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 168 | if (channel->sid_n().has_value()) { |
Tommi | e9aa867 | 2023-03-20 13:43:09 | [diff] [blame] | 169 | channel->OnTransportReady(); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 170 | } else { |
| 171 | // This happens for role==SSL_SERVER channels when we get notified by |
| 172 | // the transport *before* the SDP code calls `AllocateSctpSids` to |
| 173 | // trigger assignment of sids. In this case OnTransportReady() will be |
| 174 | // called from within `AllocateSctpSids` below. |
| 175 | RTC_LOG(LS_INFO) << "OnReadyToSend: Still waiting for an id for channel."; |
| 176 | } |
| 177 | } |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 178 | } |
| 179 | |
Florent Castelli | dcb9ffc | 2021-06-29 12:58:23 | [diff] [blame] | 180 | void DataChannelController::OnTransportClosed(RTCError error) { |
Harald Alvestrand | 2697ac1 | 2019-12-16 09:37:04 | [diff] [blame] | 181 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 182 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 183 | // This loop will close all data channels and trigger a callback to |
Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 184 | // `OnSctpDataChannelClosed`. We'll empty `sctp_data_channels_n_`, first |
| 185 | // and `OnSctpDataChannelClosed` will become a noop but we'll release the |
| 186 | // StreamId here. |
| 187 | std::vector<rtc::scoped_refptr<SctpDataChannel>> temp_sctp_dcs; |
| 188 | temp_sctp_dcs.swap(sctp_data_channels_n_); |
| 189 | for (const auto& channel : temp_sctp_dcs) { |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 190 | channel->OnTransportChannelClosed(error); |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 191 | if (channel->sid_n().has_value()) { |
| 192 | sid_allocator_.ReleaseSid(*channel->sid_n()); |
| 193 | } |
Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 194 | } |
Harald Alvestrand | 2697ac1 | 2019-12-16 09:37:04 | [diff] [blame] | 195 | } |
| 196 | |
Victor Boivie | cdecc4e | 2024-03-18 12:47:34 | [diff] [blame] | 197 | void DataChannelController::OnBufferedAmountLow(int channel_id) { |
| 198 | RTC_DCHECK_RUN_ON(network_thread()); |
| 199 | auto it = absl::c_find_if(sctp_data_channels_n_, [&](const auto& c) { |
| 200 | return c->sid_n().has_value() && c->sid_n()->stream_id_int() == channel_id; |
| 201 | }); |
| 202 | |
| 203 | if (it != sctp_data_channels_n_.end()) |
| 204 | (*it)->OnBufferedAmountLow(); |
| 205 | } |
| 206 | |
Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 207 | void DataChannelController::SetupDataChannelTransport_n( |
| 208 | DataChannelTransportInterface* transport) { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 209 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 210 | RTC_DCHECK(transport); |
| 211 | set_data_channel_transport(transport); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 212 | } |
| 213 | |
Tommi | 1f708ef | 2023-03-31 16:40:50 | [diff] [blame] | 214 | void DataChannelController::PrepareForShutdown() { |
| 215 | RTC_DCHECK_RUN_ON(signaling_thread()); |
Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 216 | signaling_safety_.reset(PendingTaskSafetyFlag::CreateDetachedInactive()); |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 217 | if (channel_usage_ != DataChannelUsage::kNeverUsed) |
| 218 | channel_usage_ = DataChannelUsage::kHaveBeenUsed; |
Tommi | 1f708ef | 2023-03-31 16:40:50 | [diff] [blame] | 219 | } |
| 220 | |
Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 221 | void DataChannelController::TeardownDataChannelTransport_n(RTCError error) { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 222 | RTC_DCHECK_RUN_ON(network_thread()); |
Mirko Bonadei | 2983135 | 2023-04-14 17:03:47 | [diff] [blame] | 223 | OnTransportClosed(error); |
Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 224 | set_data_channel_transport(nullptr); |
Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 225 | RTC_DCHECK(sctp_data_channels_n_.empty()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 226 | weak_factory_.InvalidateWeakPtrs(); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 227 | } |
| 228 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 229 | void DataChannelController::OnTransportChanged( |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 230 | DataChannelTransportInterface* new_data_channel_transport) { |
| 231 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | add7ac0 | 2023-04-12 10:01:10 | [diff] [blame] | 232 | if (data_channel_transport_ && |
| 233 | data_channel_transport_ != new_data_channel_transport) { |
Artem Titov | 880fa81 | 2021-07-30 20:30:23 | [diff] [blame] | 234 | // Changed which data channel transport is used for `sctp_mid_` (eg. now |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 235 | // it's bundled). |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 236 | set_data_channel_transport(new_data_channel_transport); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 237 | } |
| 238 | } |
| 239 | |
Taylor Brandstetter | 3a034e1 | 2020-07-09 22:32:34 | [diff] [blame] | 240 | std::vector<DataChannelStats> DataChannelController::GetDataChannelStats() |
Tomas Gunnarsson | 2e94de5 | 2020-06-16 14:54:10 | [diff] [blame] | 241 | const { |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 242 | RTC_DCHECK_RUN_ON(network_thread()); |
Taylor Brandstetter | 3a034e1 | 2020-07-09 22:32:34 | [diff] [blame] | 243 | std::vector<DataChannelStats> stats; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 244 | stats.reserve(sctp_data_channels_n_.size()); |
| 245 | for (const auto& channel : sctp_data_channels_n_) |
Tomas Gunnarsson | 2e94de5 | 2020-06-16 14:54:10 | [diff] [blame] | 246 | stats.push_back(channel->GetStats()); |
| 247 | return stats; |
| 248 | } |
| 249 | |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 250 | bool DataChannelController::HandleOpenMessage_n( |
Tommi | 4e1c957 | 2023-03-15 11:36:20 | [diff] [blame] | 251 | int channel_id, |
| 252 | DataMessageType type, |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 253 | const rtc::CopyOnWriteBuffer& buffer) { |
Tommi | 4e1c957 | 2023-03-15 11:36:20 | [diff] [blame] | 254 | if (type != DataMessageType::kControl || !IsOpenMessage(buffer)) |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 255 | return false; |
| 256 | |
| 257 | // Received OPEN message; parse and signal that a new data channel should |
| 258 | // be created. |
| 259 | std::string label; |
| 260 | InternalDataChannelInit config; |
Tommi | 4e1c957 | 2023-03-15 11:36:20 | [diff] [blame] | 261 | config.id = channel_id; |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 262 | if (!ParseDataChannelOpenMessage(buffer, &label, &config)) { |
| 263 | RTC_LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " |
Tommi | 4e1c957 | 2023-03-15 11:36:20 | [diff] [blame] | 264 | << channel_id; |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 265 | } else { |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 266 | config.open_handshake_role = InternalDataChannelInit::kAcker; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 267 | auto channel_or_error = CreateDataChannel(label, config); |
| 268 | if (channel_or_error.ok()) { |
| 269 | signaling_thread()->PostTask(SafeTask( |
| 270 | signaling_safety_.flag(), |
| 271 | [this, channel = channel_or_error.MoveValue(), |
| 272 | ready_to_send = data_channel_transport_->IsReadyToSend()] { |
| 273 | RTC_DCHECK_RUN_ON(signaling_thread()); |
| 274 | OnDataChannelOpenMessage(std::move(channel), ready_to_send); |
| 275 | })); |
| 276 | } else { |
| 277 | RTC_LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message." |
| 278 | << ToString(channel_or_error.error().type()); |
| 279 | } |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 280 | } |
Tommi | 5bbfb00 | 2023-03-04 15:47:53 | [diff] [blame] | 281 | return true; |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 282 | } |
| 283 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 284 | void DataChannelController::OnDataChannelOpenMessage( |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 285 | rtc::scoped_refptr<SctpDataChannel> channel, |
| 286 | bool ready_to_send) { |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 287 | channel_usage_ = DataChannelUsage::kInUse; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 288 | auto proxy = SctpDataChannel::CreateProxy(channel, signaling_safety_.flag()); |
Andrey Logvin | 7f16fcd | 2023-04-05 08:53:13 | [diff] [blame] | 289 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 290 | pc_->Observer()->OnDataChannel(proxy); |
Andrey Logvin | 7f16fcd | 2023-04-05 08:53:13 | [diff] [blame] | 291 | pc_->NoteDataAddedEvent(); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 292 | |
| 293 | if (ready_to_send) { |
| 294 | network_thread()->PostTask([channel = std::move(channel)] { |
| 295 | if (channel->state() != DataChannelInterface::DataState::kClosed) |
| 296 | channel->OnTransportReady(); |
| 297 | }); |
| 298 | } |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 299 | } |
| 300 | |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 301 | // RTC_RUN_ON(network_thread()) |
| 302 | RTCError DataChannelController::ReserveOrAllocateSid( |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 303 | absl::optional<StreamId>& sid, |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 304 | absl::optional<rtc::SSLRole> fallback_ssl_role) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 305 | if (sid.has_value()) { |
| 306 | return sid_allocator_.ReserveSid(*sid) |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 307 | ? RTCError::OK() |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 308 | : RTCError(RTCErrorType::INVALID_RANGE, "StreamId reserved."); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 309 | } |
| 310 | |
| 311 | // Attempt to allocate an ID based on the negotiated role. |
| 312 | absl::optional<rtc::SSLRole> role = pc_->GetSctpSslRole_n(); |
| 313 | if (!role) |
| 314 | role = fallback_ssl_role; |
| 315 | if (role) { |
| 316 | sid = sid_allocator_.AllocateSid(*role); |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 317 | if (!sid.has_value()) |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 318 | return RTCError(RTCErrorType::RESOURCE_EXHAUSTED); |
| 319 | } |
| 320 | // When we get here, we may still not have an ID, but that's a supported case |
| 321 | // whereby an id will be assigned later. |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 322 | RTC_DCHECK(sid.has_value() || !role); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 323 | return RTCError::OK(); |
| 324 | } |
| 325 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 326 | // RTC_RUN_ON(network_thread()) |
| 327 | RTCErrorOr<rtc::scoped_refptr<SctpDataChannel>> |
| 328 | DataChannelController::CreateDataChannel(const std::string& label, |
| 329 | InternalDataChannelInit& config) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 330 | absl::optional<StreamId> sid = absl::nullopt; |
| 331 | if (config.id != -1) { |
| 332 | if (config.id < 0 || config.id > cricket::kMaxSctpSid) { |
| 333 | return RTCError(RTCErrorType::INVALID_RANGE, "StreamId out of range."); |
| 334 | } |
| 335 | sid = StreamId(config.id); |
| 336 | } |
| 337 | |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 338 | RTCError err = ReserveOrAllocateSid(sid, config.fallback_ssl_role); |
| 339 | if (!err.ok()) |
| 340 | return err; |
| 341 | |
| 342 | // In case `sid` has changed. Update `config` accordingly. |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 343 | if (sid.has_value()) { |
| 344 | config.id = sid->stream_id_int(); |
| 345 | } |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 346 | |
| 347 | rtc::scoped_refptr<SctpDataChannel> channel = SctpDataChannel::Create( |
| 348 | weak_factory_.GetWeakPtr(), label, data_channel_transport_ != nullptr, |
| 349 | config, signaling_thread(), network_thread()); |
| 350 | RTC_DCHECK(channel); |
| 351 | sctp_data_channels_n_.push_back(channel); |
| 352 | |
| 353 | // If we have an id already, notify the transport. |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 354 | if (sid.has_value()) |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 355 | AddSctpDataStream(*sid, |
| 356 | config.priority.value_or(PriorityValue(Priority::kLow))); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 357 | |
| 358 | return channel; |
| 359 | } |
| 360 | |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 361 | RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>> |
Taylor Brandstetter | 3a034e1 | 2020-07-09 22:32:34 | [diff] [blame] | 362 | DataChannelController::InternalCreateDataChannelWithProxy( |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 363 | const std::string& label, |
Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 364 | const InternalDataChannelInit& config) { |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 365 | RTC_DCHECK_RUN_ON(signaling_thread()); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 366 | RTC_DCHECK(!pc_->IsClosed()); |
Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 367 | if (!config.IsValid()) { |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 368 | LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, |
| 369 | "Invalid DataChannelInit"); |
Tommi | 9296a16 | 2023-03-21 15:28:52 | [diff] [blame] | 370 | } |
| 371 | |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 372 | bool ready_to_send = false; |
Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 373 | InternalDataChannelInit new_config = config; |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 374 | auto ret = network_thread()->BlockingCall( |
| 375 | [&]() -> RTCErrorOr<rtc::scoped_refptr<SctpDataChannel>> { |
| 376 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 377 | auto channel = CreateDataChannel(label, new_config); |
| 378 | if (!channel.ok()) |
| 379 | return channel; |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 380 | ready_to_send = |
| 381 | data_channel_transport_ && data_channel_transport_->IsReadyToSend(); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 382 | if (ready_to_send) { |
| 383 | // If the transport is ready to send because the initial channel |
| 384 | // ready signal may have been sent before the DataChannel creation. |
| 385 | // This has to be done async because the upper layer objects (e.g. |
| 386 | // Chrome glue and WebKit) are not wired up properly until after |
| 387 | // `InternalCreateDataChannelWithProxy` returns. |
| 388 | network_thread()->PostTask([channel = channel.value()] { |
| 389 | if (channel->state() != DataChannelInterface::DataState::kClosed) |
| 390 | channel->OnTransportReady(); |
| 391 | }); |
| 392 | } |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 393 | |
| 394 | return channel; |
| 395 | }); |
| 396 | |
| 397 | if (!ret.ok()) |
| 398 | return ret.MoveError(); |
| 399 | |
Tommi | 44ebe2a | 2023-05-15 13:14:10 | [diff] [blame] | 400 | channel_usage_ = DataChannelUsage::kInUse; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 401 | return SctpDataChannel::CreateProxy(ret.MoveValue(), |
| 402 | signaling_safety_.flag()); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 403 | } |
| 404 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 405 | void DataChannelController::AllocateSctpSids(rtc::SSLRole role) { |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 406 | RTC_DCHECK_RUN_ON(network_thread()); |
| 407 | |
| 408 | const bool ready_to_send = |
| 409 | data_channel_transport_ && data_channel_transport_->IsReadyToSend(); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 410 | |
| 411 | std::vector<std::pair<SctpDataChannel*, StreamId>> channels_to_update; |
Taylor Brandstetter | 3a034e1 | 2020-07-09 22:32:34 | [diff] [blame] | 412 | std::vector<rtc::scoped_refptr<SctpDataChannel>> channels_to_close; |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 413 | for (auto it = sctp_data_channels_n_.begin(); |
| 414 | it != sctp_data_channels_n_.end();) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 415 | if (!(*it)->sid_n().has_value()) { |
| 416 | absl::optional<StreamId> sid = sid_allocator_.AllocateSid(role); |
| 417 | if (sid.has_value()) { |
| 418 | (*it)->SetSctpSid_n(*sid); |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 419 | AddSctpDataStream(*sid, (*it)->priority()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 420 | if (ready_to_send) { |
| 421 | RTC_LOG(LS_INFO) << "AllocateSctpSids: Id assigned, ready to send."; |
| 422 | (*it)->OnTransportReady(); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 423 | } |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 424 | channels_to_update.push_back(std::make_pair((*it).get(), *sid)); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 425 | } else { |
| 426 | channels_to_close.push_back(std::move(*it)); |
| 427 | it = sctp_data_channels_n_.erase(it); |
| 428 | continue; |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 429 | } |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 430 | } |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 431 | ++it; |
| 432 | } |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 433 | |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 434 | // Since closing modifies the list of channels, we have to do the actual |
| 435 | // closing outside the loop. |
| 436 | for (const auto& channel : channels_to_close) { |
Harald Alvestrand | dfbfb46 | 2019-12-08 04:55:43 | [diff] [blame] | 437 | channel->CloseAbruptlyWithDataChannelFailure("Failed to allocate SCTP SID"); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 438 | } |
| 439 | } |
| 440 | |
Taylor Brandstetter | 3a034e1 | 2020-07-09 22:32:34 | [diff] [blame] | 441 | void DataChannelController::OnSctpDataChannelClosed(SctpDataChannel* channel) { |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 442 | RTC_DCHECK_RUN_ON(network_thread()); |
| 443 | // After the closing procedure is done, it's safe to use this ID for |
| 444 | // another data channel. |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 445 | if (channel->sid_n().has_value()) { |
| 446 | sid_allocator_.ReleaseSid(*channel->sid_n()); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 447 | } |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 448 | auto it = absl::c_find_if(sctp_data_channels_n_, |
| 449 | [&](const auto& c) { return c.get() == channel; }); |
| 450 | if (it != sctp_data_channels_n_.end()) |
| 451 | sctp_data_channels_n_.erase(it); |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 452 | } |
| 453 | |
Tomas Gunnarsson | 7d3cfbf | 2020-06-15 11:47:42 | [diff] [blame] | 454 | void DataChannelController::set_data_channel_transport( |
| 455 | DataChannelTransportInterface* transport) { |
| 456 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 457 | |
| 458 | if (data_channel_transport_) |
| 459 | data_channel_transport_->SetDataSink(nullptr); |
| 460 | |
Tomas Gunnarsson | 7d3cfbf | 2020-06-15 11:47:42 | [diff] [blame] | 461 | data_channel_transport_ = transport; |
Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 462 | |
| 463 | if (data_channel_transport_) { |
| 464 | // There's a new data channel transport. This needs to be signaled to the |
| 465 | // `sctp_data_channels_n_` so that they can reopen and reconnect. This is |
| 466 | // necessary when bundling is applied. |
| 467 | NotifyDataChannelsOfTransportCreated(); |
| 468 | data_channel_transport_->SetDataSink(this); |
| 469 | } |
Tomas Gunnarsson | 7d3cfbf | 2020-06-15 11:47:42 | [diff] [blame] | 470 | } |
| 471 | |
Tomas Gunnarsson | 2e94de5 | 2020-06-16 14:54:10 | [diff] [blame] | 472 | void DataChannelController::NotifyDataChannelsOfTransportCreated() { |
| 473 | RTC_DCHECK_RUN_ON(network_thread()); |
Tommi | add7ac0 | 2023-04-12 10:01:10 | [diff] [blame] | 474 | RTC_DCHECK(data_channel_transport_); |
Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 475 | |
Tommi | 1158bde | 2023-03-30 10:01:56 | [diff] [blame] | 476 | for (const auto& channel : sctp_data_channels_n_) { |
Victor Boivie | cd3d29b | 2024-03-09 20:50:42 | [diff] [blame] | 477 | if (channel->sid_n().has_value()) |
Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 478 | AddSctpDataStream(*channel->sid_n(), channel->priority()); |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 479 | channel->OnTransportChannelCreated(); |
Tommi | 1158bde | 2023-03-30 10:01:56 | [diff] [blame] | 480 | } |
Tommi | dc90a9c | 2023-03-20 09:26:19 | [diff] [blame] | 481 | } |
| 482 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 483 | rtc::Thread* DataChannelController::network_thread() const { |
| 484 | return pc_->network_thread(); |
| 485 | } |
Tommi | f9e13f8 | 2023-04-06 19:21:45 | [diff] [blame] | 486 | |
Harald Alvestrand | 05e4d08 | 2019-12-03 13:04:21 | [diff] [blame] | 487 | rtc::Thread* DataChannelController::signaling_thread() const { |
| 488 | return pc_->signaling_thread(); |
| 489 | } |
| 490 | |
Harald Alvestrand | 00cf34c | 2019-12-02 08:56:02 | [diff] [blame] | 491 | } // namespace webrtc |