net.jadler.Request.java Source code

Java tutorial

Introduction

Here is the source code for net.jadler.Request.java

Source

/*
 * Copyright (c) 2013 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;

/**
 * <p>Immutable http request abstraction. Provides request method, URI, body, parameters and headers.</p>
 * 
 * <p>To create instances of this class use {@link #builder()}.</p>
 */
public class Request {

    private static final Charset DEFAULT_ENCODING = Charset.forName("ISO-8859-1");

    private final String method;

    private final URI requestURI;

    private final byte[] body;

    private final KeyValues parameters;

    private final KeyValues headers;

    private final Charset encoding;

    @SuppressWarnings("unchecked")
    private Request(final String method, final URI requestURI, final KeyValues headers, final byte[] body,
            final Charset encoding) {

        Validate.notEmpty(method, "method cannot be empty");
        this.method = method;

        Validate.notNull(requestURI, "requestURI cannot be null");
        this.requestURI = requestURI;

        this.encoding = encoding;

        Validate.notNull(body, "body cannot be null, use an empty array instead");
        this.body = body;

        Validate.notNull(headers, "headers cannot be null");
        this.headers = headers;

        this.parameters = readParameters();
    }

    /**
     * @return http method
     */
    public String getMethod() {
        return method;
    }

    /**
     * @return URI of the request. For example http://localhost:8080/test/file?a=4
     */
    public URI getURI() {
        return this.requestURI;
    }

    /**
     * @return all http parameters (read from both query string and request body) from this request.
     * Never returns {@code null}
     */
    public KeyValues getParameters() {
        return this.parameters;
    }

    /**
     * @return all http headers from this request. Never returns {@code null}
     */
    public KeyValues getHeaders() {
        return this.headers;
    }

    /**
     * Returns the body content as an {@link InputStream} instance. This method can be called multiple times
     * always returning valid, readable stream.
     * @return request body as an {@link InputStream} instance
     */
    public InputStream getBodyAsStream() {
        return new ByteArrayInputStream(body);
    }

    /**
     * @return request body as an array of bytes
     */
    public byte[] getBodyAsBytes() {
        return this.body.clone();
    }

    /**
     * @return request body as a string (if the body is empty, returns an empty string). If no encoding was
     * set using the {@code Content-Type} header ISO-8859-1 will be used
     * (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html).
     */
    public String getBodyAsString() {
        return new String(this.body, this.getEffectiveEncoding());
    }

    /**
     * @return value of the {@code Content-Type} header.
     */
    public String getContentType() {
        return this.headers.getValue("content-type");
    }

    /**
     * @return request body encoding set by the {@code Content-Type} header or {@code null} if not set
     */
    public Charset getEncoding() {
        return this.encoding;
    }

    /**
     * @return new builder for creating {@link Request} instances
     */
    public static Builder builder() {
        return new Builder();
    }

    @SuppressWarnings("unchecked")
    private KeyValues readParameters() {
        KeyValues params = readParametersFromQueryString();

        //TODO: shitty attempt to check whether the body contains html form data. Please refactor.
        if (!StringUtils.isBlank(this.getContentType())
                && this.getContentType().contains("application/x-www-form-urlencoded")) {

            if ("POST".equalsIgnoreCase(this.getMethod()) || "PUT".equalsIgnoreCase(this.getMethod())) {
                params = params.addAll(this.readParametersFromBody());
            }
        }

        return params;
    }

    private KeyValues readParametersFromQueryString() {
        return this.readParametersFromString(this.requestURI.getRawQuery());
    }

    private KeyValues readParametersFromBody() {
        return this.readParametersFromString(new String(this.body, this.getEffectiveEncoding()));
    }

    private KeyValues readParametersFromString(final String parametersString) {
        KeyValues res = new KeyValues();

        if (StringUtils.isBlank(parametersString)) {
            return res;
        }

        final String[] pairs = parametersString.split("&");

        for (final String pair : pairs) {
            final int idx = pair.indexOf('=');
            if (idx > -1) {
                final String name = StringUtils.substring(pair, 0, idx);
                final String value = StringUtils.substring(pair, idx + 1);
                res = res.add(name, value);

            } else {
                res = res.add(pair, "");
            }
        }

        return res;
    }

    private Charset getEffectiveEncoding() {
        return this.encoding == null ? DEFAULT_ENCODING : this.encoding;
    }

    @Override
    public String toString() {
        return "Request{" + "method='" + method + '\'' + ", requestURI=" + requestURI + ", parameters=" + parameters
                + ", headers=" + headers + '}';
    }

    /**
     * A builder class for {@link Request} instances.
     */
    public static class Builder {

        private String method;
        private URI requestURI;
        private byte[] body = new byte[0];
        private KeyValues headers = new KeyValues();
        private Charset encoding = null;

        /**
         * Private constructor. Use {@link Request#builder()} instead.
         */
        private Builder() {
        }

        /**
         * Sets the request method. Must be called before {@link Builder#build()}.
         * @param method request method
         * @return this builder
         */
        public Builder method(final String method) {
            this.method = method;
            return this;
        }

        /**
         * Sets the request URI. Must be called before {@link Builder#build()}.
         * @param requestURI request URI
         * @return this builder
         */
        public Builder requestURI(final URI requestURI) {
            this.requestURI = requestURI;
            return this;
        }

        /**
         * Sets the request body. If not called, an empty body will be used.
         * @param body request body (cannot be {@code null})
         * @return this builder
         */
        public Builder body(final byte[] body) {
            this.body = body;
            return this;
        }

        /**
         * Sets the request headers (all previously defined headers will be lost).
         * @param headers request headers (cannot be {@code null})
         * @return this builder
         */
        public Builder headers(final KeyValues headers) {
            Validate.notNull(headers, "headers cannot be null");

            this.headers = headers;
            return this;
        }

        /**
         * Adds a request header to the constructed request instance.
         * @param name header name (cannot be empty)
         * @param value header value (cannot be {@code null})
         * @return this builder 
         */
        public Builder header(final String name, final String value) {
            Validate.notEmpty(name, "name cannot be blank");
            Validate.notNull(value, "value cannot be null");

            this.headers = this.headers.add(name.toLowerCase(), value);
            return this;
        }

        /**
         * Sets the request encoding. If not set {@code null} value will be used signalizing no encoding was set
         * in the incoming request (using the {@code Content-Type} header)
         * @param encoding request encoding (can be {@code null})
         * @return this builder
         */
        public Builder encoding(final Charset encoding) {
            this.encoding = encoding;
            return this;
        }

        /**
         * @return new {@link Request} instance
         */
        public Request build() {
            return new Request(method, requestURI, headers, body, encoding);
        }
    }
}