@Override default boolean isDisposed() { return !channel().isActive(); }
@Override default void dispose() { channel().close(); }
private boolean hasChunkedWriter(NettyContext context) { return context.channel() .pipeline() .get(NettyPipeline.ChunkedWriter) != null; }
/** * Mark the underlying channel as persistent or not. * If false, it will force a close on terminal protocol events thus defeating * any pooling strategy * if true (default), it will release on terminal protocol events thus * keeping alive the channel if possible. * * @param persist the boolean flag to mark the {@link Channel} as fully disposable * or reusable when a user handler has terminated * * @return this NettyContext */ default NettyContext markPersistent(boolean persist){ if(persist && !channel().hasAttr(ReactorNetty.PERSISTENT_CHANNEL)) { return this; } else { channel().attr(ReactorNetty.PERSISTENT_CHANNEL) .set(persist); } return this; }
/** * Return a pre-configured attribute stored in every inbound channel * @param key attribute key * @param <T> a channel attribute type * @return a {@link Channel} attribute * @see Channel#attr(AttributeKey) */ default <T> Attribute<T> attr(AttributeKey<T> key) { return context().channel() .attr(key); }
/** * Return an observing {@link Mono} terminating with success when shutdown * successfully * or error. * * @return a {@link Mono} terminating with success if shutdown successfully or error */ default Mono<Void> onClose(){ return FutureMono.from(channel().closeFuture()); }
/** * Remove a named handler if present and return this context * * @param name handler name * * @return this NettyContext */ default NettyContext removeHandler(String name) { ReactorNetty.removeHandler(channel(), name); return this; }
/** * Replace a named handler if present and return this context. * If handler wasn't present, an {@link RuntimeException} will be thrown. * <p> * Note: if the new handler is of different type, dependent handling like * the "extractor" introduced via HTTP-based {@link #addHandler} might not * expect/support the new messages type. * * @param name handler name * * @return this NettyContext */ default NettyContext replaceHandler(String name, ChannelHandler handler) { ReactorNetty.replaceHandler(channel(), name, handler); return this; } }
static void autoAddHttpExtractor(NettyContext 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(NettyContext.isPersistent(c.channel())){ c.onClose(() -> c.removeHandler(extractorName)); } } }
/** * {@inheritDoc} * <p> * This removes the ChunkedWriter handler if it was added by this strategy. It then * calls the {@link #afterWrite(NettyContext)} method * * @param context the context from which to obtain the channel and pipeline */ @Override public final void cleanupPipeline(NettyContext context) { if (hasChunkedWriter(context)) { context.channel() .pipeline() .remove(NettyPipeline.ChunkedWriter); } afterWrite(context); }
/** * Return the assigned {@link ByteBufAllocator}. * * @return the {@link ByteBufAllocator} */ default ByteBufAllocator alloc() { return context().channel() .alloc(); }
/** * {@inheritDoc} * <p> * This adds a ChunkedWriter to the pipeline to extract chunks from the * {@link io.netty.handler.stream.ChunkedInput} that the strategy produces. This step * is skipped if the handler is already present, and the placement of the handler * depends on the presence of the ReactiveBridge handler (see {@link NettyPipeline}). * * @param context the context from which to obtain the channel and pipeline */ @Override public final void preparePipeline(NettyContext context) { if (!hasChunkedWriter(context)) { boolean hasReactiveBridge = context.channel() .pipeline() .get(NettyPipeline.ReactiveBridge) != null; if (hasReactiveBridge) { context.channel() .pipeline() .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.ChunkedWriter, new ChunkedWriteHandler()); } else { context.channel() .pipeline() .addLast(NettyPipeline.ChunkedWriter, new ChunkedWriteHandler()); } } }
/** * Provide a new {@link NettyOutbound} scoped configuration for sending. The * {@link NettyPipeline.SendOptions} changes will apply to the next written object or * {@link Publisher}. * * @param configurator the callback invoked to retrieve send configuration * * @return {@code this} instance */ default NettyOutbound options(Consumer<? super NettyPipeline.SendOptions> configurator) { context().channel() .pipeline() .fireUserEventTriggered(new NettyPipeline.SendOptionsChangeEvent(configurator, null)); return this; }
/** * Send Object to the peer, listen for any error on write and close on terminal signal * (complete|error). If more than one publisher is attached (multiple calls to send()) * completion occurs after all publishers complete. * Note: Nesting any send* method is not supported. * * @param dataStream the dataStream publishing Buffer items to write on this channel * * @return A Publisher to signal successful sequence write (e.g. after "flush") or any * error during write */ default NettyOutbound sendObject(Publisher<?> dataStream) { return then(FutureMono.disposableWriteAndFlush(context().channel(), dataStream)); }
/** * A {@link Flux} extension that allows for extra decoding operators * @return a new {@link ByteBufFlux} */ default ByteBufFlux receive() { return ByteBufFlux.fromInbound(receiveObject(), context().channel() .alloc()); }
/** * Return remote address if remote channel {@link NettyContext} otherwise local * address if server selector channel. * * @return remote or local {@link InetSocketAddress} */ default InetSocketAddress address(){ Channel c = channel(); if (c instanceof SocketChannel) { return ((SocketChannel) c).remoteAddress(); } if (c instanceof ServerSocketChannel) { return ((ServerSocketChannel) c).localAddress(); } if (c instanceof DatagramChannel) { InetSocketAddress a = ((DatagramChannel) c).remoteAddress(); return a != null ? a : ((DatagramChannel)c ).localAddress(); } throw new IllegalStateException("Does not have an InetSocketAddress"); }
/** * Send data to the peer, listen for any error on write and close on terminal signal * (complete|error). * Note: Nesting any send* method is not supported. * * @param msg the object to publish * * @return A {@link Mono} to signal successful sequence write (e.g. after "flush") or * any error during write */ default NettyOutbound sendObject(Object msg) { context().onClose(() -> ReactorNetty.safeRelease(msg)); return then(FutureMono.deferFuture(() -> context().channel() .writeAndFlush(msg))); }
@Override public Mono<Void> then() { ByteBufAllocator alloc = parent.channel().alloc(); return Flux.from(source) .collect(alloc::heapBuffer, ByteBuf::writeBytes) .flatMap(agg -> { if (!HttpUtil.isTransferEncodingChunked(request) && !HttpUtil.isContentLengthSet(request)) { request.headers() .setInt(HttpHeaderNames.CONTENT_LENGTH, agg.readableBytes()); } return parent.then().thenEmpty(FutureMono.disposableWriteAndFlush(context().channel(), Mono.just(agg))); }); } }
Objects.requireNonNull(handler, "handler"); Channel channel = context.channel(); boolean exists = channel.pipeline().get(name) != null;
return FutureMono.from(context().channel().writeAndFlush(message));