@Override @Nullable protected SslInfo initSslInfo() { SslHandler sslHandler = ((Connection) this.request).channel().pipeline().get(SslHandler.class); if (sslHandler != null) { SSLSession session = sslHandler.engine().getSession(); return new DefaultSslInfo(session); } return null; }
@Override public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) { if (!uri.isAbsolute()) { return Mono.error(new IllegalArgumentException("URI is not absolute: " + uri)); } return this.httpClient .request(io.netty.handler.codec.http.HttpMethod.valueOf(method.name())) .uri(uri.toString()) .send((request, outbound) -> requestCallback.apply(adaptRequest(method, uri, request, outbound))) .responseConnection((res, con) -> Mono.just(adaptResponse(res, con.inbound(), con.outbound().alloc()))) .next(); }
private static WebClient.Builder createDefaultWebClient(Duration connectTimeout, Duration readTimeout) { HttpClient httpClient = HttpClient.create() .compress(true) .tcpConfiguration(tcp -> tcp.bootstrap(bootstrap -> bootstrap.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis() )).observe((connection, newState) -> { if (ConnectionObserver.State.CONNECTED.equals(newState)) { connection.addHandlerLast(new ReadTimeoutHandler(readTimeout.toMillis(), TimeUnit.MILLISECONDS )); } })); ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); return WebClient.builder().clientConnector(connector); } }
@Override @SuppressWarnings("unchecked") public void onStateChange(Connection connection, State newState) { if (newState == State.CONFIGURED) { try { if (log.isDebugEnabled()) { log.debug(format(connection.channel(), "Handler is being applied: {}"), handler); } Mono.fromDirect(handler.apply(connection.inbound(), connection.outbound())) .subscribe(connection.disposeSubscriber()); } catch (Throwable t) { log.error(format(connection.channel(), ""), t); connection.channel() .close(); } } } }
@Override public Flux<Frame> receive() { return connection .inbound() .receive() .map( buf -> { CompositeByteBuf composite = connection.channel().alloc().compositeBuffer(); ByteBuf length = wrappedBuffer(new byte[FRAME_LENGTH_SIZE]); FrameHeaderFlyweight.encodeLength(length, 0, buf.readableBytes()); composite.addComponents(true, length, buf.retain()); return Frame.from(composite); }); }
@Override public Mono<Void> onClose() { return connection .onDispose() .doFinally( s -> { if (!channelClosed.isDisposed()) { channelClosed.dispose(); } }); }
@Override public void onStateChange(Connection connection, State newState) { if (newState == State.DISCONNECTING) { if (connection.channel() .isActive() && !connection.isPersistent()) { connection.dispose(); } } childObs.onStateChange(connection, newState); } }
static void autoAddHttpExtractor(Connection c, String name, ChannelHandler handler){ if (handler instanceof ByteToMessageDecoder || handler instanceof ByteToMessageCodec || handler instanceof CombinedChannelDuplexHandler) { String extractorName = name+"$extractor"; if(c.channel().pipeline().context(extractorName) != null){ return; } c.channel().pipeline().addBefore(name, extractorName, HTTP_EXTRACTOR); if(c.isPersistent()){ c.onTerminate().subscribe(null, null, () -> c.removeHandler(extractorName)); } } }
static void addChunkedWriter(Connection c){ if (c.channel() .pipeline() .get(ChunkedWriteHandler.class) == null) { c.addHandlerLast(NettyPipeline.ChunkedWriter, new ChunkedWriteHandler()); } }
.subscribe(c -> { System.out.println("context.dispose()"); conn.dispose(); latch.countDown(); }); conn.onDispose() .subscribe( AtomicBoolean disposed = new AtomicBoolean(false); in.withConnection(conn -> { disposed.set(conn.isDisposed()); System.out.println("context.isDisposed() " + conn.isDisposed()); }); if (disposed.get()) {
/** * Lambda setter for reacting on channel close occurrence. * * @param disposable function to run when disposable would take place */ public Mono<Void> onClose(Disposable disposable) { return Mono.create( sink -> inbound.withConnection( connection -> connection .onDispose(disposable) .onTerminate() .subscribe(sink::success, sink::error, sink::success))); }
private Mono<Void> closeConnections() { return Mono.defer( () -> Mono.when( connections .stream() .map( connection -> { connection.dispose(); return connection .onTerminate() .doOnError(e -> LOGGER.warn("Failed to close connection: " + e)) .onErrorResume(e -> Mono.empty()); }) .collect(Collectors.toList())) .doOnTerminate(connections::clear)); }
@Test public void addByteDecoderWhenEmptyPipeline() { ChannelHandler decoder = new LineBasedFrameDecoder(12); testContext.addHandlerLast("decoder", decoder) .addHandlerFirst("decoder$extract", NettyPipeline.inboundHandler(ADD_EXTRACTOR)); assertEquals(channel.pipeline() .names(), Arrays.asList("decoder$extract", "decoder", "DefaultChannelPipeline$TailContext#0")); }
@Override public void dispose() { connection.dispose(); }
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_CONN_ATTR is not added // until the NettyRoutingFilter is run return chain.filter(exchange).then(Mono.defer(() -> { Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR); if (connection == null) { return Mono.empty(); } log.trace("NettyWriteResponseFilter start"); ServerHttpResponse response = exchange.getResponse(); NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory(); //TODO: what if it's not netty final Flux<NettyDataBuffer> body = connection.inbound().receive() .retain() //TODO: needed? .map(factory::wrap); MediaType contentType = null; try { contentType = response.getHeaders().getContentType(); } catch (Exception e) { log.trace("invalid media type", e); } return (isStreamingMediaType(contentType) ? response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body)); })); }
@Override public Mono<CloseableChannel> start(ConnectionAcceptor acceptor) { Objects.requireNonNull(acceptor, "acceptor must not be null"); return server .doOnConnection( c -> { c.addHandlerLast(new RSocketLengthCodec()); TcpDuplexConnection connection = new TcpDuplexConnection(c); acceptor.apply(connection).then(Mono.<Void>never()).subscribe(c.disposeSubscriber()); }) .bind() .map(CloseableChannel::new); } }
/** * Add a {@link ChannelHandler} with {@link #addHandlerFirst} if of type of * {@link io.netty.channel.ChannelOutboundHandler} otherwise with * {@link #addHandlerLast}. Implementation may add more auto handling in particular * HTTP based context will prepend an HttpContent body extractor. * <p> * {@code [ [reactor codecs], [<- user FIRST HANDLERS added here, user LAST HANDLERS added here ->], [reactor handlers] ]} * <p> * If effectively added, the handler will be safely removed when the channel is made * inactive (pool release). * * @param handler handler instance * * @return this Connection */ default Connection addHandler(ChannelHandler handler){ return addHandler(handler.getClass().getSimpleName(), handler); }
static void registerForClose(boolean shouldCleanupOnClose, String name, Connection context) { if (!shouldCleanupOnClose) return; context.onTerminate().subscribe(null, null, () -> context.removeHandler(name)); }
private Mono<? extends Void> send0(Connection conn, Message message, Address address) { // check sender not null Objects.requireNonNull(message.sender(), "sender must be not null"); // do send return conn.outbound() .options(SendOptions::flushOnEach) .send( Mono.just(message) .flatMap(msg -> networkEmulator.tryFail(msg, address)) .flatMap(msg -> networkEmulator.tryDelay(msg, address)) .map(this::toByteBuf)) .then(); }