private void decode(@NotNull ChannelHandlerContext context, @NotNull ByteBuf buffer) {
    ChannelPipeline pipeline = context.pipeline();
    if (detectSsl && SslHandler.isEncrypted(buffer)) {
      SSLEngine engine = SSL_SERVER_CONTEXT.get().newEngine(context.alloc());
      engine.setUseClientMode(false);
      pipeline.addLast(new SslHandler(engine), new ChunkedWriteHandler(),
                       new PortUnificationServerHandler(delegatingHttpRequestHandler, false, detectGzip));
    }
    else {
      int magic1 = buffer.getUnsignedByte(buffer.readerIndex());
      int magic2 = buffer.getUnsignedByte(buffer.readerIndex() + 1);
      if (detectGzip && magic1 == 31 && magic2 == 139) {
        pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP),
                         new PortUnificationServerHandler(delegatingHttpRequestHandler, detectSsl, false));
      }
      else if (isHttp(magic1, magic2)) {
        NettyUtil.addHttpServerCodec(pipeline);
        pipeline.addLast("delegatingHttpHandler", delegatingHttpRequestHandler);
        Logger logger = Logger.getInstance(BuiltInServer.class);
        if (logger.isDebugEnabled()) {
          pipeline.addLast(new ChannelOutboundHandlerAdapter() {
            @Override
            public void write(ChannelHandlerContext context, Object message, ChannelPromise promise) throws Exception {
              if (message instanceof HttpResponse response) {
                logger.debug("OUT HTTP: " + response);
              }
              super.write(context, message, promise);
            }
          });
        }
      }
      else if (magic1 == 'C' && magic2 == 'H') {
        buffer.skipBytes(2);
        pipeline.addLast(new CustomHandlerDelegator());
      }
      else {
        Logger.getInstance(BuiltInServer.class).warn("unknown request, first two bytes " + magic1 + " " + magic2);
        context.close();
      }
    }

    // must be after new channels handlers addition (netty bug?)
    pipeline.remove(this);
    // Buffer will be automatically released after messageReceived, but we pass it to next handler, and next handler will also release, so, we must retain.
    // We can introduce Decoder.isAutoRelease, but in this case, if error will be thrown while we are executing, buffer will not be released.
    // So, it is robust solution just always release (Decoder does) and just retain (we - client) if autorelease behavior is not suitable.
    buffer.retain();
    // we must fire channel read - new added handler must read buffer
    context.fireChannelRead(buffer);
  }
private void decode(@NotNull ChannelHandlerContext context, @NotNull ByteBuf buffer)