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

org.hcjf.io.net.http.HttpClient Maven / Gradle / Ivy

package org.hcjf.io.net.http;

import org.hcjf.errors.Errors;
import org.hcjf.io.net.NetClient;
import org.hcjf.io.net.NetPackage;
import org.hcjf.io.net.NetService;
import org.hcjf.io.net.NetSession;
import org.hcjf.log.Log;
import org.hcjf.properties.SystemProperties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.util.UUID;

/**
 * Client to invoke some http server.
 * @author javaito
 */
public class HttpClient extends NetClient {

    public static final String HTTP_CLIENT_LOG_TAG = "HTTP_CLIENT";

    private static final String CONNECTION_TIMEOUT_MESSAGE = "Connection timeout";
    private static final String READ_TIMEOUT_MESSAGE = "Read timeout";
    private static final String DISCONNECTION_MESSAGE = "Http client request end";
    private static final String SESSION_NAME = "Http client session";

    private final URL url;
    private Status status;
    private HttpResponse response;
    private HttpRequest request;
    private Long connectTimeout;
    private Long writeTimeout;
    private Long readTimeout;
    private HttpSession session;
    private HttpPackage.HttpProtocol httpProtocol;
    private Boolean httpsInsecureConnection;

    public HttpClient(URL url) {
        super(url.getHost(), url.getPort() != -1 ? url.getPort() :
                    url.getProtocol().equalsIgnoreCase(HttpPackage.HttpProtocol.HTTPS.toString()) ?
                            SystemProperties.getInteger(SystemProperties.Net.Https.DEFAULT_CLIENT_PORT) :
                            SystemProperties.getInteger(SystemProperties.Net.Http.DEFAULT_CLIENT_PORT),
                    url.getProtocol().equalsIgnoreCase(HttpPackage.HttpProtocol.HTTPS.toString()) ?
                            NetService.TransportLayerProtocol.TCP_SSL :
                            NetService.TransportLayerProtocol.TCP);
        this.url = url;
        this.connectTimeout = SystemProperties.getLong(SystemProperties.Net.Http.DEFAULT_CLIENT_CONNECT_TIMEOUT);
        this.writeTimeout = SystemProperties.getLong(SystemProperties.Net.Http.DEFAULT_CLIENT_WRITE_TIMEOUT);
        this.readTimeout = SystemProperties.getLong(SystemProperties.Net.Http.DEFAULT_CLIENT_READ_TIMEOUT);
        this.httpProtocol = url.getProtocol().equalsIgnoreCase(HttpPackage.HttpProtocol.HTTPS.toString()) ?
                HttpPackage.HttpProtocol.HTTPS : HttpPackage.HttpProtocol.HTTP;
        this.httpsInsecureConnection = false;
        init();
    }

    private void init() {
        //Init defaults
        this.response = null;
        this.status = Status.INACTIVE;

        //Create default request
        request = new HttpRequest();
        request.setProtocol(httpProtocol);
        request.setHttpVersion(HttpVersion.VERSION_1_1);
        request.setContext(url.getFile());
        request.setMethod(HttpMethod.GET);
        request.setBody(new byte[0]);
        request.addHeader(new HttpHeader(HttpHeader.HOST, url.getHost()));
        request.addHeader(new HttpHeader(HttpHeader.USER_AGENT, HttpHeader.DEFAULT_USER_AGENT));
    }

    /**
     * Return the connection timeout value.
     * @return Connection timeout vaue.
     */
    public final Long getConnectTimeout() {
        return connectTimeout;
    }

    /**
     * Set the connection timeout value.
     * @param connectTimeout Connection timeout value.
     */
    public final void setConnectTimeout(Long connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    /**
     * Return the write timeout value.
     * @return Write timeout value.
     */
    public final Long getWriteTimeout() {
        return writeTimeout;
    }

    /**
     * Set the write timeout value.
     * @param writeTimeout Write timeout value.
     */
    public final void setWriteTimeout(Long writeTimeout) {
        this.writeTimeout = writeTimeout;
    }

    /**
     * Return the read timeout value.
     * @return Read timeout value.
     */
    public final Long getReadTimeout() {
        return readTimeout;
    }

    /**
     * Set the read timeout value.
     * @param readTimeout Read timeout value.
     */
    public final void setReadTimeout(Long readTimeout) {
        this.readTimeout = readTimeout;
    }

    /**
     * Add path over base context of the constructor URI.
     * @param paths Adding paths.
     */
    public void addPath(String... paths) {
        StringBuilder newPath = new StringBuilder();
        newPath.append(this.request.getContext());
        String separator = this.request.getContext().endsWith(
                HttpPackage.HTTP_CONTEXT_SEPARATOR) ? "" : HttpPackage.HTTP_CONTEXT_SEPARATOR;
        for(String path : paths) {
            newPath.append(separator).append(path);
            separator = HttpPackage.HTTP_CONTEXT_SEPARATOR;
        }
        this.request.setContext(newPath.toString());
    }

    /**
     * Return all the internal fields to the default values
     * in order to reuse this client.
     */
    public final void reset() {
        init();
    }

    /**
     * Set the request body.
     * @param body Request body.
     */
    public void setBody(byte[] body) {
        request.setBody(body);
    }

    /**
     * Set the http method to request.
     * @param method Http method.
     */
    public final void setHttpMethod(HttpMethod method) {
        request.setMethod(method);
    }

    /**
     * Add header to request.
     * @param header Http header.
     */
    public final void addHttpHeader(String header) {
        request.addHeader(new HttpHeader(header));
    }

    /**
     * Add header to request.
     * @param header Http header.
     */
    public final void addHttpHeader(HttpHeader header) {
        request.addHeader(header);
    }

    /**
     * Returns if the https connection is insecure or not.
     * @return Secure https connection
     */
    public Boolean isHttpsInsecureConnection() {
        return httpsInsecureConnection;
    }

    /**
     * Set if the https connection is insecure of not.
     * @param httpsInsecureConnection Secure https connection.
     */
    public void setHttpsInsecureConnection(Boolean httpsInsecureConnection) {
        this.httpsInsecureConnection = httpsInsecureConnection;
    }

    /**
     * Creates the SSL engine.
     * @return SSL engine instance.
     */
    @Override
    protected SSLEngine getSSLEngine() {
        try {
            SSLEngine engine;
            if(isHttpsInsecureConnection()) {
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return null;
                            }
                            public void checkClientTrusted(
                                    java.security.cert.X509Certificate[] certs, String authType) {
                            }
                            public void checkServerTrusted(
                                    java.security.cert.X509Certificate[] certs, String authType) {
                            }
                        }
                };
                SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                engine = sslContext.createSSLEngine();
            } else {
                engine = SSLContext.getDefault().createSSLEngine();
            }
            engine.setUseClientMode(true);
            engine.beginHandshake();
            return engine;
        } catch (Exception ex) {
            throw new IllegalArgumentException(Errors.getMessage(Errors.ORG_HCJF_IO_NET_HTTP_6), ex);
        }
    }

    /**
     * This method return the object that represent the
     * client's session.
     * @return Client's session.
     */
    @Override
    public HttpSession getSession() {
        return session;
    }

    @Override
    public HttpSession checkSession(HttpSession session, HttpPackage payLoad, NetPackage netPackage) {
        return session;
    }

    /**
     * This method decode the implementation data.
     * @param payLoad Implementation data.
     * @return Implementation data encoded.
     */
    @Override
    protected byte[] encode(HttpPackage payLoad) {
        byte[] result = null;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            out.write(payLoad.getProtocolHeader());
            if(payLoad.getBody() != null) {
                out.write(payLoad.getBody());
            }
            out.flush();
            result = out.toByteArray();
        } catch (Exception ex){}
        return result;
    }

    /**
     * This method decode the net package to obtain the implementation data
     * @param netPackage Net package.
     * @return Return the implementation data.
     */
    @Override
    protected HttpPackage decode(NetPackage netPackage) {
        if(response == null) {
            response = new HttpResponse();
            response.setProtocol(httpProtocol);
        }
        response.addData(netPackage.getPayload());
        return response;
    }

    /**
     * Destroy the session.
     * @param session Net session to be destroyed
     */
    @Override
    public void destroySession(NetSession session) {
        session = null;
    }

    /**
     *
     * @param session Connected session.
     * @param payLoad Decoded package payload.
     * @param netPackage Original package.
     */
    @Override
    protected void onConnect(HttpSession session, HttpPackage payLoad, NetPackage netPackage) {
        synchronized (this) {
            status = Status.CONNECTED;
            notifyAll();
        }
    }

    /**
     *
     * @param session Net session.
     * @param payLoad Net package decoded
     * @param netPackage Net package.
     */
    @Override
    protected final void onRead(HttpSession session, HttpPackage payLoad, NetPackage netPackage) {
        if(response.isComplete()) {
            synchronized (this) {
                status = Status.DONE;
                notifyAll();
            }
        }
    }

    /**
     * This method execute all the steps to do a http request. Creates the connection,
     * sends the request package and reads the response, then this response
     * is returned as a method response.
     * @return Http response package.
     */
    public final HttpResponse request() {
        long time = System.currentTimeMillis();
        session = new HttpSession(UUID.randomUUID(), this);
        session.setRequest(request);
        session.setSessionName(SESSION_NAME);
        Integer errorCode = null;

        //Connection block
        status = Status.CONNECTING;
        synchronized (this) {
            connect();
            if (status == Status.CONNECTING) {
                try {
                    wait(getConnectTimeout());
                } catch (InterruptedException e) {}
            }

            if (status == Status.CONNECTING) {
                status = Status.ERROR;
                errorCode = HttpResponseCode.REQUEST_TIMEOUT;
            }
        }

        //Request writing request and read response block
        if(status != Status.ERROR) {
            status = Status.WRITING;

            synchronized (this) {
                Log.out(HTTP_CLIENT_LOG_TAG, "Request\r\n%s", request.toString());
                try {
                    write(getSession(), request, false);
                } catch (Exception ex) {
                    status = Status.ERROR;
                    errorCode = HttpResponseCode.BAD_REQUEST;
                }

                if (status == Status.WRITING) {
                    try {
                        wait(getReadTimeout());
                    } catch (InterruptedException e) {
                    }
                }

                if (status == Status.WRITING) {
                    status = Status.ERROR;
                    errorCode = HttpResponseCode.REQUEST_TIMEOUT;
                }
            }
        }

        HttpResponse response;
        if(status == Status.ERROR) {
            response = new HttpResponse();
            response.setHttpVersion(HttpVersion.VERSION_1_1);
            response.setResponseCode(errorCode);
        } else {
            response = this.response;
        }

        disconnect(getSession(), DISCONNECTION_MESSAGE);

        Log.in(HTTP_CLIENT_LOG_TAG, "Response -> [Time: %d ms]\r\n%s",
                (System.currentTimeMillis() - time), response.toString());
        return response;
    }

    /**
     * Clietn request status.
     */
    private enum Status {

        INACTIVE,

        CONNECTING,

        CONNECTED,

        WRITING,

        DONE,

        ERROR;

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy