Chromium Code Reviews| Index: webrtc/p2p/client/basicportallocator.cc | 
| diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc | 
| index e39a44013cdcf4f5b4cf627e225c499afb1e1687..3c970f377b031d639369072f16e0da1682681fda 100644 | 
| --- a/webrtc/p2p/client/basicportallocator.cc | 
| +++ b/webrtc/p2p/client/basicportallocator.cc | 
| @@ -47,6 +47,36 @@ const int PHASE_SSLTCP = 3; | 
| const int kNumPhases = 4; | 
| +// Gets address family priority: IPv6 > IPv4 > Unspecified. | 
| +int GetAddressFamilyPriority(int ip_family) { | 
| + switch (ip_family) { | 
| + case AF_INET6: | 
| + return 2; | 
| + case AF_INET: | 
| + return 1; | 
| + default: | 
| + RTC_DCHECK(false); | 
| + return 0; | 
| + } | 
| +} | 
| + | 
| +// Returns positive if a is better, negative if b is better, and 0 otherwise. | 
| +int ComparePort(const cricket::Port* a, const cricket::Port* b) { | 
| + static constexpr int a_is_better = 1; | 
| + static constexpr int b_is_better = -1; | 
| + // Protocol type is defined as UDP = 0, TCP = 1, SSLTCP = 2. | 
| + if (a->GetProtocol() < b->GetProtocol()) { | 
| + return a_is_better; | 
| + } | 
| + if (a->GetProtocol() > b->GetProtocol()) { | 
| + return b_is_better; | 
| + } | 
| + | 
| + int a_family = GetAddressFamilyPriority(a->Network()->GetBestIP().family()); | 
| + int b_family = GetAddressFamilyPriority(b->Network()->GetBestIP().family()); | 
| + return a_family - b_family; | 
| +} | 
| + | 
| } // namespace | 
| namespace cricket { | 
| @@ -74,7 +104,7 @@ BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager, | 
| const ServerAddresses& stun_servers) | 
| : network_manager_(network_manager), socket_factory_(socket_factory) { | 
| ASSERT(socket_factory_ != NULL); | 
| - SetConfiguration(stun_servers, std::vector<RelayServerConfig>(), 0); | 
| + SetConfiguration(stun_servers, std::vector<RelayServerConfig>(), 0, false); | 
| Construct(); | 
| } | 
| @@ -101,7 +131,7 @@ BasicPortAllocator::BasicPortAllocator( | 
| turn_servers.push_back(config); | 
| } | 
| - SetConfiguration(stun_servers, turn_servers, 0); | 
| + SetConfiguration(stun_servers, turn_servers, 0, false); | 
| Construct(); | 
| } | 
| @@ -122,7 +152,8 @@ PortAllocatorSession* BasicPortAllocator::CreateSessionInternal( | 
| void BasicPortAllocator::AddTurnServer(const RelayServerConfig& turn_server) { | 
| std::vector<RelayServerConfig> new_turn_servers = turn_servers(); | 
| new_turn_servers.push_back(turn_server); | 
| - SetConfiguration(stun_servers(), new_turn_servers, candidate_pool_size()); | 
| + SetConfiguration(stun_servers(), new_turn_servers, candidate_pool_size(), | 
| + prune_turn_ports()); | 
| } | 
| // BasicPortAllocatorSession | 
| @@ -217,9 +248,9 @@ void BasicPortAllocatorSession::ClearGettingPorts() { | 
| std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const { | 
| std::vector<PortInterface*> ret; | 
| - for (const PortData& port : ports_) { | 
| - if (port.has_pairable_candidate() && !port.error()) { | 
| - ret.push_back(port.port()); | 
| + for (const PortData& data : ports_) { | 
| + if (data.ready()) { | 
| 
 
Taylor Brandstetter
2016/06/29 20:57:35
To me this is a little confusing because it's not
 
honghaiz3
2016/06/29 22:53:29
It basically means the port has a pairable candida
 
 | 
| + ret.push_back(data.port()); | 
| } | 
| } | 
| return ret; | 
| @@ -228,6 +259,10 @@ std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const { | 
| std::vector<Candidate> BasicPortAllocatorSession::ReadyCandidates() const { | 
| std::vector<Candidate> candidates; | 
| for (const PortData& data : ports_) { | 
| + if (!data.ready()) { | 
| + continue; | 
| + } | 
| + | 
| for (const Candidate& candidate : data.port()->Candidates()) { | 
| if (!CheckCandidateFilter(candidate)) { | 
| continue; | 
| @@ -278,16 +313,11 @@ bool BasicPortAllocatorSession::CandidatesAllocationDone() const { | 
| return false; | 
| } | 
| - // If all allocated ports are in complete state, session must have got all | 
| + // If all allocated ports are no longer gathering, session must have got all | 
| // expected candidates. Session will trigger candidates allocation complete | 
| // signal. | 
| - if (!std::all_of(ports_.begin(), ports_.end(), [](const PortData& port) { | 
| - return (port.complete() || port.error()); | 
| - })) { | 
| - return false; | 
| - } | 
| - | 
| - return true; | 
| + return std::none_of(ports_.begin(), ports_.end(), | 
| + [](const PortData& port) { return port.inprogress(); }); | 
| } | 
| void BasicPortAllocatorSession::OnMessage(rtc::Message *message) { | 
| @@ -357,7 +387,7 @@ void BasicPortAllocatorSession::OnConfigStop() { | 
| bool send_signal = false; | 
| for (std::vector<PortData>::iterator it = ports_.begin(); | 
| it != ports_.end(); ++it) { | 
| - if (!it->complete() && !it->error()) { | 
| + if (it->inprogress()) { | 
| // Updating port state to error, which didn't finish allocating candidates | 
| // yet. | 
| it->set_error(); | 
| @@ -443,11 +473,8 @@ void BasicPortAllocatorSession::DoAllocate() { | 
| LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated"; | 
| done_signal_needed = true; | 
| } else { | 
| + PortConfiguration* config = configs_.empty() ? nullptr : configs_.back(); | 
| for (uint32_t i = 0; i < networks.size(); ++i) { | 
| - PortConfiguration* config = NULL; | 
| - if (configs_.size() > 0) | 
| - config = configs_.back(); | 
| - | 
| uint32_t sequence_flags = flags(); | 
| if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) { | 
| // If all the ports are disabled we should just fire the allocation | 
| @@ -568,37 +595,84 @@ void BasicPortAllocatorSession::OnCandidateReady( | 
| PortData* data = FindPort(port); | 
| ASSERT(data != NULL); | 
| // Discarding any candidate signal if port allocation status is | 
| - // already in completed state. | 
| - if (data->complete() || data->error()) { | 
| + // already done with gathering. | 
| + if (!data->inprogress()) { | 
| return; | 
| } | 
| + // Mark that the port has a pairable candidate, either because we have a | 
| + // usable candidate from the port, or simply because the port is bound to the | 
| + // any address and therefore has no host candidate. This will trigger the port | 
| + // to start creating candidate pairs (connections) and issue connectivity | 
| + // checks. If port has already been marked as having a pairable candidate, | 
| + // do nothing here. | 
| + // Note: We should check whether any candidates may become ready after this | 
| + // because there we will check whether the candidate is generated by the ready | 
| + // ports, which may include this port. | 
| + if (CandidatePairable(c, port) && !data->has_pairable_candidate()) { | 
| + data->set_has_pairable_candidate(true); | 
| + | 
| + bool pruned_port = false; | 
| + if (allocator_->prune_turn_ports() && port->Type() == RELAY_PORT_TYPE) { | 
| 
 
Taylor Brandstetter
2016/06/29 20:57:35
It's not safe to access allocator_->prune_turn_por
 
honghaiz3
2016/06/29 22:53:29
Good observation. Done.
 
 | 
| + pruned_port = PruneTurnPorts(port); | 
| + } | 
| + // If the current port is not pruned yet, SignalPortReady. | 
| + if (!data->pruned()) { | 
| + SignalPortReady(this, port); | 
| + } | 
| + // If we have pruned any port, maybe need to signal port allocation done. | 
| + if (pruned_port) { | 
| + MaybeSignalCandidatesAllocationDone(); | 
| 
 
Taylor Brandstetter
2016/06/29 20:57:35
Does this mean we may signal candidate allocation
 
honghaiz3
2016/06/29 22:53:29
Done. Move this after the SignalCandidatesReady.
 
 | 
| + } | 
| + } | 
| + | 
| ProtocolType pvalue; | 
| bool candidate_protocol_enabled = | 
| StringToProto(c.protocol().c_str(), &pvalue) && | 
| data->sequence()->ProtocolEnabled(pvalue); | 
| - if (CheckCandidateFilter(c) && candidate_protocol_enabled) { | 
| + if (data->ready() && CheckCandidateFilter(c) && candidate_protocol_enabled) { | 
| std::vector<Candidate> candidates; | 
| candidates.push_back(SanitizeRelatedAddress(c)); | 
| SignalCandidatesReady(this, candidates); | 
| } | 
| +} | 
| - // Port has already been marked as having a pairable candidate. | 
| - // Nothing to do here. | 
| - if (data->has_pairable_candidate()) { | 
| - return; | 
| +Port* BasicPortAllocatorSession::GetBestTurnPortForNetwork( | 
| + const std::string& network_name) const { | 
| + Port* best_turn_port = nullptr; | 
| + for (const PortData& data : ports_) { | 
| + if (data.port()->Network()->name() == network_name && | 
| + data.port()->Type() == RELAY_PORT_TYPE && data.ready() && | 
| + (!best_turn_port || ComparePort(data.port(), best_turn_port) > 0)) { | 
| + best_turn_port = data.port(); | 
| + } | 
| } | 
| - | 
| - // Mark that the port has a pairable candidate, either because we have a | 
| - // usable candidate from the port, or simply because the port is bound to the | 
| - // any address and therefore has no host candidate. This will trigger the port | 
| - // to start creating candidate pairs (connections) and issue connectivity | 
| - // checks. | 
| - if (CandidatePairable(c, port)) { | 
| - data->set_has_pairable_candidate(true); | 
| - SignalPortReady(this, port); | 
| + return best_turn_port; | 
| +} | 
| + | 
| +bool BasicPortAllocatorSession::PruneTurnPorts(Port* newly_pairable_turn_port) { | 
| + bool pruned_port = false; | 
| + // Note: We determine the same network based only on their network names. So | 
| + // if an IPv4 address and an IPv6 address have the same network name, they | 
| + // are considered the same network here. | 
| + const std::string& network_name = newly_pairable_turn_port->Network()->name(); | 
| + Port* best_turn_port = GetBestTurnPortForNetwork(network_name); | 
| + // |port| is already in the list of ports, so the best port cannot be nullptr. | 
| + RTC_CHECK(best_turn_port != nullptr); | 
| + | 
| + for (PortData& data : ports_) { | 
| + if (data.port()->Network()->name() == network_name && | 
| + data.port()->Type() == RELAY_PORT_TYPE && !data.pruned() && | 
| + ComparePort(data.port(), best_turn_port) < 0) { | 
| + data.set_pruned(); | 
| + pruned_port = true; | 
| + if (data.port() != newly_pairable_turn_port) { | 
| + SignalPortPruned(this, data.port()); | 
| + } | 
| + } | 
| } | 
| + return pruned_port; | 
| } | 
| void BasicPortAllocatorSession::OnPortComplete(Port* port) { | 
| @@ -607,7 +681,7 @@ void BasicPortAllocatorSession::OnPortComplete(Port* port) { | 
| ASSERT(data != NULL); | 
| // Ignore any late signals. | 
| - if (data->complete() || data->error()) { | 
| + if (!data->inprogress()) { | 
| return; | 
| } | 
| @@ -622,7 +696,7 @@ void BasicPortAllocatorSession::OnPortError(Port* port) { | 
| PortData* data = FindPort(port); | 
| ASSERT(data != NULL); | 
| // We might have already given up on this port and stopped it. | 
| - if (data->complete() || data->error()) { | 
| + if (!data->inprogress()) { | 
| return; | 
| } | 
| @@ -1028,13 +1102,11 @@ void AllocationSequence::CreateRelayPorts() { | 
| return; | 
| } | 
| - PortConfiguration::RelayList::const_iterator relay; | 
| - for (relay = config_->relays.begin(); | 
| - relay != config_->relays.end(); ++relay) { | 
| - if (relay->type == RELAY_GTURN) { | 
| - CreateGturnPort(*relay); | 
| - } else if (relay->type == RELAY_TURN) { | 
| - CreateTurnPort(*relay); | 
| + for (RelayServerConfig& relay : config_->relays) { | 
| + if (relay.type == RELAY_GTURN) { | 
| + CreateGturnPort(relay); | 
| + } else if (relay.type == RELAY_TURN) { | 
| + CreateTurnPort(relay); | 
| } else { | 
| ASSERT(false); | 
| } |