Skip to content

bug: NettyApiServer.start() blocks indefinitely on bind without timeout #260

@sfloess

Description

@sfloess

Bug Description

The start() method uses .sync() on the bind operation, which blocks indefinitely if the bind fails or hangs. This can cause the application startup to freeze.

Location

jplatform-rest-api-netty/src/main/java/org/flossware/jplatform/rest/netty/NettyApiServer.java:124

Problematic Code

ChannelFuture future = bootstrap.bind(config.getHost(), config.getPort()).sync();
serverChannel = future.channel();
running = true;

Impact

  • Indefinite hang: If bind operation stalls, application startup freezes
  • No timeout: No way to detect and recover from hang
  • Thread blocked: Startup thread blocked indefinitely
  • No feedback: User doesn't know why startup is stuck
  • Resource leak: EventLoopGroups created but server not running

Example

// Port already in use or firewall blocking
NettyApiServer server = new NettyApiServer(config);
server.start();
// bind() operation queued but never completes
// sync() blocks forever
// Application appears to hang during startup
// No exception, no timeout, no log message
// EventLoopGroups consuming resources

Proposed Fix

@Override
public void start() throws ServerStartupException {
    if (running) {
        return;
    }

    try {
        bossGroup = new NioEventLoopGroup(config.getBossThreads());
        workerGroup = config.getWorkerThreads() > 0
            ? new NioEventLoopGroup(config.getWorkerThreads())
            : new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new HttpServerCodec());
                    pipeline.addLast(new HttpObjectAggregator(config.getMaxContentLength()));
                    pipeline.addLast(new HttpRequestHandler(routes));
                }
            })
            .option(ChannelOption.SO_BACKLOG, config.getBacklog())
            .childOption(ChannelOption.SO_KEEPALIVE, config.isKeepAlive());

        // Bind with timeout
        ChannelFuture bindFuture = bootstrap.bind(config.getHost(), config.getPort());
        
        // Wait for bind to complete with timeout
        if (!bindFuture.await(30, TimeUnit.SECONDS)) {
            // Timeout waiting for bind
            throw new ServerStartupException(
                "Timeout waiting for server to bind to " + config.getHost() + ":" + config.getPort(),
                config.getPort(),
                null
            );
        }
        
        // Check if bind was successful
        if (!bindFuture.isSuccess()) {
            throw new ServerStartupException(
                "Failed to bind server to " + config.getHost() + ":" + config.getPort(),
                config.getPort(),
                bindFuture.cause()
            );
        }
        
        serverChannel = bindFuture.channel();
        running = true;

        logger.info("Netty API server started on {}:{}", config.getHost(), config.getPort());
        
    } catch (ServerStartupException e) {
        // Cleanup resources on failure
        cleanupResources();
        throw e;
    } catch (Exception e) {
        cleanupResources();
        logger.error("Failed to start Netty API server", e);
        throw new ServerStartupException("Failed to start server", config.getPort(), e);
    }
}

private void cleanupResources() {
    if (workerGroup != null) {
        workerGroup.shutdownGracefully();
    }
    if (bossGroup != null) {
        bossGroup.shutdownGracefully();
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions