Chromium Code Reviews| Index: webrtc/api/peerconnection.cc |
| diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc |
| index ebce40ca5eb0cf37d03243663826ea911eda06c3..676e87c24f154af6e56defc9ef71c18557b29f15 100644 |
| --- a/webrtc/api/peerconnection.cc |
| +++ b/webrtc/api/peerconnection.cc |
| @@ -49,6 +49,7 @@ using webrtc::DataChannel; |
| using webrtc::MediaConstraintsInterface; |
| using webrtc::MediaStreamInterface; |
| using webrtc::PeerConnectionInterface; |
| +using webrtc::RtcError; |
| using webrtc::RtpSenderInternal; |
| using webrtc::RtpSenderInterface; |
| using webrtc::RtpSenderProxy; |
| @@ -208,10 +209,11 @@ bool ParseHostnameAndPortFromString(const std::string& in_str, |
| // Adds a STUN or TURN server to the appropriate list, |
| // by parsing |url| and using the username/password in |server|. |
| -bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| - const std::string& url, |
| - cricket::ServerAddresses* stun_servers, |
| - std::vector<cricket::RelayServerConfig>* turn_servers) { |
| +RtcError ParseIceServerUrl( |
| + const PeerConnectionInterface::IceServer& server, |
| + const std::string& url, |
| + cricket::ServerAddresses* stun_servers, |
| + std::vector<cricket::RelayServerConfig>* turn_servers) { |
| // draft-nandakumar-rtcweb-stun-uri-01 |
| // stunURI = scheme ":" stun-host [ ":" stun-port ] |
| // scheme = "stun" / "stuns" |
| @@ -239,14 +241,14 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| rtc::tokenize_with_empty_tokens(uri_transport_param, '=', &tokens); |
| if (tokens[0] != kTransport) { |
| LOG(LS_WARNING) << "Invalid transport parameter key."; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| if (tokens.size() < 2 || |
| !cricket::StringToProto(tokens[1].c_str(), &turn_transport_type) || |
| (turn_transport_type != cricket::PROTO_UDP && |
| turn_transport_type != cricket::PROTO_TCP)) { |
| LOG(LS_WARNING) << "Transport param should always be udp or tcp."; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| } |
| @@ -256,7 +258,7 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| &service_type, |
| &hoststring)) { |
| LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " << url; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| // GetServiceTypeAndHostnameFromUri should never give an empty hoststring |
| @@ -269,12 +271,12 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| std::string username(server.username); |
| if (tokens.size() > kTurnHostTokensNum) { |
| LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| if (tokens.size() == kTurnHostTokensNum) { |
| if (tokens[0].empty() || tokens[1].empty()) { |
| LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| username.assign(rtc::s_url_decode(tokens[0])); |
| hoststring = tokens[1]; |
| @@ -291,12 +293,12 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| std::string address; |
| if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { |
| LOG(WARNING) << "Invalid hostname format: " << uri_without_transport; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| if (port <= 0 || port > 0xffff) { |
| LOG(WARNING) << "Invalid port: " << port; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| switch (service_type) { |
| @@ -306,16 +308,23 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, |
| break; |
| case TURN: |
| case TURNS: { |
| + if (username.empty() || server.password.empty()) { |
| + // The WebRTC spec requires throwing an InvalidAccessError when username |
| + // or |
| + // credential are ommitted; this is the native equivalent. |
| + return RtcError::INVALID_PARAMETER; |
| + } |
| turn_servers->push_back(cricket::RelayServerConfig( |
| address, port, username, server.password, turn_transport_type)); |
| break; |
| } |
| - case INVALID: |
| default: |
| - LOG(WARNING) << "Configuration not supported: " << url; |
| - return false; |
| + // We shouldn't get to this point with an invalid service_type, we should |
| + // have returned an error already. |
| + RTC_DCHECK(false) << "Unexpected service type"; |
| + return RtcError::INTERNAL_ERROR; |
| } |
| - return true; |
| + return RtcError::NONE; |
| } |
| // Check if we can send |new_stream| on a PeerConnection. |
| @@ -427,6 +436,14 @@ void SetChannelOnSendersAndReceivers(CHANNEL* channel, |
| } |
| } |
| +// Helper to set an error and return from a method. |
| +bool SafeSetError(webrtc::RtcError error, webrtc::RtcError* error_output) { |
| + if (error_output) { |
| + *error_output = error; |
| + } |
| + return error == webrtc::RtcError::NONE; |
| +} |
| + |
| } // namespace |
| namespace webrtc { |
| @@ -442,14 +459,84 @@ static const char* const kRtcErrorNames[] = { |
| "NETWORK_ERROR", |
| "INTERNAL_ERROR", |
| }; |
| +static_assert(static_cast<int>(RtcError::INTERNAL_ERROR) == |
| + (arraysize(kRtcErrorNames) - 1), |
| + "kRtcErrorNames must have as many strings as RtcError " |
| + "has values."); |
| std::ostream& operator<<(std::ostream& stream, RtcError error) { |
| int index = static_cast<int>(error); |
| - RTC_CHECK(index < static_cast<int>(sizeof(kRtcErrorNames) / |
| - sizeof(kRtcErrorNames[0]))); |
| return stream << kRtcErrorNames[index]; |
| } |
| +bool PeerConnectionInterface::RTCConfiguration::operator==( |
| + const PeerConnectionInterface::RTCConfiguration& o) const { |
| + // This static_assert prevents us from accidentally breaking operator==. |
| + struct stuff_being_tested_for_equality { |
| + IceTransportsType type; |
| + IceServers servers; |
| + BundlePolicy bundle_policy; |
| + RtcpMuxPolicy rtcp_mux_policy; |
| + TcpCandidatePolicy tcp_candidate_policy; |
| + CandidateNetworkPolicy candidate_network_policy; |
| + int audio_jitter_buffer_max_packets; |
| + bool audio_jitter_buffer_fast_accelerate; |
| + int ice_connection_receiving_timeout; |
| + int ice_backup_candidate_pair_ping_interval; |
| + ContinualGatheringPolicy continual_gathering_policy; |
| + std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates; |
| + bool prioritize_most_likely_ice_candidate_pairs; |
| + struct cricket::MediaConfig media_config; |
| + bool disable_ipv6; |
| + bool enable_rtp_data_channel; |
| + bool enable_quic; |
| + rtc::Optional<int> screencast_min_bitrate; |
| + rtc::Optional<bool> combined_audio_video_bwe; |
| + rtc::Optional<bool> enable_dtls_srtp; |
| + int ice_candidate_pool_size; |
| + bool prune_turn_ports; |
| + bool presume_writable_when_fully_relayed; |
| + bool enable_ice_renomination; |
| + bool redetermine_role_on_ice_restart; |
| + }; |
| + static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), |
| + "Did you add something to RTCConfiguration and forget to " |
| + "update operator==?"); |
|
Taylor Brandstetter
2016/12/20 02:04:39
This may seem like overkill, but I think there's a
|
| + return type == o.type && servers == o.servers && |
| + bundle_policy == o.bundle_policy && |
| + rtcp_mux_policy == o.rtcp_mux_policy && |
| + tcp_candidate_policy == o.tcp_candidate_policy && |
| + candidate_network_policy == o.candidate_network_policy && |
| + audio_jitter_buffer_max_packets == o.audio_jitter_buffer_max_packets && |
| + audio_jitter_buffer_fast_accelerate == |
| + o.audio_jitter_buffer_fast_accelerate && |
| + ice_connection_receiving_timeout == |
| + o.ice_connection_receiving_timeout && |
| + ice_backup_candidate_pair_ping_interval == |
| + o.ice_backup_candidate_pair_ping_interval && |
| + continual_gathering_policy == o.continual_gathering_policy && |
| + certificates == o.certificates && |
| + prioritize_most_likely_ice_candidate_pairs == |
| + o.prioritize_most_likely_ice_candidate_pairs && |
| + media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && |
| + enable_rtp_data_channel == o.enable_rtp_data_channel && |
| + enable_quic == o.enable_quic && |
| + screencast_min_bitrate == o.screencast_min_bitrate && |
| + combined_audio_video_bwe == o.combined_audio_video_bwe && |
| + enable_dtls_srtp == o.enable_dtls_srtp && |
| + ice_candidate_pool_size == o.ice_candidate_pool_size && |
| + prune_turn_ports == o.prune_turn_ports && |
| + presume_writable_when_fully_relayed == |
| + o.presume_writable_when_fully_relayed && |
| + enable_ice_renomination == o.enable_ice_renomination && |
| + redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart; |
| +} |
| + |
| +bool PeerConnectionInterface::RTCConfiguration::operator!=( |
| + const PeerConnectionInterface::RTCConfiguration& o) const { |
| + return !(*this == o); |
| +} |
| + |
| // Generate a RTCP CNAME when a PeerConnection is created. |
| std::string GenerateRtcpCname() { |
| std::string cname; |
| @@ -549,28 +636,33 @@ bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints, |
| return mandatory_constraints_satisfied == constraints->GetMandatory().size(); |
| } |
| -bool ParseIceServers(const PeerConnectionInterface::IceServers& servers, |
| - cricket::ServerAddresses* stun_servers, |
| - std::vector<cricket::RelayServerConfig>* turn_servers) { |
| +RtcError ParseIceServers( |
| + const PeerConnectionInterface::IceServers& servers, |
| + cricket::ServerAddresses* stun_servers, |
| + std::vector<cricket::RelayServerConfig>* turn_servers) { |
| for (const webrtc::PeerConnectionInterface::IceServer& server : servers) { |
| if (!server.urls.empty()) { |
| for (const std::string& url : server.urls) { |
| if (url.empty()) { |
| LOG(LS_ERROR) << "Empty uri."; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| - if (!ParseIceServerUrl(server, url, stun_servers, turn_servers)) { |
| - return false; |
| + RtcError err = |
| + ParseIceServerUrl(server, url, stun_servers, turn_servers); |
| + if (err != RtcError::NONE) { |
| + return err; |
| } |
| } |
| } else if (!server.uri.empty()) { |
| // Fallback to old .uri if new .urls isn't present. |
| - if (!ParseIceServerUrl(server, server.uri, stun_servers, turn_servers)) { |
| - return false; |
| + RtcError err = |
| + ParseIceServerUrl(server, server.uri, stun_servers, turn_servers); |
| + if (err != RtcError::NONE) { |
| + return err; |
| } |
| } else { |
| LOG(LS_ERROR) << "Empty uri."; |
| - return false; |
| + return RtcError::SYNTAX_ERROR; |
| } |
| } |
| // Candidates must have unique priorities, so that connectivity checks |
| @@ -580,7 +672,7 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& servers, |
| // First in the list gets highest priority. |
| turn_server.priority = priority--; |
| } |
| - return true; |
| + return RtcError::NONE; |
| } |
| PeerConnection::PeerConnection(PeerConnectionFactory* factory) |
| @@ -623,12 +715,18 @@ bool PeerConnection::Initialize( |
| std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, |
| PeerConnectionObserver* observer) { |
| TRACE_EVENT0("webrtc", "PeerConnection::Initialize"); |
| - RTC_DCHECK(observer != nullptr); |
| + if (!allocator) { |
| + LOG(LS_ERROR) << "PeerConnection initialized without a PortAllocator? " |
| + << "This shouldn't happen if using PeerConnectionFactory."; |
| + return false; |
|
Taylor Brandstetter
2016/12/20 02:04:39
There's no code path in which passing a null PortA
|
| + } |
| if (!observer) { |
| + // TODO(deadbeef): Why do we do this? |
| + LOG(LS_ERROR) << "PeerConnection initialized without a " |
| + << "PeerConnectionObserver"; |
| return false; |
| } |
| observer_ = observer; |
| - |
| port_allocator_ = std::move(allocator); |
| // The port allocator lives on the network thread and should be initialized |
| @@ -1284,7 +1382,8 @@ PeerConnectionInterface::RTCConfiguration PeerConnection::GetConfiguration() { |
| return configuration_; |
| } |
| -bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration) { |
| +bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, |
| + RtcError* error) { |
| TRACE_EVENT0("webrtc", "PeerConnection::SetConfiguration"); |
| if (session_->local_description() && |
| @@ -1292,32 +1391,61 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration) { |
| configuration_.ice_candidate_pool_size) { |
| LOG(LS_ERROR) << "Can't change candidate pool size after calling " |
| "SetLocalDescription."; |
| - return false; |
| + return SafeSetError(RtcError::INVALID_MODIFICATION, error); |
| } |
| - // TODO(deadbeef): Return false and log an error if there are any unsupported |
| - // modifications. |
| - if (port_allocator_) { |
| - if (!network_thread()->Invoke<bool>( |
| - RTC_FROM_HERE, |
| - rtc::Bind(&PeerConnection::ReconfigurePortAllocator_n, this, |
| - configuration))) { |
| - LOG(LS_ERROR) << "Failed to apply configuration to PortAllocator."; |
| - return false; |
| - } |
| + |
| + // The simplest (and most future-compatible) way to tell if the config was |
| + // modified in an invalid way is to copy each property we do support |
| + // modifying, then use operator==. There are far more properties we don't |
| + // support modifying than those we do, and more could be added. |
| + RTCConfiguration modified_config = configuration_; |
| + modified_config.servers = configuration.servers; |
| + modified_config.type = configuration.type; |
| + modified_config.ice_candidate_pool_size = |
| + configuration.ice_candidate_pool_size; |
| + modified_config.prune_turn_ports = configuration.prune_turn_ports; |
| + if (configuration != modified_config) { |
| + LOG(LS_ERROR) << "Modifying the configuration in an unsupported way."; |
| + return SafeSetError(RtcError::INVALID_MODIFICATION, error); |
| + } |
| + |
| + // Note that this isn't possible through chromium, since it's an unsigned |
| + // short in WebIDL. |
| + if (configuration.ice_candidate_pool_size < 0 || |
| + configuration.ice_candidate_pool_size > UINT16_MAX) { |
| + return SafeSetError(RtcError::INVALID_RANGE, error); |
| + } |
| + |
| + // Parse ICE servers before hopping to network thread. |
| + cricket::ServerAddresses stun_servers; |
| + std::vector<cricket::RelayServerConfig> turn_servers; |
| + RtcError parse_error = |
| + ParseIceServers(configuration.servers, &stun_servers, &turn_servers); |
| + if (parse_error != RtcError::NONE) { |
| + return SafeSetError(parse_error, error); |
| } |
| - // TODO(deadbeef): Shouldn't have to hop to the network thread twice... |
| - session_->SetIceConfig(session_->ParseIceConfig(configuration)); |
| + // In theory this shouldn't fail. |
| + if (!network_thread()->Invoke<bool>( |
| + RTC_FROM_HERE, |
| + rtc::Bind(&PeerConnection::ReconfigurePortAllocator_n, this, |
| + stun_servers, turn_servers, modified_config.type, |
| + modified_config.ice_candidate_pool_size, |
| + modified_config.prune_turn_ports))) { |
| + LOG(LS_ERROR) << "Failed to apply configuration to PortAllocator."; |
| + return SafeSetError(RtcError::INTERNAL_ERROR, error); |
| + } |
| // As described in JSEP, calling setConfiguration with new ICE servers or |
| // candidate policy must set a "needs-ice-restart" bit so that the next offer |
| // triggers an ICE restart which will pick up the changes. |
| - if (configuration.servers != configuration_.servers || |
| - configuration.type != configuration_.type) { |
| + if (modified_config.servers != configuration_.servers || |
| + modified_config.type != configuration_.type || |
| + modified_config.prune_turn_ports != configuration_.prune_turn_ports) { |
| session_->SetNeedsIceRestartFlag(); |
| } |
| - configuration_ = configuration; |
| - return true; |
| + configuration_ = modified_config; |
| + return SafeSetError(RtcError::NONE, error); |
| } |
| bool PeerConnection::AddIceCandidate( |
| @@ -1344,7 +1472,7 @@ void PeerConnection::RegisterUMAObserver(UMAObserver* observer) { |
| } |
| // Send information about IPv4/IPv6 status. |
| - if (uma_observer_ && port_allocator_) { |
| + if (uma_observer_) { |
| port_allocator_->SetMetricsObserver(uma_observer_); |
| if (port_allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_IPV6) { |
| uma_observer_->IncrementEnumCounter( |
| @@ -2337,7 +2465,8 @@ bool PeerConnection::InitializePortAllocator_n( |
| const RTCConfiguration& configuration) { |
| cricket::ServerAddresses stun_servers; |
| std::vector<cricket::RelayServerConfig> turn_servers; |
| - if (!ParseIceServers(configuration.servers, &stun_servers, &turn_servers)) { |
| + if (ParseIceServers(configuration.servers, &stun_servers, &turn_servers) != |
| + RtcError::NONE) { |
| return false; |
| } |
| @@ -2383,19 +2512,17 @@ bool PeerConnection::InitializePortAllocator_n( |
| } |
| bool PeerConnection::ReconfigurePortAllocator_n( |
| - const RTCConfiguration& configuration) { |
| - cricket::ServerAddresses stun_servers; |
| - std::vector<cricket::RelayServerConfig> turn_servers; |
| - if (!ParseIceServers(configuration.servers, &stun_servers, &turn_servers)) { |
| - return false; |
| - } |
| + const cricket::ServerAddresses& stun_servers, |
| + const std::vector<cricket::RelayServerConfig>& turn_servers, |
| + IceTransportsType type, |
| + int candidate_pool_size, |
| + bool prune_turn_ports) { |
| port_allocator_->set_candidate_filter( |
| - ConvertIceTransportTypeToCandidateFilter(configuration.type)); |
| + ConvertIceTransportTypeToCandidateFilter(type)); |
| // Call this last since it may create pooled allocator sessions using the |
| // candidate filter set above. |
| return port_allocator_->SetConfiguration( |
| - stun_servers, turn_servers, configuration.ice_candidate_pool_size, |
| - configuration.prune_turn_ports); |
| + stun_servers, turn_servers, candidate_pool_size, prune_turn_ports); |
| } |
| bool PeerConnection::StartRtcEventLog_w(rtc::PlatformFile file, |