Java tutorial
/* * 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); } } }