The proper closing procedure is: 1. Alice resets outgoing stream. 2. Bob receives incoming stream reset, resets his outgoing stream. 3. Alice receives incoming stream reset; channel closed! 4. Bob receives acknowledgement of reset; channel closed! https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7 However, up until now we've been sending both an incoming and outgoing reset from the side initiating the closing procedure, and doing nothing on the remote side. This means that if you call "Close" and the remote endpoint is using an old version of WebRTC, the channel's state will be stuck at "closing" since the remote endpoint won't send a reset. Which is already what happens when Firefox is talking to Chrome. This CL also fixes an issue where the DataChannel's state prematurely went to "closed" before the closing procedure was complete. Which could result in a new DataChannel attempting to re-use the ID and failing. TBR=magjed@webrtc.org Bug: chromium:449934, webrtc:4453 Change-Id: Ic1ba813e46538c6c65868961aae6a9780d68a5e2 Reviewed-on: https://webrtc-review.googlesource.com/79061 Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23478}
142 lines
6.1 KiB
C++
142 lines
6.1 KiB
C++
/*
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#ifndef MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
|
|
#define MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
|
|
|
|
// TODO(deadbeef): Move SCTP code out of media/, and make it not depend on
|
|
// anything in media/.
|
|
|
|
#include <memory> // for unique_ptr
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "rtc_base/copyonwritebuffer.h"
|
|
#include "rtc_base/thread.h"
|
|
// For SendDataParams/ReceiveDataParams.
|
|
// TODO(deadbeef): Use something else for SCTP. It's confusing that we use an
|
|
// SSRC field for SID.
|
|
#include "media/base/mediachannel.h"
|
|
#include "p2p/base/packettransportinternal.h"
|
|
|
|
namespace cricket {
|
|
|
|
// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs)
|
|
// are 0-based, the highest usable SID is 1023.
|
|
//
|
|
// It's recommended to use the maximum of 65535 in:
|
|
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2
|
|
// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes
|
|
// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535
|
|
// streams would waste ~6MB.
|
|
//
|
|
// Note: "max" and "min" here are inclusive.
|
|
constexpr uint16_t kMaxSctpStreams = 1024;
|
|
constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1;
|
|
constexpr uint16_t kMinSctpSid = 0;
|
|
|
|
// This is the default SCTP port to use. It is passed along the wire and the
|
|
// connectee and connector must be using the same port. It is not related to the
|
|
// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in
|
|
// usrsctp.h)
|
|
const int kSctpDefaultPort = 5000;
|
|
|
|
// Abstract SctpTransport interface for use internally (by PeerConnection etc.).
|
|
// Exists to allow mock/fake SctpTransports to be created.
|
|
class SctpTransportInternal {
|
|
public:
|
|
virtual ~SctpTransportInternal() {}
|
|
|
|
// Changes what underlying DTLS transport is uses. Used when switching which
|
|
// bundled transport the SctpTransport uses.
|
|
virtual void SetDtlsTransport(rtc::PacketTransportInternal* transport) = 0;
|
|
|
|
// When Start is called, connects as soon as possible; this can be called
|
|
// before DTLS completes, in which case the connection will begin when DTLS
|
|
// completes. This method can be called multiple times, though not if either
|
|
// of the ports are changed.
|
|
//
|
|
// |local_sctp_port| and |remote_sctp_port| are passed along the wire and the
|
|
// listener and connector must be using the same port. They are not related
|
|
// to the ports at the IP level. If set to -1, we default to
|
|
// kSctpDefaultPort.
|
|
//
|
|
// TODO(deadbeef): Add remote max message size as parameter to Start, once we
|
|
// start supporting it.
|
|
// TODO(deadbeef): Support calling Start with different local/remote ports
|
|
// and create a new association? Not clear if this is something we need to
|
|
// support though. See: https://github.com/w3c/webrtc-pc/issues/979
|
|
virtual bool Start(int local_sctp_port, int remote_sctp_port) = 0;
|
|
|
|
// NOTE: Initially there was a "Stop" method here, but it was never used, so
|
|
// it was removed.
|
|
|
|
// Informs SctpTransport that |sid| will start being used. Returns false if
|
|
// it is impossible to use |sid|, or if it's already in use.
|
|
// Until calling this, can't send data using |sid|.
|
|
// TODO(deadbeef): Actually implement the "returns false if |sid| can't be
|
|
// used" part. See:
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=619849
|
|
virtual bool OpenStream(int sid) = 0;
|
|
// The inverse of OpenStream. Begins the closing procedure, which will
|
|
// eventually result in SignalClosingProcedureComplete on the side that
|
|
// initiates it, and both SignalClosingProcedureStartedRemotely and
|
|
// SignalClosingProcedureComplete on the other side.
|
|
virtual bool ResetStream(int sid) = 0;
|
|
// Send data down this channel (will be wrapped as SCTP packets then given to
|
|
// usrsctp that will then post the network interface).
|
|
// Returns true iff successful data somewhere on the send-queue/network.
|
|
// Uses |params.ssrc| as the SCTP sid.
|
|
virtual bool SendData(const SendDataParams& params,
|
|
const rtc::CopyOnWriteBuffer& payload,
|
|
SendDataResult* result = nullptr) = 0;
|
|
|
|
// Indicates when the SCTP socket is created and not blocked by congestion
|
|
// control. This changes to false when SDR_BLOCK is returned from SendData,
|
|
// and
|
|
// changes to true when SignalReadyToSendData is fired. The underlying DTLS/
|
|
// ICE channels may be unwritable while ReadyToSendData is true, because data
|
|
// can still be queued in usrsctp.
|
|
virtual bool ReadyToSendData() = 0;
|
|
|
|
sigslot::signal0<> SignalReadyToSendData;
|
|
// ReceiveDataParams includes SID, seq num, timestamp, etc. CopyOnWriteBuffer
|
|
// contains message payload.
|
|
sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
|
|
SignalDataReceived;
|
|
// Parameter is SID; fired when we receive an incoming stream reset on an
|
|
// open stream, indicating that the other side started the closing procedure.
|
|
// After resetting the outgoing stream, SignalClosingProcedureComplete will
|
|
// fire too.
|
|
sigslot::signal1<int> SignalClosingProcedureStartedRemotely;
|
|
// Parameter is SID; fired when closing procedure is complete (both incoming
|
|
// and outgoing streams reset).
|
|
sigslot::signal1<int> SignalClosingProcedureComplete;
|
|
|
|
// Helper for debugging.
|
|
virtual void set_debug_name_for_testing(const char* debug_name) = 0;
|
|
};
|
|
|
|
// Factory class which can be used to allow fake SctpTransports to be injected
|
|
// for testing. Or, theoretically, SctpTransportInternal implementations that
|
|
// use something other than usrsctp.
|
|
class SctpTransportInternalFactory {
|
|
public:
|
|
virtual ~SctpTransportInternalFactory() {}
|
|
|
|
// Create an SCTP transport using |channel| for the underlying transport.
|
|
virtual std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
|
|
rtc::PacketTransportInternal* channel) = 0;
|
|
};
|
|
|
|
} // namespace cricket
|
|
|
|
#endif // MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
|