Netty series: interaction between self built client and HTTP server

Time:2021-12-5

brief introduction

In the last article, we built an HTTP server supporting Chinese, which can be accessed from the browser and get the corresponding results. Although the browser is very common in daily applications, sometimes we may call the service of HTTP server from the self built client.

Today, I’d like to introduce how to build an HTTP client to interact with the HTTP server.

Build request using client

In the last article, we used the browser to access the server and got the response results. So how to build the request on the client?

The HTTP request in netty can be divided into two parts: httprequest and httpcontent. Httprequest only contains the version number of the request and the information of the message header, and httpcontent only contains the real request content information.

However, if you want to build a request, you need to include both httprequest and httpcontent information. Netty provides a request class called defaultfullhttprequest, which contains two parts of information and can be used directly.

Using the constructor of defaultfullhttprequest, we can construct an httprequest request as follows:

HttpRequest request = new DefaultFullHttpRequest(
                    HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath(), Unpooled.EMPTY_BUFFER);

In the above code, the protocol we use is HTTP 1.1, the method is get, and the requested content is an empty buffer.

After building the basic request information, we may need to add some additional information in the header, such as connection, accept encoding and cookie information.

For example, set the following information:

request.headers().set(HttpHeaderNames.HOST, host);
            request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
            request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);

Also set cookies:

request.headers().set(
                    HttpHeaderNames.COOKIE,
                    ClientCookieEncoder.STRICT.encode(
                            new DefaultCookie("name", "flydean"),
                            new DefaultCookie("site", "www.flydean.com")));

We use the clientcookie encoder.encode method to set cookies. Clientcookie encoder has two encoder modes, one is strict and the other is lax.

In strict mode, the name and value of cookies will be checked and sorted.

Corresponding to the encoder is the clientcookie decoder, which is used to parse cookies.

After setting all our requests, we can write them to the channel.

accept-encoding

When the client writes a request, we add accept encoding to the request header and set its value to gzip, indicating that the encoding method received by the client is gzip.

If the server sends the encoded content of gzip, how can the client parse it? We need to decode the encoding format of gzip.

Netty provides the httpcontentdecompressor class, which can decode the encoding in gzip or deflate format. After decoding, the “content encoding” and “content length” in the response header will be modified at the same time.

We just need to add it to pipline.

The corresponding class is httpcontentcompressor, which is used to gzip or deflate httpmessage and httpcontent.

Therefore, httpcontentdecompressor should be added to the client’s pipline, and httpcontentcompressor should be added to the server’s pipline.

Server parses HTTP requests

The server needs a handler to parse the messages requested by the client. For the server, what should be paid attention to when parsing the client’s request?

The first thing to pay attention to is the problem of the client 100 continue request.

A unique function in HTTP is called 100 (continue) status, which means that when the client is not sure whether the server side will receive the request, it can first send a request header and add a “100 continue” field to the header, but it will not send the request body yet. Send the request body until the response from the server is received.

If the server receives the 100continue request, it can directly return the confirmation:

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

    private static void send100Continue(ChannelHandlerContext ctx) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER);
        ctx.write(response);
    }

If the request is not 100, the server side can prepare the content to be returned:

Here, a StringBuilder is used to store the content to be returned:

StringBuilder buf = new StringBuilder();

Why use stringbuf? Because it is possible that the server side cannot fully accept the client’s request at one time, all the contents to be returned are put into the buffer and returned together after all are accepted.

We can add welcome information to the server and various information obtained from the client:

buf.setLength(0);
            BUF. Append ("welcome to www.flybean. Com \ R \ n");
            buf.append("===================================\r\n");

            buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
            buf.append("HOSTNAME: ").append(request.headers().get(HttpHeaderNames.HOST, "unknown")).append("\r\n");
            buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");

You can also add request header information to the buffer:

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

You can add request parameter information to the buffer:

            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");
            }

Note the processing method when httpcontent is read. If the message read is httpcontent, add the content of content to the buffer:

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);
            }

So how to judge whether a request is over? Netty provides a class called lasthttpcontent, which represents the last part of the message. After receiving this part of the message, we can judge that an HTTP request has been completed and can formally return the message:

if (msg instanceof LastHttpContent) {
                log.info("LastHttpContent:{}",msg);
                buf.append("END OF CONTENT\r\n");

To write back to the channel, you also need to build a defaultfullhttpresponse, which is built using buffer:

FullHttpResponse response = new DefaultFullHttpResponse(
                HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
                Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));

Then add some necessary header information and call ctx.write to write back.

summary

This paper introduces how to build HTTP requests in the client, and explains in detail the parsing process of HTTP requests by the HTTP server.

Examples of this article can be referred to:learn-netty4

This article has been included inhttp://www.flydean.com/19-netty-http-client-request-2/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don’t know are waiting for you to find!

Welcome to my official account: “those things in procedure”, understand technology, know you better!

Recommended Today

The real problem of Alibaba IOS algorithm can’t hang up this time

More and more IOS developers continue to enter the peak of job hopping in 2020 Three main trends of interview in 2020: IOS bottom layer, algorithm, data structure and audio and video development Occupied the main battlefield. Data structure and algorithm interview, especially figure, has become the main reason for the failure of most first-line […]