Archimedes Trajano
Archimedes Trajano

Reputation: 41580

How do I connect to a UNIX domain socket running an HTTP server using Netty?

I am trying to connect to a Docker UNIX domain socket using Netty. Here's my attempt so far.

@PostConstruct
public void init() throws Exception {
    io.netty.bootstrap.Bootstrap bootstrap = new io.netty.bootstrap.Bootstrap();
    bootstrap
            .group(new NioEventLoopGroup())
            .channel(NioSocketChannel.class)
            .option(ChannelOption.SO_KEEPALIVE, true)
            .remoteAddress(new DomainSocketAddress("/var/run/docker.sock"))
            .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel
                            .pipeline()
                            .addLast(new SimpleChannelInboundHandler<HttpObject>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
                                    System.out.println(httpObject);
                                }
                            });
                }
            });
    final Channel channel = bootstrap.connect().sync().channel();

    final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/services", Unpooled.EMPTY_BUFFER);
    request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
    channel.writeAndFlush(request);
    channel.closeFuture().sync();
    System.out.println("DONE");
}

At the moment I am getting

Caused by: java.nio.channels.UnsupportedAddressTypeException: null

Is there an example on how to do HTTP connections to UDS using Netty? So far I only found raw UDS and TCP HTTP but not combined.

Upvotes: 2

Views: 3331

Answers (1)

Archimedes Trajano
Archimedes Trajano

Reputation: 41580

Here's a working implementation.

        io.netty.bootstrap.Bootstrap bootstrap = new io.netty.bootstrap.Bootstrap();
        final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup();
        try {
            bootstrap
                    .group(epollEventLoopGroup)
                    .channel(EpollDomainSocketChannel.class)
                    .handler(new ChannelInitializer<UnixChannel>() {
                        @Override
                        public void initChannel(UnixChannel ch) throws Exception {
                            ch
                                    .pipeline()
                                    .addLast(new HttpClientCodec())
                                    .addLast(new HttpContentDecompressor())
                                    .addLast(new SimpleChannelInboundHandler<HttpObject>() {
                                        private StringBuilder messageBuilder = new StringBuilder();
                                        @Override
                                        public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
                                            if (msg instanceof HttpContent) {
                                                HttpContent content = (HttpContent) msg;
                                                messageBuilder.append(content.content().toString(StandardCharsets.UTF_8));
                                                if (msg instanceof LastHttpContent) {
                                                    System.out.println(messageBuilder);
                                                }
                                            } else {
                                                System.out.println(msg.getClass());
                                            }
                                        }
                                    });
                        }
                    });
            final Channel channel = bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel();

            final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/services", Unpooled.EMPTY_BUFFER);
            request.headers().set(HttpHeaderNames.HOST, "daemon");
            channel.writeAndFlush(request);
            channel.closeFuture().sync();
        } finally {
            epollEventLoopGroup.shutdownGracefully();
        }

Few things to note:

  • Use the EpollEventLoopGroup and EpollDomainSocketChannel with a ChannelInitializer<UnixChannel>.
  • HTTP requires new HttpCodec() in the pipeline to use the Netty HTTP objects.
  • The data may be chunked so you need to assemble it and wait for the LastHttpContent object

https://github.com/trajano/netty-docker-daemon-socket

Upvotes: 2

Related Questions