def test_layer_stack(tctx): stack = tunnel.LayerStack() a = TChildLayer(tctx) b = TChildLayer(tctx) stack /= a stack /= b assert stack[0] == a assert a.child_layer is b stack2 = tunnel.LayerStack() stack2 /= TChildLayer(tctx) stack2 /= stack assert stack2[0].child_layer is a # type: ignore
def get_connection(self, event: GetHttpConnection, *, reuse: bool = True) -> layer.CommandGenerator[None]: # Do we already have a connection we can re-use? if reuse: for connection in self.connections: # see "tricky multiplexing edge case" in make_http_connection for an explanation conn_is_pending_or_h2 = ( connection.alpn == b"h2" or connection in self.waiting_for_establishment ) h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2 connection_suitable = ( event.connection_spec_matches(connection) and not h2_to_h1 ) if connection_suitable: if connection in self.waiting_for_establishment: self.waiting_for_establishment[connection].append(event) return elif connection.connected: stream = self.command_sources.pop(event) yield from self.event_to_child(stream, GetHttpConnectionCompleted(event, (connection, None))) return else: pass # the connection is at least half-closed already, we want a new one. can_use_context_connection = ( self.context.server not in self.connections and self.context.server.connected and event.connection_spec_matches(self.context.server) ) context = self.context.fork() stack = tunnel.LayerStack() if not can_use_context_connection: context.server = Server(event.address) if event.tls: context.server.sni = event.address[0] if event.via: assert event.via.scheme in ("http", "https") http_proxy = Server(event.via.address) if event.via.scheme == "https": http_proxy.alpn_offers = tls.HTTP_ALPNS http_proxy.sni = event.via.address[0] stack /= tls.ServerTLSLayer(context, http_proxy) send_connect = not (self.mode == HTTPMode.upstream and not event.tls) stack /= _upstream_proxy.HttpUpstreamProxy(context, http_proxy, send_connect) if event.tls: stack /= tls.ServerTLSLayer(context) stack /= HttpClient(context) self.connections[context.server] = stack[0] self.waiting_for_establishment[context.server].append(event) yield from self.event_to_child(stack[0], events.Start())
def test_layer_stack(tctx): stack = tunnel.LayerStack() a = TChildLayer(tctx) b = TChildLayer(tctx) stack /= a stack /= b assert stack[0] == a assert a.child_layer is b
def handle_connect_upstream(self): assert self.context.server.via.scheme in ("http", "https") http_proxy = Server(self.context.server.via.address) stack = tunnel.LayerStack() if self.context.server.via.scheme == "https": http_proxy.sni = self.context.server.via.address[0] stack /= tls.ServerTLSLayer(self.context, http_proxy) stack /= _upstream_proxy.HttpUpstreamProxy(self.context, http_proxy, True) self.child_layer = stack[0] yield from self.handle_connect_finish()
def make(cls, ctx: context.Context, send_connect: bool) -> tunnel.LayerStack: spec = ctx.server.via assert spec assert spec.scheme in ("http", "https") http_proxy = connection.Server(spec.address) stack = tunnel.LayerStack() if spec.scheme == "https": http_proxy.alpn_offers = tls.HTTP1_ALPNS http_proxy.sni = spec.address[0] stack /= tls.ServerTLSLayer(ctx, http_proxy) stack /= cls(ctx, http_proxy, send_connect) return stack
def get_connection(self, event: GetHttpConnection, *, reuse: bool = True) -> layer.CommandGenerator[None]: # Do we already have a connection we can re-use? if reuse: for connection in self.connections: connection_suitable = ( event.connection_spec_matches(connection)) if connection_suitable: if connection in self.waiting_for_establishment: self.waiting_for_establishment[connection].append( event) return elif connection.error: stream = self.command_sources.pop(event) yield from self.event_to_child( stream, GetHttpConnectionCompleted( event, (None, connection.error))) return elif connection.connected: # see "tricky multiplexing edge case" in make_http_connection for an explanation h2_to_h1 = self.context.client.alpn == b"h2" and connection.alpn != b"h2" if not h2_to_h1: stream = self.command_sources.pop(event) yield from self.event_to_child( stream, GetHttpConnectionCompleted( event, (connection, None))) return else: pass # the connection is at least half-closed already, we want a new one. context_connection_matches = ( self.context.server not in self.connections and event.connection_spec_matches(self.context.server)) can_use_context_connection = (context_connection_matches and self.context.server.connected) if context_connection_matches and self.context.server.error: stream = self.command_sources.pop(event) yield from self.event_to_child( stream, GetHttpConnectionCompleted(event, (None, self.context.server.error))) return context = self.context.fork() stack = tunnel.LayerStack() if not can_use_context_connection: context.server = Server(event.address) if event.via: context.server.via = event.via assert event.via.scheme in ("http", "https") # We always send a CONNECT request, *except* for plaintext absolute-form HTTP requests in upstream mode. send_connect = event.tls or self.mode != HTTPMode.upstream stack /= _upstream_proxy.HttpUpstreamProxy.make( context, send_connect) if event.tls: # Assume that we are in transparent mode and lazily did not open a connection yet. # We don't want the IP (which is the address) as the upstream SNI, but the client's SNI instead. if self.mode == HTTPMode.transparent and event.address == self.context.server.address: context.server.sni = self.context.client.sni or event.address[ 0] else: context.server.sni = event.address[0] stack /= tls.ServerTLSLayer(context) stack /= HttpClient(context) self.connections[context.server] = stack[0] self.waiting_for_establishment[context.server].append(event) yield from self.event_to_child(stack[0], events.Start())