All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.jdisc.http.HttpRequest Maven / Gradle / Ivy

// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.http;

import com.yahoo.jdisc.HeaderFields;
import com.yahoo.jdisc.Request;
import com.yahoo.jdisc.handler.CompletionHandler;
import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.RequestHandler;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.service.CurrentContainer;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.security.Principal;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * A HTTP request.
 *
 * @author Anirudha Khanna
 * @author Einar M R Rosenvinge
 */
public class HttpRequest extends Request {

    public enum Method {
        OPTIONS,
        GET,
        HEAD,
        POST,
        PUT,
        PATCH,
        DELETE,
        TRACE,
        CONNECT
    }

    public enum Version {
        HTTP_1_0("HTTP/1.0"),
        HTTP_1_1("HTTP/1.1"),
        HTTP_2_0("HTTP/2.0");

        private final String str;

        Version(String str) {
            this.str = str;
        }

        @Override
        public String toString() {
            return str;
        }

        public static Version fromString(String str) {
            for (Version version : values()) {
                if (version.str.equals(str)) {
                    return version;
                }
            }
            throw new IllegalArgumentException(str);
        }
    }

    private final long jvmRelativeCreatedAt = System.nanoTime();
    private final HeaderFields trailers = new HeaderFields();
    private final Map> parameters = new HashMap<>();
    private volatile Principal principal;
    private final long connectedAt;
    private Method method;
    private Version version;
    private SocketAddress remoteAddress;
    private URI proxyServer;
    private Long connectionTimeout;

    protected HttpRequest(CurrentContainer container, URI uri, Method method, Version version,
                          SocketAddress remoteAddress, Long connectedAtMillis, long creationTime)
    {
        super(container, uri, true, creationTime);
        try {
            this.method = method;
            this.version = version;
            this.remoteAddress = remoteAddress;
            this.parameters.putAll(getUriQueryParameters(uri));
            if (connectedAtMillis != null) {
                this.connectedAt = connectedAtMillis;
            } else {
                this.connectedAt = creationTime(TimeUnit.MILLISECONDS);
            }
        } catch (Throwable e) {
            release();
            throw e;
        }
    }

    private HttpRequest(Request parent, URI uri, Method method, Version version) {
        super(parent, uri);
        try {
            this.method = method;
            this.version = version;
            this.remoteAddress = null;
            this.parameters.putAll(getUriQueryParameters(uri));
            this.connectedAt = creationTime(TimeUnit.MILLISECONDS);
        } catch (Throwable e) {
            release();
            throw e;
        }
    }

    private static Map> getUriQueryParameters(URI uri) {
        if (uri.getRawQuery() == null) return Map.of();
        MultiMap params = new MultiMap<>();
        UrlEncoded.decodeUtf8To(uri.getRawQuery(), params);
        return Map.copyOf(params);
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Version getVersion() {
        return version;
    }

    /** Returns the remote address, or null if unresolved */
    public String getRemoteHostAddress() {
        if (remoteAddress instanceof InetSocketAddress) {
            InetAddress remoteInetAddress =  ((InetSocketAddress) remoteAddress).getAddress();
            if (remoteInetAddress == null)
                return null;
            return remoteInetAddress.getHostAddress();
        }
        else {
            throw new RuntimeException("Unknown SocketAddress class: " + remoteAddress.getClass().getName());
        }
    }

    public String getRemoteHostName() {
        if (remoteAddress instanceof InetSocketAddress) {
            InetAddress remoteInetAddress = ((InetSocketAddress) remoteAddress).getAddress();
            if (remoteInetAddress == null) return null; // not resolved; we have no network
            return remoteInetAddress.getHostName();
        }
        else {
            throw new RuntimeException("Unknown SocketAddress class: " + remoteAddress.getClass().getName());
        }
    }

    public int getRemotePort() {
        if (remoteAddress instanceof InetSocketAddress)
            return ((InetSocketAddress) remoteAddress).getPort();
        else
            throw new RuntimeException("Unknown SocketAddress class: " + remoteAddress.getClass().getName());
    }

    public void setVersion(Version version) {
        this.version = version;
    }

    public SocketAddress getRemoteAddress() {
        return remoteAddress;
    }

    public void setRemoteAddress(SocketAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public URI getProxyServer() {
        return proxyServer;
    }

    public void setProxyServer(URI proxyServer) {
        this.proxyServer = proxyServer;
    }

    /**
     * 

For server requests, this returns the timestamp of when the underlying HTTP channel was connected. * *

For client requests, this returns the same value as {@link #creationTime(java.util.concurrent.TimeUnit)}.

* * @param unit the unit to return the time in * @return the timestamp of when the underlying HTTP channel was connected, or request creation time */ public long getConnectedAt(TimeUnit unit) { return unit.convert(connectedAt, TimeUnit.MILLISECONDS); } public Long getConnectionTimeout(TimeUnit unit) { if (connectionTimeout == null) { return null; } return unit.convert(connectionTimeout, TimeUnit.MILLISECONDS); } /** *

Sets the allocated time that this HttpRequest is allowed to spend trying to connect to a remote host. This has * no effect on an HttpRequest received by a {@link RequestHandler}. If no connection timeout is assigned to an * HttpRequest, it defaults the connection-timeout in the client configuration.

* *

NOTE: Where {@link Request#setTimeout(long, TimeUnit)} sets the expiration time between calling a * RequestHandler and a {@link ResponseHandler}, this method sets the expiration time of the connect-operation as * performed by the client.

* * @param timeout The allocated amount of time. * @param unit The time unit of the timeout argument. */ public void setConnectionTimeout(long timeout, TimeUnit unit) { this.connectionTimeout = unit.toMillis(timeout); } public Map> parameters() { return parameters; } public void copyHeaders(HeaderFields target) { target.addAll(headers()); } public List decodeCookieHeader() { List cookies = headers().get(HttpHeaders.Names.COOKIE); if (cookies == null) { return List.of(); } List ret = new LinkedList<>(); for (String cookie : cookies) { ret.addAll(Cookie.fromCookieHeader(cookie)); } return ret; } public void encodeCookieHeader(List cookies) { headers().put(HttpHeaders.Names.COOKIE, Cookie.toCookieHeader(cookies)); } /** *

Returns the set of trailer header fields of this HttpRequest. These are typically meta-data that should have * been part of {@link #headers()}, but were not available prior to calling {@link #connect(ResponseHandler)}. You * must NOT WRITE to these headers AFTER calling {@link ContentChannel#close(CompletionHandler)}, and you must NOT * READ from these headers BEFORE {@link ContentChannel#close(CompletionHandler)} has been called.

* *

NOTE: These headers are NOT thread-safe. You need to explicitly synchronized on the returned object to * prevent concurrency issues such as ConcurrentModificationExceptions.

* * @return The trailer headers of this HttpRequest. */ public HeaderFields trailers() { return trailers; } /** * Returns whether this request was explicitly chunked from the client. NOTE that there are cases * where the underlying HTTP server library (Netty for the time being) will read the request in a chunked manner. An * application MUST wait for {@link com.yahoo.jdisc.handler.ContentChannel#close(com.yahoo.jdisc.handler.CompletionHandler)} * before it can actually know that it has received the entire request. * * @return true if this request was chunked from the client. */ public boolean isChunked() { return version == Version.HTTP_1_1 && headers().containsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); } public boolean hasChunkedResponse() { return version == Version.HTTP_1_1 && !headers().isTrue(HttpHeaders.Names.X_DISABLE_CHUNKING); } public boolean isKeepAlive() { if (headers().containsIgnoreCase(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE)) { return true; } if (headers().containsIgnoreCase(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE)) { return false; } return version == Version.HTTP_1_1; } /** * @return the relative created timestamp (using {@link System#nanoTime()} */ public long relativeCreatedAtNanoTime() { return jvmRelativeCreatedAt; } public Principal getUserPrincipal() { return principal; } public void setUserPrincipal(Principal principal) { this.principal = principal; } public static HttpRequest newServerRequest(CurrentContainer container, URI uri) { return newServerRequest(container, uri, Method.GET); } public static HttpRequest newServerRequest(CurrentContainer container, URI uri, Method method) { return newServerRequest(container, uri, method, Version.HTTP_1_1); } public static HttpRequest newServerRequest(CurrentContainer container, URI uri, Method method, Version version) { return newServerRequest(container, uri, method, version, null); } public static HttpRequest newServerRequest(CurrentContainer container, URI uri, Method method, Version version, SocketAddress remoteAddress) { return new HttpRequest(container, uri, method, version, remoteAddress, null, -1); } public static HttpRequest newServerRequest(CurrentContainer container, URI uri, Method method, Version version, SocketAddress remoteAddress, long connectedAtMillis, long creationTime) { return new HttpRequest(container, uri, method, version, remoteAddress, connectedAtMillis, creationTime); } public static HttpRequest newClientRequest(Request parent, URI uri) { return newClientRequest(parent, uri, Method.GET); } public static HttpRequest newClientRequest(Request parent, URI uri, Method method) { return newClientRequest(parent, uri, method, Version.HTTP_1_1); } public static HttpRequest newClientRequest(Request parent, URI uri, Method method, Version version) { return new HttpRequest(parent, uri, method, version); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy