1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import collections
6
7
8PortPair = collections.namedtuple('PortPair', ['local_port', 'remote_port'])
9PortSet = collections.namedtuple('PortSet', ['http', 'https', 'dns'])
10
11class PortPairs(collections.namedtuple('PortPairs', ['http', 'https', 'dns'])):
12  __slots__ = ()
13
14  @classmethod
15  def Zip(cls, local_ports, remote_ports):
16    """Zip a pair of PortSet's into a single PortPairs object."""
17    with_dns = local_ports.dns is not None and remote_ports.dns is not None
18    return cls(
19      PortPair(local_ports.http, remote_ports.http),
20      PortPair(local_ports.https, remote_ports.https),
21      PortPair(local_ports.dns, remote_ports.dns) if with_dns else None)
22
23  @property
24  def local_ports(self):
25    """Return a tuple of local ports only."""
26    return PortSet(*[p.local_port if p is not None else None for p in self])
27
28  @property
29  def remote_ports(self):
30    """Return a tuple of remote ports only."""
31    return PortSet(*[p.remote_port if p is not None else None for p in self])
32
33
34class ForwarderFactory(object):
35
36  def Create(self, port_pairs):
37    """Creates a forwarder that maps remote (device) <-> local (host) ports.
38
39    Args:
40      port_pairs: A PortPairs instance that consists of a PortPair mapping
41          for each protocol. http is required. https and dns may be None.
42    """
43    raise NotImplementedError()
44
45  @property
46  def host_ip(self):
47    return '127.0.0.1'
48
49
50class Forwarder(object):
51
52  def __init__(self, port_pairs):
53    assert port_pairs.http, 'HTTP port mapping is required.'
54    self._port_pairs = PortPairs(*[
55        PortPair(p.local_port, p.remote_port or p.local_port)
56        if p else None for p in port_pairs])
57    self._forwarding = True
58
59  @property
60  def host_port(self):
61    return self._port_pairs.http.remote_port
62
63  @property
64  def host_ip(self):
65    return '127.0.0.1'
66
67  @property
68  def port_pairs(self):
69    return self._port_pairs
70
71  @property
72  def url(self):
73    assert self.host_ip and self.host_port
74    return 'http://%s:%i' % (self.host_ip, self.host_port)
75
76  def Close(self):
77    self._port_pairs = None
78    self._forwarding = False
79