Package jodd.http

Source Code of jodd.http.HttpRequest

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.http;

import jodd.util.Base64;
import jodd.util.StringBand;
import jodd.util.StringPool;
import jodd.util.StringUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import static jodd.util.StringPool.CRLF;
import static jodd.util.StringPool.SPACE;

/**
* HTTP request.
*/
public class HttpRequest extends HttpBase<HttpRequest> {

  private static final int DEFAULT_PORT = -1;

  protected String protocol = "http";
  protected String host = "localhost";
  protected int port = DEFAULT_PORT;
  protected String method = "GET";
  protected String path = StringPool.SLASH;
  protected HttpValuesMap<Object> query;

  // ---------------------------------------------------------------- init

  public HttpRequest() {
    initRequest();
  }

  /**
   * Prepares request on creation. By default, it just
   * adds "Connection: Close" header.
   */
  protected void initRequest() {
    connectionKeepAlive(false);
  }

  // ---------------------------------------------------------------- properties

  /**
   * Returns request host name.
   */
  public String host() {
    return host;
  }

  /**
   * Sets request host name.
   */
  public HttpRequest host(String host) {
    this.host = host;
    return this;
  }

  /**
   * Returns used protocol. By default it's "http".
   */
  public String protocol() {
    return protocol;
  }

  /**
   * Defines protocol.
   */
  public HttpRequest protocol(String protocol) {
    this.protocol = protocol;
    return this;
  }

  /**
   * Returns request port number. When port is not
   * explicitly defined, returns default port for
   * current protocol.
   */
  public int port() {
    if (port == DEFAULT_PORT) {
      if (protocol == null) {
        return 80;
      }
      if (protocol.equalsIgnoreCase("https")) {
        return 443;
      }
      return 80;
    }
    return port;
  }

  /**
   * Sets request port number.
   */
  public HttpRequest port(int port) {
    this.port = port;
    return this;
  }

  // ---------------------------------------------------------------- set

  /**
   * Sets the destination (method, host, port... ) at once.
   */
  public HttpRequest set(String destination) {
    destination = destination.trim();

    // http method

    int ndx = destination.indexOf(' ');

    if (ndx != -1) {
      method = destination.substring(0, ndx).toUpperCase();
      destination = destination.substring(ndx + 1);
    }

    // protocol

    ndx = destination.indexOf("://");

    if (ndx != -1) {
      protocol = destination.substring(0, ndx);
      destination = destination.substring(ndx + 3);
    }

    // host

    ndx = destination.indexOf('/');

    if (ndx == -1) {
      ndx = destination.length();
    }

    if (ndx != 0) {

      host = destination.substring(0, ndx);
      destination = destination.substring(ndx);

      // port

      ndx = host.indexOf(':');

      if (ndx == -1) {
        port = DEFAULT_PORT;
      } else {
        port = Integer.parseInt(host.substring(ndx + 1));
        host = host.substring(0, ndx);
      }
    }

    // path + query

    path(destination);

    return this;
  }

  // ---------------------------------------------------------------- static factories


  /**
   * Builds a CONNECT request.
   */
  public static HttpRequest connect(String destination) {
    return new HttpRequest()
        .method("CONNECT")
        .set(destination);
  }
  /**
   * Builds a GET request.
   */
  public static HttpRequest get(String destination) {
    return new HttpRequest()
        .method("GET")
        .set(destination);
  }
  /**
   * Builds a POST request.
   */
  public static HttpRequest post(String destination) {
    return new HttpRequest()
        .method("POST")
        .set(destination);
  }
  /**
   * Builds a PUT request.
   */
  public static HttpRequest put(String destination) {
    return new HttpRequest()
        .method("PUT")
        .set(destination);
  }
  /**
   * Builds a PATCH request.
   */
  public static HttpRequest patch(String destination) {
    return new HttpRequest()
        .method("PATCH")
        .set(destination);
  }
  /**
   * Builds a DELETE request.
   */
  public static HttpRequest delete(String destination) {
    return new HttpRequest()
        .method("DELETE")
        .set(destination);
  }
  /**
   * Builds a HEAD request.
   */
  public static HttpRequest head(String destination) {
    return new HttpRequest()
        .method("HEAD")
        .set(destination);
  }
  /**
   * Builds a TRACE request.
   */
  public static HttpRequest trace(String destination) {
    return new HttpRequest()
        .method("TRACE")
        .set(destination);
  }
  /**
   * Builds an OPTIONS request.
   */
  public static HttpRequest options(String destination) {
    return new HttpRequest()
        .method("OPTIONS")
        .set(destination);
  }

  // ---------------------------------------------------------------- request

  /**
   * Returns request method.
   */
  public String method() {
    return method;
  }

  /**
   * Specifies request method. It will be converted into uppercase.
   */
  public HttpRequest method(String method) {
    this.method = method.toUpperCase();
    return this;
  }

  /**
   * Returns request path, without the query.
   */
  public String path() {
    return path;
  }

  /**
   * Sets request path. Query string is allowed.
   * Adds a slash if path doesn't start with one.
   * Query will be stripped out from the path.
   * Previous query is discarded.
   * @see #query()
   */
  public HttpRequest path(String path) {
    // this must be the only place that sets the path

    if (path.startsWith(StringPool.SLASH) == false) {
      path = StringPool.SLASH + path;
    }

    int ndx = path.indexOf('?');

    if (ndx != -1) {
      String queryString = path.substring(ndx + 1);

      path = path.substring(0, ndx);

      query = HttpUtil.parseQuery(queryString, true);
    } else {
      query = HttpValuesMap.ofObjects();
    }

    this.path = path;

    return this;
  }

  /**
   * Forces multipart requests. When set to <code>false</code>,
   * it will be {@link #isFormMultipart() detected} if request
   * should be multipart. By setting this to <code>true</code>
   * we are forcing usage of multipart request.
   */
  public HttpRequest multipart(boolean multipart) {
    this.multipart = true;
    return this;
  }


  // ---------------------------------------------------------------- query

  /**
   * Adds query parameter.
   */
  public HttpRequest query(String name, String value) {
    query.add(name, value);
    return this;
  }

  /**
   * Adds many query parameters at once. Although it accepts objects,
   * each value will be converted to string.
   */
  public HttpRequest query(String name1, Object value1, Object... parameters) {
    query(name1, value1 == null ? null : value1.toString());

    for (int i = 0; i < parameters.length; i += 2) {
      String name = parameters[i].toString();

      String value = parameters[i + 1].toString();
      query.add(name, value == null ? null : value);
    }
    return this;
  }

  /**
   * Adds all parameters from the provided map.
   */
  public HttpRequest query(Map<String, String> queryMap) {
    for (Map.Entry<String, String> entry : queryMap.entrySet()) {
      query.add(entry.getKey(), entry.getValue());
    }
    return this;
  }

  /**
   * Returns backend map of query parameters.
   */
  public Map<String, Object[]> query() {
    return query;
  }

  /**
   * Clears all query parameters.
   */
  public HttpRequest clearQueries() {
    query.clear();
    return this;
  }

  /**
   * Removes query parameters for given name.
   */
  public HttpRequest removeQuery(String name) {
    query.remove(name);
    return this;
  }

  // ---------------------------------------------------------------- queryString

  /**
   * @see #queryString(String, boolean)
   */
  public HttpRequest queryString(String queryString) {
    return queryString(queryString, true);
  }

  /**
   * Sets query from provided query string. Previous query values
   * are discarded.
   */
  public HttpRequest queryString(String queryString, boolean decode) {
    this.query = HttpUtil.parseQuery(queryString, decode);
    return this;
  }

  /**
   * Generates query string. All values are URL encoded.
   */
  public String queryString() {
    if (query == null) {
      return StringPool.EMPTY;
    }
    return HttpUtil.buildQuery(query, queryEncoding);
  }

  // ---------------------------------------------------------------- query encoding

  protected String queryEncoding = JoddHttp.defaultQueryEncoding;

  /**
   * Defines encoding for query parameters. Default value is
   * copied from {@link JoddHttp#defaultQueryEncoding}.
   */
  public HttpRequest queryEncoding(String encoding) {
    this.queryEncoding = encoding;
    return this;
  }

  // ---------------------------------------------------------------- full path

  /**
   * Returns full URL path.
   * Simply concatenates {@link #protocol(String) protocol}, {@link #host(String) host},
   * {@link #port(int) port}, {@link #path(String) path} and {@link #queryString(String) query string}.
   */
  public String url() {
    StringBand url = new StringBand(8);

    if (protocol != null) {
      url.append(protocol);
      url.append("://");
    }

    if (host != null) {
      url.append(host);
    }

    if (port != DEFAULT_PORT) {
      url.append(':');
      url.append(port);
    }

    if (path != null) {
      url.append(path);
    }

    String queryString = queryString();

    if (StringUtil.isNotBlank(queryString)) {
      url.append('?');
      url.append(queryString);
    }

    return url.toString();
  }

  // ---------------------------------------------------------------- auth

  /**
   * Enables basic authentication by adding required header.
   */
  public HttpRequest basicAuthentication(String username, String password) {
    String data = username.concat(StringPool.COLON).concat(password);

    String base64 = Base64.encodeToString(data);

    header("Authorization", "Basic " + base64, true);

    return this;
  }

  // ---------------------------------------------------------------- misc

  /**
   * Sets 'Host' header from current host and port.
   */
  public HttpRequest setHostHeader() {
    String hostPort = this.host;

    if (port != DEFAULT_PORT) {
      hostPort += StringPool.COLON + port;
    }

    header(HEADER_HOST, hostPort, true);
    return this;
  }

  // ---------------------------------------------------------------- monitor

  /**
   * Registers {@link jodd.http.HttpProgressListener listener} that will
   * monitor upload progress. Be aware that the whole size of the
   * request is being monitored, not only the files content.
   */
  public HttpRequest monitor(HttpProgressListener httpProgressListener) {
    this.httpProgressListener = httpProgressListener;
    return this;
  }

  // ---------------------------------------------------------------- connection properties

  protected int timeout = -1;

  /**
   * Defines connection timeout in milliseconds.
   * @see jodd.http.HttpConnection#setTimeout(int)
   */
  public HttpRequest timeout(int milliseconds) {
    this.timeout = milliseconds;
    return this;
  }

  /**
   * Returns timeout in milliseconds. Negative value means that
   * default value is used.
   */
  public int timeout() {
    return timeout;
  }

  // ---------------------------------------------------------------- send

  protected HttpConnection httpConnection;
  protected HttpConnectionProvider httpConnectionProvider;

  /**
   * Returns http connection provider that was used for creating
   * current http connection.
   */
  public HttpConnectionProvider httpConnectionProvider() {
    return httpConnectionProvider;
  }

  /**
   * Returns {@link HttpConnection} that is going to be
   * used for sending this request. Value is available
   * ONLY after calling {@link #open()} and before {@link #send()}.
   */
  public HttpConnection httpConnection() {
    return httpConnection;
  }

  /**
   * Opens a new {@link HttpConnection connection} using
   * {@link JoddHttp#httpConnectionProvider default connection provider}.
   */
  public HttpRequest open() {
    return open(JoddHttp.httpConnectionProvider);
  }

  /**
   * Opens a new {@link jodd.http.HttpConnection connection}
   * using given {@link jodd.http.HttpConnectionProvider}.
   */
  public HttpRequest open(HttpConnectionProvider httpConnectionProvider) {
    if (this.httpConnection != null) {
      throw new HttpException("Connection already opened");
    }
    try {
      this.httpConnectionProvider = httpConnectionProvider;
      this.httpConnection = httpConnectionProvider.createHttpConnection(this);
    } catch (IOException ioex) {
      throw new HttpException(ioex);
    }

    return this;
  }

  /**
   * Assignees provided {@link jodd.http.HttpConnection} for communication.
   * It does not actually opens it until the {@link #send() sending}.
   */
  public HttpRequest open(HttpConnection httpConnection) {
    if (this.httpConnection != null) {
      throw new HttpException("Connection already opened");
    }
    this.httpConnection = httpConnection;
    this.httpConnectionProvider = null;
    return this;
  }

  /**
   * Continues using the same keep-alive connection.
   * Don't use any variant of <code>open()</code> when
   * continuing the communication!
   * First it checks if "Connection" header exist in the response
   * and if it is equal to "Keep-Alive" value. Then it
   * checks the "Keep-Alive" headers "max" parameter.
   * If its value is positive, then the existing {@link jodd.http.HttpConnection}
   * from the request will be reused. If max value is 1,
   * connection will be sent with "Connection: Close" header, indicating
   * its the last request. When new connection is created, the
   * same {@link jodd.http.HttpConnectionProvider} that was used for
   * creating initial connection is used for opening the new connection.
   *
   * @param doContinue set it to <code>false</code> to indicate the last connection
   */
  public HttpRequest keepAlive(HttpResponse httpResponse, boolean doContinue) {
    boolean keepAlive = httpResponse.isConnectionPersistent();
    if (keepAlive) {
      HttpConnection previousConnection = httpResponse.getHttpRequest().httpConnection;

      if (previousConnection != null) {
        // keep using the connection!
        this.httpConnection = previousConnection;
        this.httpConnectionProvider = httpResponse.getHttpRequest().httpConnectionProvider();
      }

      //keepAlive = true; (already set)
    } else {
      // close previous connection
      httpResponse.close();

      // force keep-alive on new request
      keepAlive = true;
    }

    // if we don't want to continue with this persistent session, mark this connection as closed
    if (doContinue == false) {
      keepAlive = false;
    }

    connectionKeepAlive(keepAlive);

    // if connection is not opened, open it using previous connection provider
    if (httpConnection == null) {
      open(httpResponse.getHttpRequest().httpConnectionProvider());
    }
    return this;
  }

  /**
   * {@link #open() Opens connection} if not already open, sends request,
   * reads response and closes the request. If keep-alive mode is enabled
   * connection will not be closed.
   */
  public HttpResponse send() {
    if (httpConnection == null) {
      open();
    }

    // prepare http connection

    if (timeout != -1) {
      httpConnection.setTimeout(timeout);
    }

    // sends data
    HttpResponse httpResponse;
    try {
      OutputStream outputStream = httpConnection.getOutputStream();

      sendTo(outputStream);

      InputStream inputStream = httpConnection.getInputStream();

      httpResponse = HttpResponse.readFrom(inputStream);

      httpResponse.assignHttpRequest(this);
    } catch (IOException ioex) {
      throw new HttpException(ioex);
    }

    boolean keepAlive = httpResponse.isConnectionPersistent();

    if (keepAlive == false) {
      // closes connection if keep alive is false, or if counter reached 0
      httpConnection.close();
      httpConnection = null;
    }

    return httpResponse;
  }

  // ---------------------------------------------------------------- buffer

  /**
   * Prepares the request buffer.
   */
  @Override
  protected Buffer buffer(boolean fullRequest) {
    // INITIALIZATION

    // host port

    if (header(HEADER_HOST) == null) {
      setHostHeader();
    }

    // form

    Buffer formBuffer = formBuffer();

    // query string

    String queryString = queryString();

    // user-agent

    if (header("User-Agent") == null) {
      header("User-Agent", "Jodd HTTP");
    }

    // POST method requires Content-Type to be set

    if (method.equals("POST") && (contentLength() == null)) {
      contentLength(0);
    }


    // BUILD OUT

    Buffer request = new Buffer();

    request.append(method)
      .append(SPACE)
      .append(path);

    if (query != null && !query.isEmpty()) {
      request.append('?');
      request.append(queryString);
    }

    request.append(SPACE)
      .append(httpVersion)
      .append(CRLF);

    for (String key : headers.keySet()) {
      String[] values = headers.getStrings(key);

      String headerName = HttpUtil.prepareHeaderParameterName(key);

      for (String value : values) {
        request.append(headerName);
        request.append(": ");
        request.append(value);
        request.append(CRLF);
      }
    }

    if (fullRequest) {
      request.append(CRLF);

      if (form != null) {
        request.append(formBuffer);
      } else if (body != null) {
        request.append(body);
      }
    }

    return request;
  }

  // ---------------------------------------------------------------- parse

  /**
   * Parses input stream and creates new <code>HttpRequest</code> object.
   */
  public static HttpRequest readFrom(InputStream in) {
    BufferedReader reader;
    try {
      reader = new BufferedReader(new InputStreamReader(in, StringPool.ISO_8859_1));
    } catch (UnsupportedEncodingException uneex) {
      return null;
    }

    HttpRequest httpRequest = new HttpRequest();

    String line;
    try {
      line = reader.readLine();
    } catch (IOException ioex) {
      throw new HttpException(ioex);
    }

    if (!StringUtil.isBlank(line)) {
      String[] s = StringUtil.splitc(line, ' ');

      httpRequest.method(s[0]);
      httpRequest.path(s[1]);
      httpRequest.httpVersion(s[2]);

      httpRequest.readHeaders(reader);
      httpRequest.readBody(reader);
    }

    return httpRequest;
  }

}
TOP

Related Classes of jodd.http.HttpRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.