void responseReceived(int id, @Nullable ByteBuf buffer) {
    Channel channel = requests.remove(id);
    if (channel == null || !channel.isActive()) {
      if (buffer != null) {
        buffer.release();
      }
      return;
    }

    if (buffer == null) {
      Responses.sendStatus(HttpResponseStatus.BAD_GATEWAY, channel);
      return;
    }

    HttpResponse httpResponse =
        new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buffer);
    try {
      parseHeaders(httpResponse, buffer);
      Responses.addServer(httpResponse);
      if (!HttpHeaderUtil.isContentLengthSet(httpResponse)) {
        HttpHeaderUtil.setContentLength(httpResponse, buffer.readableBytes());
      }
    } catch (Throwable e) {
      buffer.release();
      try {
        LOG.error(e);
      } finally {
        Responses.sendStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR, channel);
      }
      return;
    }
    channel.writeAndFlush(httpResponse);
  }
 private void writeResponse(HttpObject msg, ChannelHandlerContext ctx) {
   FullHttpResponse response =
       new DefaultFullHttpResponse(
           HTTP_1_1,
           msg.decoderResult().isSuccess() ? OK : BAD_REQUEST,
           Unpooled.copiedBuffer(_buf.toString(), CharsetUtil.UTF_8));
   if (HttpHeaderUtil.isKeepAlive(_request)) {
     response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
     ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
   } else {
     ctx.write(response).addListener(ChannelFutureListener.CLOSE);
   }
 }
  @Override
  protected void messageReceived(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof HttpRequest) {
      HttpRequest request = _request = (HttpRequest) msg;
      HttpMethod method = request.method();
      HttpHeaders headers = request.headers();
      QueryStringDecoder query = new QueryStringDecoder(request.uri());

      if (HttpHeaderUtil.is100ContinueExpected(request))
        ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));

      //            if (headers != null) for (Map.Entry<CharSequence, CharSequence> h : headers) {}

      if (method.equals(HttpMethod.GET)) RamdRequest.build(query.path(), query.parameters());
    }

    if (msg instanceof HttpContent) {
      ByteBuf bb = ((HttpContent) msg).content();
      if (bb.isReadable()) {}
      _buf.append("Done.");
      writeResponse((HttpContent) msg, ctx);
    }
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof HttpRequest) {
      HttpRequest request = this.request = (HttpRequest) msg;
      URI uri = new URI(request.uri());
      if (!uri.getPath().startsWith("/form")) {
        // Write Menu
        writeMenu(ctx);
        return;
      }
      responseContent.setLength(0);
      responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
      responseContent.append("===================================\r\n");

      responseContent.append("VERSION: " + request.protocolVersion().text() + "\r\n");

      responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n");
      responseContent.append("\r\n\r\n");

      // new getMethod
      for (Entry<CharSequence, CharSequence> entry : request.headers()) {
        responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      // new getMethod
      Set<Cookie> cookies;
      String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE);
      if (value == null) {
        cookies = Collections.emptySet();
      } else {
        cookies = ServerCookieDecoder.STRICT.decode(value);
      }
      for (Cookie cookie : cookies) {
        responseContent.append("COOKIE: " + cookie + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
      Map<String, List<String>> uriAttributes = decoderQuery.parameters();
      for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
        for (String attrVal : attr.getValue()) {
          responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n");
        }
      }
      responseContent.append("\r\n\r\n");

      // if GET Method: should not try to create a HttpPostRequestDecoder
      if (request.method().equals(HttpMethod.GET)) {
        // GET Method: should not try to create a HttpPostRequestDecoder
        // So stop here
        responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
        // Not now: LastHttpContent will be sent writeResponse(ctx.channel());
        return;
      }
      try {
        decoder = new HttpPostRequestDecoder(factory, request);
      } catch (ErrorDataDecoderException e1) {
        e1.printStackTrace();
        responseContent.append(e1.getMessage());
        writeResponse(ctx.channel());
        ctx.channel().close();
        return;
      }

      readingChunks = HttpHeaderUtil.isTransferEncodingChunked(request);
      responseContent.append("Is Chunked: " + readingChunks + "\r\n");
      responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n");
      if (readingChunks) {
        // Chunk version
        responseContent.append("Chunks: ");
        readingChunks = true;
      }
    }

    // check if the decoder was constructed before
    // if not it handles the form get
    if (decoder != null) {
      if (msg instanceof HttpContent) {
        // New chunk is received
        HttpContent chunk = (HttpContent) msg;
        try {
          decoder.offer(chunk);
        } catch (ErrorDataDecoderException e1) {
          e1.printStackTrace();
          responseContent.append(e1.getMessage());
          writeResponse(ctx.channel());
          ctx.channel().close();
          return;
        }
        responseContent.append('o');
        // example of reading chunk by chunk (minimize memory usage due to
        // Factory)
        readHttpDataChunkByChunk();
        // example of reading only if at the end
        if (chunk instanceof LastHttpContent) {
          writeResponse(ctx.channel());
          readingChunks = false;

          reset();
        }
      }
    } else {
      writeResponse(ctx.channel());
    }
  }