def __init__(self, name, sygnal, config): super(WebpushPushkin, self).__init__(name, sygnal, config) nonunderstood = self.cfg.keys() - self.UNDERSTOOD_CONFIG_FIELDS if nonunderstood: logger.warning( "The following configuration fields are not understood: %s", nonunderstood, ) self.http_pool = HTTPConnectionPool(reactor=sygnal.reactor) self.max_connections = self.get_config("max_connections", DEFAULT_MAX_CONNECTIONS) self.connection_semaphore = DeferredSemaphore(self.max_connections) self.http_pool.maxPersistentPerHost = self.max_connections tls_client_options_factory = ClientTLSOptionsFactory() # use the Sygnal global proxy configuration proxy_url = sygnal.config.get("proxy") self.http_agent = ProxyAgent( reactor=sygnal.reactor, pool=self.http_pool, contextFactory=tls_client_options_factory, proxy_url_str=proxy_url, ) self.http_agent_wrapper = HttpAgentWrapper(self.http_agent) self.allowed_endpoints = None # type: Optional[List[Pattern]] allowed_endpoints = self.get_config("allowed_endpoints") if allowed_endpoints: if not isinstance(allowed_endpoints, list): raise PushkinSetupException( "'allowed_endpoints' should be a list or not set") self.allowed_endpoints = list(map(glob_to_regex, allowed_endpoints)) privkey_filename = self.get_config("vapid_private_key") if not privkey_filename: raise PushkinSetupException( "'vapid_private_key' not set in config") if not os.path.exists(privkey_filename): raise PushkinSetupException( "path in 'vapid_private_key' does not exist") try: self.vapid_private_key = Vapid.from_file( private_key_file=privkey_filename) except VapidException as e: raise PushkinSetupException( "invalid 'vapid_private_key' file") from e self.vapid_contact_email = self.get_config("vapid_contact_email") if not self.vapid_contact_email: raise PushkinSetupException( "'vapid_contact_email' not set in config") self.ttl = self.get_config("ttl", DEFAULT_TTL) if not isinstance(self.ttl, int): raise PushkinSetupException("'ttl' must be an int if set")
def __init__(self, name, sygnal, config, canonical_reg_id_store): super(GcmPushkin, self).__init__(name, sygnal, config) nonunderstood = set(self.cfg.keys()).difference( self.UNDERSTOOD_CONFIG_FIELDS) if len(nonunderstood) > 0: logger.warning( "The following configuration fields are not understood: %s", nonunderstood, ) self.http_pool = HTTPConnectionPool(reactor=sygnal.reactor) self.max_connections = self.get_config("max_connections", DEFAULT_MAX_CONNECTIONS) self.connection_semaphore = DeferredSemaphore(self.max_connections) self.http_pool.maxPersistentPerHost = self.max_connections tls_client_options_factory = ClientTLSOptionsFactory() # use the Sygnal global proxy configuration proxy_url = sygnal.config.get("proxy") self.http_agent = ProxyAgent( reactor=sygnal.reactor, pool=self.http_pool, contextFactory=tls_client_options_factory, proxy_url_str=proxy_url, ) self.db = sygnal.database self.canonical_reg_id_store = canonical_reg_id_store self.api_key = self.get_config("api_key") if not self.api_key: raise PushkinSetupException("No API key set in config") # Use the fcm_options config dictionary as a foundation for the body; # this lets the Sygnal admin choose custom FCM options # (e.g. content_available). self.base_request_body: dict = self.get_config("fcm_options", {}) if not isinstance(self.base_request_body, dict): raise PushkinSetupException( "Config field fcm_options, if set, must be a dictionary of options" )
def execute( self, http_agent: ProxyAgent, low_priority: bool, topic: bytes ) -> IResponse: body_producer = FileBodyProducer(BytesIO(self.data)) # Convert the headers to the camelcase version. headers = { b"User-Agent": ["sygnal"], b"Content-Encoding": [self.vapid_headers["content-encoding"]], b"Authorization": [self.vapid_headers["authorization"]], b"TTL": [self.vapid_headers["ttl"]], b"Urgency": ["low" if low_priority else "normal"], } if topic: headers[b"Topic"] = [topic] return http_agent.request( b"POST", self.endpoint.encode(), headers=Headers(headers), bodyProducer=body_producer, )
def test_https_request_via_proxy(self): agent = ProxyAgent( self.reactor, contextFactory=get_test_https_policy(), proxy_url_str="http://proxy.com:1080", ) self.reactor.lookups["proxy.com"] = "1.2.3.5" d = agent.request(b"GET", b"https://test.com/abc") # there should be a pending TCP connection clients = self.reactor.tcpClients self.assertEqual(len(clients), 1) (host, port, client_factory, _timeout, _bindAddress) = clients[0] self.assertEqual(host, "1.2.3.5") self.assertEqual(port, 1080) # make a test HTTP server, and wire up the client proxy_server = self._make_connection(client_factory, _get_test_protocol_factory()) # fish the transports back out so that we can do the old switcheroo s2c_transport = proxy_server.transport client_protocol = s2c_transport.other c2s_transport = client_protocol.transport # the FakeTransport is async, so we need to pump the reactor self.reactor.advance(0) # now there should be a pending CONNECT request self.assertEqual(len(proxy_server.requests), 1) request = proxy_server.requests[0] self.assertEqual(request.method, b"CONNECT") self.assertEqual(request.path, b"test.com:443") # tell the proxy server not to close the connection proxy_server.persistent = True # this just stops the http Request trying to do a chunked response # request.setHeader(b"Content-Length", b"0") request.finish() # now we can replace the proxy channel with a new, SSL-wrapped HTTP channel ssl_factory = _wrap_server_factory_for_tls( _get_test_protocol_factory()) ssl_protocol = ssl_factory.buildProtocol(None) http_server = ssl_protocol.wrappedProtocol ssl_protocol.makeConnection( FakeTransport(client_protocol, self.reactor, ssl_protocol)) c2s_transport.other = ssl_protocol self.reactor.advance(0) server_name = ssl_protocol._tlsConnection.get_servername() expected_sni = b"test.com" self.assertEqual( server_name, expected_sni, "Expected SNI %r but got %r" % (expected_sni, server_name), ) # now there should be a pending request self.assertEqual(len(http_server.requests), 1) request = http_server.requests[0] self.assertEqual(request.method, b"GET") self.assertEqual(request.path, b"/abc") self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"test.com"]) request.write(b"result") self.reactor.advance(0) request.finish() self.reactor.advance(0) resp = self.successResultOf(d) body = self.successResultOf(readBody(resp)) self.assertEqual(body, b"result")