io.vertx.core.http.impl.Http2ServerRequestImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.http.impl.Http2ServerRequestImpl.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 *  ------------------------------------------------------
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *      The Eclipse Public License is available at
 *      http://www.eclipse.org/legal/epl-v10.html
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.core.http.impl;

import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2Stream;
import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.CaseInsensitiveHeaders;
import io.vertx.core.http.HttpConnection;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerFileUpload;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.http.StreamResetException;
import io.vertx.core.http.HttpFrame;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.spi.metrics.HttpServerMetrics;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import java.net.URISyntaxException;
import java.nio.channels.ClosedChannelException;

/**
 * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
 */
public class Http2ServerRequestImpl extends VertxHttp2Stream<Http2ServerConnection> implements HttpServerRequest {

    private static final Logger log = LoggerFactory.getLogger(HttpServerRequestImpl.class);

    private final String serverOrigin;
    private final Http2ServerResponseImpl response;
    private final Http2Headers headers;
    private MultiMap headersMap;
    private MultiMap params;
    private HttpMethod method;
    private String rawMethod;
    private String absoluteURI;
    private String uri;
    private String path;
    private String query;
    private MultiMap attributes;

    private Handler<Buffer> dataHandler;
    private Handler<Void> endHandler;
    private boolean ended;
    private long bytesRead;

    private Handler<HttpServerFileUpload> uploadHandler;
    private HttpPostRequestDecoder postRequestDecoder;

    private Handler<Throwable> exceptionHandler;
    private Handler<HttpFrame> customFrameHandler;

    private NetSocket netSocket;

    public Http2ServerRequestImpl(Http2ServerConnection conn, Http2Stream stream, HttpServerMetrics metrics,
            String serverOrigin, Http2Headers headers, String contentEncoding, boolean writable) {
        super(conn, stream, writable);

        this.serverOrigin = serverOrigin;
        this.headers = headers;

        String host = host();
        if (host == null) {
            int idx = serverOrigin.indexOf("://");
            host = serverOrigin.substring(idx + 3);
        }
        Object metric = metrics.isEnabled() ? metrics.requestBegin(conn.metric(), this) : null;
        this.response = new Http2ServerResponseImpl(conn, this, metric, false, contentEncoding, host);
    }

    @Override
    void handleInterestedOpsChanged() {
        response.writabilityChanged();
    }

    @Override
    void handleException(Throwable cause) {
        if (exceptionHandler != null) {
            exceptionHandler.handle(cause);
            response.handleError(cause);
        }
    }

    @Override
    void handleClose() {
        if (!ended) {
            ended = true;
            if (exceptionHandler != null) {
                exceptionHandler.handle(new ClosedChannelException());
            }
        }
        response.handleClose();
    }

    @Override
    void handleCustomFrame(int type, int flags, Buffer buff) {
        if (customFrameHandler != null) {
            customFrameHandler.handle(new HttpFrameImpl(type, flags, buff));
        }
    }

    void handleData(Buffer data) {
        bytesRead += data.length();
        if (postRequestDecoder != null) {
            try {
                postRequestDecoder.offer(new DefaultHttpContent(data.getByteBuf()));
            } catch (Exception e) {
                handleException(e);
            }
        }
        if (dataHandler != null) {
            dataHandler.handle(data);
        }
    }

    void handleEnd(MultiMap trailers) {
        ended = true;
        conn.reportBytesRead(bytesRead);
        if (postRequestDecoder != null) {
            try {
                postRequestDecoder.offer(LastHttpContent.EMPTY_LAST_CONTENT);
                while (postRequestDecoder.hasNext()) {
                    InterfaceHttpData data = postRequestDecoder.next();
                    if (data instanceof Attribute) {
                        Attribute attr = (Attribute) data;
                        try {
                            formAttributes().add(attr.getName(), attr.getValue());
                        } catch (Exception e) {
                            // Will never happen, anyway handle it somehow just in case
                            handleException(e);
                        }
                    }
                }
            } catch (HttpPostRequestDecoder.EndOfDataDecoderException e) {
                // ignore this as it is expected
            } catch (Exception e) {
                handleException(e);
            } finally {
                postRequestDecoder.destroy();
            }
        }
        if (endHandler != null) {
            endHandler.handle(null);
        }
    }

    @Override
    void handleReset(long errorCode) {
        ended = true;
        if (exceptionHandler != null) {
            exceptionHandler.handle(new StreamResetException(errorCode));
        }
        response.callReset(errorCode);
        if (endHandler != null) {
            endHandler.handle(null);
        }
    }

    private void checkEnded() {
        if (ended) {
            throw new IllegalStateException("Request has already been read");
        }
    }

    @Override
    public HttpServerRequest exceptionHandler(Handler<Throwable> handler) {
        synchronized (conn) {
            exceptionHandler = handler;
        }
        return this;
    }

    @Override
    public HttpServerRequest handler(Handler<Buffer> handler) {
        synchronized (conn) {
            checkEnded();
            dataHandler = handler;
        }
        return this;
    }

    @Override
    public HttpServerRequest pause() {
        synchronized (conn) {
            doPause();
        }
        return this;
    }

    @Override
    public HttpServerRequest resume() {
        synchronized (conn) {
            doResume();
        }
        return this;
    }

    @Override
    public HttpServerRequest endHandler(Handler<Void> handler) {
        synchronized (conn) {
            checkEnded();
            endHandler = handler;
        }
        return this;
    }

    @Override
    public HttpVersion version() {
        return HttpVersion.HTTP_2;
    }

    @Override
    public HttpMethod method() {
        synchronized (conn) {
            if (method == null) {
                String sMethod = headers.method().toString();
                method = HttpUtils.toVertxMethod(sMethod);
            }
            return method;
        }
    }

    @Override
    public String rawMethod() {
        synchronized (conn) {
            if (rawMethod == null) {
                rawMethod = headers.method().toString();
            }
            return rawMethod;
        }
    }

    @Override
    public boolean isSSL() {
        return conn.isSsl();
    }

    @Override
    public String uri() {
        synchronized (conn) {
            if (uri == null) {
                CharSequence path = headers.path();
                if (path != null) {
                    uri = path.toString();
                }
            }
            return uri;
        }
    }

    @Override
    public String path() {
        synchronized (conn) {
            if (path == null) {
                CharSequence path = headers.path();
                if (path != null) {
                    this.path = HttpUtils.parsePath(path.toString());
                }
            }
            return path;
        }
    }

    @Override
    public String query() {
        synchronized (conn) {
            if (query == null) {
                CharSequence path = headers.path();
                if (path != null) {
                    this.query = HttpUtils.parseQuery(path.toString());
                }
            }
            return query;
        }
    }

    @Override
    public String scheme() {
        CharSequence scheme = headers.scheme();
        return scheme != null ? scheme.toString() : null;
    }

    @Override
    public String host() {
        CharSequence authority = headers.authority();
        return authority != null ? authority.toString() : null;
    }

    @Override
    public Http2ServerResponseImpl response() {
        return response;
    }

    @Override
    public MultiMap headers() {
        synchronized (conn) {
            if (headersMap == null) {
                headersMap = new Http2HeadersAdaptor(headers);
            }
            return headersMap;
        }
    }

    @Override
    public String getHeader(String headerName) {
        return headers().get(headerName);
    }

    @Override
    public String getHeader(CharSequence headerName) {
        return headers().get(headerName);
    }

    @Override
    public MultiMap params() {
        synchronized (conn) {
            if (params == null) {
                params = HttpUtils.params(uri());
            }
            return params;
        }
    }

    @Override
    public String getParam(String paramName) {
        return params().get(paramName);
    }

    @Override
    public SocketAddress remoteAddress() {
        return conn.remoteAddress();
    }

    @Override
    public SocketAddress localAddress() {
        return conn.localAddress();
    }

    @Override
    public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
        return conn.getPeerCertificateChain();
    }

    @Override
    public String absoluteURI() {
        if (method() == HttpMethod.CONNECT) {
            return null;
        }
        synchronized (conn) {
            if (absoluteURI == null) {
                try {
                    absoluteURI = HttpUtils.absoluteURI(serverOrigin, this);
                } catch (URISyntaxException e) {
                    log.error("Failed to create abs uri", e);
                }
            }
            return absoluteURI;
        }
    }

    @Override
    public NetSocket netSocket() {
        synchronized (conn) {
            checkEnded();
            if (netSocket == null) {
                response.toNetSocket();
                netSocket = conn.toNetSocket(this);
            }
            return netSocket;
        }
    }

    @Override
    public HttpServerRequest setExpectMultipart(boolean expect) {
        synchronized (conn) {
            checkEnded();
            if (expect) {
                if (postRequestDecoder == null) {
                    CharSequence contentType = headers.get(HttpHeaderNames.CONTENT_TYPE);
                    if (contentType != null) {
                        io.netty.handler.codec.http.HttpMethod method = io.netty.handler.codec.http.HttpMethod
                                .valueOf(headers.method().toString());
                        String lowerCaseContentType = contentType.toString().toLowerCase();
                        boolean isURLEncoded = lowerCaseContentType
                                .startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString());
                        if ((lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString())
                                || isURLEncoded)
                                && (method == io.netty.handler.codec.http.HttpMethod.POST
                                        || method == io.netty.handler.codec.http.HttpMethod.PUT
                                        || method == io.netty.handler.codec.http.HttpMethod.PATCH
                                        || method == io.netty.handler.codec.http.HttpMethod.DELETE)) {
                            HttpRequest req = new DefaultHttpRequest(
                                    io.netty.handler.codec.http.HttpVersion.HTTP_1_1, method,
                                    headers.path().toString());
                            req.headers().add(HttpHeaderNames.CONTENT_TYPE, contentType);
                            postRequestDecoder = new HttpPostRequestDecoder(
                                    new NettyFileUploadDataFactory(vertx, this, () -> uploadHandler), req);
                        }
                    }
                }
            } else {
                postRequestDecoder = null;
            }
        }
        return this;
    }

    @Override
    public boolean isExpectMultipart() {
        synchronized (conn) {
            return postRequestDecoder != null;
        }
    }

    @Override
    public HttpServerRequest uploadHandler(@Nullable Handler<HttpServerFileUpload> handler) {
        synchronized (conn) {
            checkEnded();
            uploadHandler = handler;
            return this;
        }
    }

    @Override
    public MultiMap formAttributes() {
        synchronized (conn) {
            // Create it lazily
            if (attributes == null) {
                attributes = new CaseInsensitiveHeaders();
            }
            return attributes;
        }
    }

    @Override
    public String getFormAttribute(String attributeName) {
        return formAttributes().get(attributeName);
    }

    @Override
    public ServerWebSocket upgrade() {
        throw new UnsupportedOperationException("HTTP/2 request cannot be upgraded to a websocket");
    }

    @Override
    public boolean isEnded() {
        synchronized (conn) {
            return ended;
        }
    }

    @Override
    public HttpServerRequest customFrameHandler(Handler<HttpFrame> handler) {
        synchronized (conn) {
            customFrameHandler = handler;
            return this;
        }
    }

    @Override
    public HttpConnection connection() {
        return conn;
    }
}