Example usage for io.netty.handler.codec.http QueryStringDecoder QueryStringDecoder

List of usage examples for io.netty.handler.codec.http QueryStringDecoder QueryStringDecoder

Introduction

In this page you can find the example usage for io.netty.handler.codec.http QueryStringDecoder QueryStringDecoder.

Prototype

public QueryStringDecoder(URI uri) 

Source Link

Document

Creates a new decoder that decodes the specified URI.

Usage

From source file:HttpUploadServerHandler.java

License:Apache License

@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof HttpRequest) {
        HttpRequest request = this.request = (HttpRequest) msg;
        URI uri = new URI(request.getUri());
        if (!uri.getPath().startsWith("/form")) {
            // Write Menu
            writeMenu(ctx);/*from  w w w .  j  a v  a  2s  . c o  m*/
            return;
        }
        responseContent.setLength(0);
        responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
        responseContent.append("===================================\r\n");

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

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

        // new getMethod
        List<Entry<String, String>> headers = request.headers().entries();
        for (Entry<String, String> entry : 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().get(COOKIE);
        if (value == null) {
            cookies = Collections.emptySet();
        } else {
            cookies = CookieDecoder.decode(value);
        }
        for (Cookie cookie : cookies) {
            responseContent.append("COOKIE: " + cookie.toString() + "\r\n");
        }
        responseContent.append("\r\n\r\n");

        QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri());
        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
        try {
            decoder = new HttpPostRequestDecoder(factory, request);
        } catch (ErrorDataDecoderException e1) {
            e1.printStackTrace();
            responseContent.append(e1.getMessage());
            writeResponse(ctx.channel());
            ctx.channel().close();
            return;
        } catch (IncompatibleDataDecoderException e1) {
            // GET Method: should not try to create a HttpPostRequestDecoder
            // So OK but stop here
            responseContent.append(e1.getMessage());
            responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
            writeResponse(ctx.channel());
            return;
        }

        readingChunks = HttpHeaders.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) {
                readHttpDataAllReceive(ctx.channel());
                writeResponse(ctx.channel());
                readingChunks = false;

                reset();
            }
        }
    }
}

From source file:baseFrame.netty.atest.HttpHelloWorldServerHandler.java

License:Apache License

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof HttpRequest) {
        HttpRequest request = (HttpRequest) msg;
        if (HttpHeaders.is100ContinueExpected(request)) {
            ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
        }/*  w  w w. j  av  a 2 s .  c o m*/
        boolean keepAlive = HttpHeaders.isKeepAlive(request);

        // Encode the cookie.
        String cookieString = request.headers().get(Names.COOKIE);
        if (cookieString != null) {
            Set<Cookie> cookies = CookieDecoder.decode(cookieString);
            if (!cookies.isEmpty()) {
                // Reset the cookies if necessary.
                for (Cookie cookie : cookies) {
                    response.headers().add(Names.SET_COOKIE, ServerCookieEncoder.encode(cookie));
                }
            }
        } else {
            // Browser sent no cookie.  Add some.
            /*  response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
              response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));*/
        }

        if (HttpHeaders.is100ContinueExpected(request)) {
            send100Continue(ctx);
        }

        responseContent.append("VERSION: ").append(request.getProtocolVersion()).append("\r\n");
        //responseContent.append("HOSTNAME: ").append(request.headers().).append("\r\n");
        responseContent.append("REQUEST_URI: ").append(request.getUri()).append("\r\n\r\n");

        HttpHeaders headers = request.headers();
        if (!headers.isEmpty()) {
            for (Entry<String, String> h : headers) {
                CharSequence key = h.getKey();
                CharSequence value = h.getValue();
                responseContent.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
            }
            responseContent.append("\r\n");
        }

        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
        Map<String, List<String>> params = queryStringDecoder.parameters();
        if (!params.isEmpty()) {
            for (Entry<String, List<String>> p : params.entrySet()) {
                String key = p.getKey();
                List<String> vals = p.getValue();
                for (String val : vals) {
                    responseContent.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n");
                }
            }
            responseContent.append("\r\n");
        }

        response = setResponse(response, responseContent);

        if (!keepAlive) {
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            response.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
            ctx.write(response);
        }
    }

    if (msg instanceof HttpContent) {
        HttpContent content = (HttpContent) msg;

        if (msg instanceof LastHttpContent) {

        }
    }
}

From source file:cc.blynk.core.http.handlers.OTAHandler.java

License:Apache License

@Override
public boolean accept(ChannelHandlerContext ctx, HttpRequest req) {
    if (req.method() == HttpMethod.POST && req.uri().startsWith(handlerUri)) {
        try {/* www  .  j  av  a  2  s  . co  m*/
            User superAdmin = AuthHeadersBaseHttpHandler.validateAuth(userDao, req);
            if (superAdmin != null) {
                ctx.channel().attr(AuthHeadersBaseHttpHandler.USER).set(superAdmin);
                queryStringDecoder = new QueryStringDecoder(req.uri());
                return true;
            }
        } catch (IllegalAccessException e) {
            //return 403 and stop processing.
            ctx.writeAndFlush(Response.forbidden(e.getMessage()));
            return true;
        }
    }
    return false;
}

From source file:cc.io.lessons.server.HttpUploadServerHandler.java

License:Apache License

@Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

    if (msg instanceof HttpRequest) {
        System.out.println(msg.toString());
    } else {//from  www  . j  a v a 2 s .  c o m
        System.out.println(ByteBufUtil.hexDump(((HttpContent) msg).content()));
        //System.out.println(((HttpContent) msg).content().toString(Charset.defaultCharset()));
    }

    if (msg instanceof HttpRequest) {
        HttpRequest request = this.request = (HttpRequest) msg;
        URI uri = new URI(request.getUri());
        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.getProtocolVersion().text() + "\r\n");

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

        // new getMethod
        List<Entry<String, String>> headers = request.headers().entries();
        for (Entry<String, String> entry : 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().get(COOKIE);
        if (value == null) {
            cookies = Collections.emptySet();
        } else {
            cookies = CookieDecoder.decode(value);
        }
        for (Cookie cookie : cookies) {
            responseContent.append("COOKIE: " + cookie.toString() + "\r\n");
        }
        responseContent.append("\r\n\r\n");

        QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri());
        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
        try {
            decoder = new HttpPostRequestDecoder(factory, request);
        } catch (ErrorDataDecoderException e1) {
            e1.printStackTrace();
            responseContent.append(e1.getMessage());
            writeResponse(ctx.channel());
            ctx.channel().close();
            return;
        } catch (IncompatibleDataDecoderException e1) {
            // GET Method: should not try to create a HttpPostRequestDecoder
            // So OK but stop here
            responseContent.append(e1.getMessage());
            responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
            writeResponse(ctx.channel());
            return;
        }

        readingChunks = HttpHeaders.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();
            }
        }
    }
}

From source file:ch07.handlers.HttpSnoopServerHandler.java

License:Apache License

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof HttpRequest) {
        HttpRequest request = this.request = (HttpRequest) msg;

        if (HttpHeaders.is100ContinueExpected(request)) {
            send100Continue(ctx);/*from  www .ja  v  a2  s  . c o m*/
        }

        buf.setLength(0);
        buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
        buf.append("===================================\r\n");

        buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
        buf.append("HOSTNAME: ").append(HttpHeaders.getHost(request, "unknown")).append("\r\n");
        buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");

        HttpHeaders headers = request.headers();
        if (!headers.isEmpty()) {
            for (Map.Entry<String, String> h : headers) {
                String key = h.getKey();
                String value = h.getValue();
                buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
            }
            buf.append("\r\n");
        }

        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
        Map<String, List<String>> params = queryStringDecoder.parameters();
        if (!params.isEmpty()) {
            for (Entry<String, List<String>> p : params.entrySet()) {
                String key = p.getKey();
                List<String> vals = p.getValue();
                for (String val : vals) {
                    buf.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n");
                }
            }
            buf.append("\r\n");
        }

        appendDecoderResult(buf, request);
    }

    if (msg instanceof HttpContent) {
        HttpContent httpContent = (HttpContent) msg;

        ByteBuf content = httpContent.content();
        if (content.isReadable()) {
            buf.append("CONTENT: ");
            buf.append(content.toString(CharsetUtil.UTF_8));
            buf.append("\r\n");
            appendDecoderResult(buf, request);
        }

        if (msg instanceof LastHttpContent) {
            buf.append("END OF CONTENT\r\n");

            LastHttpContent trailer = (LastHttpContent) msg;
            if (!trailer.trailingHeaders().isEmpty()) {
                buf.append("\r\n");
                for (String name : trailer.trailingHeaders().names()) {
                    for (String value : trailer.trailingHeaders().getAll(name)) {
                        buf.append("TRAILING HEADER: ");
                        buf.append(name).append(" = ").append(value).append("\r\n");
                    }
                }
                buf.append("\r\n");
            }

            if (!writeResponse(trailer, ctx)) {
                // If keep-alive is off, close the connection once the content is fully written.
                ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            }
        }
    }
}

From source file:co.paralleluniverse.comsat.webactors.netty.HttpRequestWrapper.java

License:Open Source License

@Override
public final Multimap<String, String> getParameters() {
    QueryStringDecoder queryStringDecoder;
    if (params == null) {
        queryStringDecoder = new QueryStringDecoder(req.getUri());
        final ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.builder();
        final Map<String, List<String>> parameters = queryStringDecoder.parameters();
        for (final String k : parameters.keySet())
            builder.putAll(k, parameters.get(k));
        params = builder.build();/*from ww  w  .j a v a  2 s .  com*/
    }
    return params;
}

From source file:com.addthis.hydra.query.web.HttpQueryHandler.java

License:Apache License

protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    if (!request.getDecoderResult().isSuccess()) {
        sendError(ctx, HttpResponseStatus.BAD_REQUEST);
        return;// www .j  a v  a  2  s  .  c  o m
    }
    QueryStringDecoder urlDecoder = new QueryStringDecoder(request.getUri());
    String target = urlDecoder.path();
    if (request.getMethod() == HttpMethod.POST) {
        log.trace("POST Method handling triggered for {}", request);
        String postBody = request.content().toString(CharsetUtil.UTF_8);
        log.trace("POST body {}", postBody);
        urlDecoder = new QueryStringDecoder(postBody, false);
    }
    log.trace("target uri {}", target);
    KVPairs kv = new KVPairs();
    /**
     * The "/query/google/submit" endpoint needs to unpack the
     * "state" parameter into KV pairs.
     */
    if (target.equals("/query/google/submit")) {
        String state = urlDecoder.parameters().get("state").get(0);
        QueryStringDecoder newDecoder = new QueryStringDecoder(state, false);
        decodeParameters(newDecoder, kv);
        if (urlDecoder.parameters().containsKey("code")) {
            kv.add(GoogleDriveAuthentication.authtoken, urlDecoder.parameters().get("code").get(0));
        }
        if (urlDecoder.parameters().containsKey("error")) {
            kv.add(GoogleDriveAuthentication.autherror, urlDecoder.parameters().get("error").get(0));
        }
    } else {
        decodeParameters(urlDecoder, kv);
    }
    log.trace("kv pairs {}", kv);
    switch (target) {
    case "/":
        sendRedirect(ctx, "/query/index.html");
        break;
    case "/q/":
        sendRedirect(ctx, "/query/call?" + kv.toString());
        break;
    case "/query/call":
    case "/query/call/":
        QueryServer.rawQueryCalls.inc();
        queryQueue.queueQuery(meshQueryMaster, kv, request, ctx);
        break;
    case "/query/google/authorization":
        GoogleDriveAuthentication.gdriveAuthorization(kv, ctx);
        break;
    case "/query/google/submit":
        boolean success = GoogleDriveAuthentication.gdriveAccessToken(kv, ctx);
        if (success) {
            queryQueue.queueQuery(meshQueryMaster, kv, request, ctx);
        }
        break;
    default:
        fastHandle(ctx, request, target, kv);
        break;
    }
}

From source file:com.addthis.hydra.query.web.HttpStaticFileHandler.java

License:Apache License

@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    if (!request.getDecoderResult().isSuccess()) {
        sendError(ctx, BAD_REQUEST);/*from w w  w. j  av a  2  s .c o m*/
        return;
    }
    // since we are using send file, we must remove the compression unit or it will donk out
    ChannelHandler compressor = ctx.pipeline().get("compressor");
    if (compressor != null) {
        ctx.pipeline().remove("compressor");
    }

    if (request.getMethod() != GET) {
        sendError(ctx, METHOD_NOT_ALLOWED);
        return;
    }

    QueryStringDecoder urlDecoder = new QueryStringDecoder(request.getUri());
    String target = urlDecoder.path();

    final String path = sanitizeUri(target);
    if (path == null) {
        sendError(ctx, FORBIDDEN);
        return;
    }

    Path file = Paths.get(webDir + path);
    log.trace("trying to serve static file {}", file);
    if (Files.isHidden(file) || Files.notExists(file)) {
        sendError(ctx, NOT_FOUND);
        return;
    }

    if (!Files.isRegularFile(file)) {
        sendError(ctx, FORBIDDEN);
        return;
    }

    log.trace("cache validation occuring for {}", file);
    // Cache Validation
    String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
    if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat(HttpUtils.HTTP_DATE_FORMAT, Locale.US);
        Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

        // Only compare up to the second because the datetime format we send to the client
        // does not have milliseconds
        long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
        long fileLastModifiedSeconds = Files.getLastModifiedTime(file).toMillis() / 1000;
        if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
            sendNotModified(ctx);
            return;
        }
    }

    log.trace("sending {}", file);

    FileChannel fileChannel;
    try {
        fileChannel = FileChannel.open(file, StandardOpenOption.READ);
    } catch (IOException fnfe) {
        sendError(ctx, NOT_FOUND);
        return;
    }
    long fileLength = fileChannel.size();

    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    setContentLength(response, fileLength);
    setContentTypeHeader(response, file);
    try {
        setDateAndCacheHeaders(response, file);
    } catch (IOException ioex) {
        fileChannel.close();
        sendError(ctx, NOT_FOUND);
        return;
    }
    if (isKeepAlive(request)) {
        response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.
    ctx.write(new DefaultFileRegion(fileChannel, 0, fileLength));

    // Write the end marker
    ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

    // Decide whether to close the connection or not.
    if (!isKeepAlive(request)) {
        // Close the connection when the whole content is written out.
        lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    } else {
        ctx.pipeline().remove(this);
        if (compressor != null) {
            ctx.pipeline().addBefore("query", "compressor", compressor);
        }
    }
}

From source file:com.alibaba.dubbo.qos.command.decoder.HttpCommandDecoder.java

License:Apache License

public static CommandContext decode(HttpRequest request) {
    CommandContext commandContext = null;
    if (request != null) {
        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
        String path = queryStringDecoder.path();
        String[] array = path.split("/");
        if (array.length == 2) {
            String name = array[1];

            // process GET request and POST request separately. Check url for GET, and check body for POST
            if (request.getMethod() == HttpMethod.GET) {
                if (queryStringDecoder.parameters().isEmpty()) {
                    commandContext = CommandContextFactory.newInstance(name);
                    commandContext.setHttp(true);
                } else {
                    List<String> valueList = new ArrayList<String>();
                    for (List<String> values : queryStringDecoder.parameters().values()) {
                        valueList.addAll(values);
                    }/*ww  w  .  ja va  2s .c  om*/
                    commandContext = CommandContextFactory.newInstance(name, valueList.toArray(new String[] {}),
                            true);
                }
            } else if (request.getMethod() == HttpMethod.POST) {
                HttpPostRequestDecoder httpPostRequestDecoder = new HttpPostRequestDecoder(request);
                List<String> valueList = new ArrayList<String>();
                for (InterfaceHttpData interfaceHttpData : httpPostRequestDecoder.getBodyHttpDatas()) {
                    if (interfaceHttpData.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                        Attribute attribute = (Attribute) interfaceHttpData;
                        try {
                            valueList.add(attribute.getValue());
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                }
                if (valueList.isEmpty()) {
                    commandContext = CommandContextFactory.newInstance(name);
                    commandContext.setHttp(true);
                } else {
                    commandContext = CommandContextFactory.newInstance(name, valueList.toArray(new String[] {}),
                            true);
                }
            }
        }
    }

    return commandContext;
}

From source file:com.bala.learning.learning.netty.HttpServerHandler.java

License:Apache License

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof HttpRequest) {
        HttpRequest request = this.request = (HttpRequest) msg;

        if (HttpHeaders.is100ContinueExpected(request)) {
            send100Continue(ctx);//from   w w  w  .  ja va2  s.  c  o  m
        }

        buf.setLength(0);
        buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
        buf.append("===================================\r\n");

        buf.append("VERSION: ").append(request.getProtocolVersion()).append("\r\n");
        buf.append("HOSTNAME: ").append(HttpHeaders.getHost(request, "unknown")).append("\r\n");
        buf.append("REQUEST_URI: ").append(request.getUri()).append("\r\n\r\n");

        HttpHeaders headers = request.headers();
        if (!headers.isEmpty()) {
            for (Map.Entry<String, String> h : headers) {
                String key = h.getKey();
                String value = h.getValue();
                buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
            }
            buf.append("\r\n");
        }

        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
        Map<String, List<String>> params = queryStringDecoder.parameters();
        if (!params.isEmpty()) {
            for (Entry<String, List<String>> p : params.entrySet()) {
                String key = p.getKey();
                List<String> vals = p.getValue();
                for (String val : vals) {
                    buf.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n");
                }
            }
            buf.append("\r\n");
        }

        appendDecoderResult(buf, request);
    }

    if (msg instanceof HttpContent) {
        HttpContent httpContent = (HttpContent) msg;

        ByteBuf content = httpContent.content();
        if (content.isReadable()) {
            buf.append("CONTENT: ");
            buf.append(content.toString(CharsetUtil.UTF_8));
            buf.append("\r\n");
            appendDecoderResult(buf, request);
        }

        if (msg instanceof LastHttpContent) {
            buf.append("END OF CONTENT\r\n");

            LastHttpContent trailer = (LastHttpContent) msg;
            if (!trailer.trailingHeaders().isEmpty()) {
                buf.append("\r\n");
                for (String name : trailer.trailingHeaders().names()) {
                    for (String value : trailer.trailingHeaders().getAll(name)) {
                        buf.append("TRAILING HEADER: ");
                        buf.append(name).append(" = ").append(value).append("\r\n");
                    }
                }
                buf.append("\r\n");
            }

            if (!writeResponse(trailer, ctx)) {
                // If keep-alive is off, close the connection once the content is fully written.
                ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            }
        }
    }
}