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

org.wisdom.framework.vertx.RequestFromVertx Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.wisdom.framework.vertx;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.net.MediaType;
import org.vertx.java.core.Handler;
import org.vertx.java.core.MultiMap;
import org.vertx.java.core.buffer.Buffer;
import org.vertx.java.core.http.HttpServerFileUpload;
import org.vertx.java.core.http.HttpServerRequest;
import org.wisdom.api.configuration.ApplicationConfiguration;
import org.wisdom.api.cookies.Cookie;
import org.wisdom.api.cookies.Cookies;
import org.wisdom.api.http.HeaderNames;
import org.wisdom.api.http.MimeTypes;
import org.wisdom.api.http.Request;
import org.wisdom.framework.vertx.cookies.CookiesImpl;
import org.wisdom.framework.vertx.file.DiskFileUpload;
import org.wisdom.framework.vertx.file.MixedFileUpload;
import org.wisdom.framework.vertx.file.VertxFileUpload;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * An implementation of {@link org.wisdom.api.http.Request} based on Vert.X Request
 * ({@link org.vertx.java.core.http.HttpServerRequest}).
 */
public class RequestFromVertx extends Request {

    private final HttpServerRequest request;
    private final Cookies cookies;

    /**
     * List of uploaded files.
     */
    private List files = Lists.newArrayList();

    /**
     * The raw body.
     */
    private Buffer raw = new Buffer(0);

    /**
     * The map used to store data shared in the request scope.
     */
    private final Map data;

    private Map> formData;
    private Map> headers;

    /**
     * Creates a {@link org.wisdom.framework.vertx.RequestFromVertx} object
     *
     * @param context       the HTTP content
     * @param request       the Vertx Request
     * @param configuration the application configuration
     */
    public RequestFromVertx(final ContextFromVertx context, final HttpServerRequest request,
                            final ApplicationConfiguration configuration) {
        this.request = request;
        if (HttpUtils.isPostOrPut(request)) {
            this.request.expectMultiPart(true);
            this.request.uploadHandler(new Handler() {
                public void handle(HttpServerFileUpload upload) {
                    files.add(new MixedFileUpload(context.vertx(), upload,
                            configuration.getLongWithDefault("http.upload.disk.threshold", DiskFileUpload.MINSIZE),
                            configuration.getLongWithDefault("http.upload.max", -1l)));
                }
            });
        }

        this.cookies = new CookiesImpl(request);
        this.data = new HashMap<>();

        this.request.dataHandler(new Handler() {
            @Override
            public void handle(Buffer event) {
                if (event == null) {
                    return;
                }

                // We may have the content in different HTTP message, check if we already have a content.
                // Issue #257.
                // To avoid we run out of memory we cut the read body to 100Kb. This can be configured using the
                // "request.body.max.size" property.
                boolean exceeded = raw.length() >=
                        configuration.getIntegerWithDefault("request.body.max.size", 100 * 1024);
                if (!exceeded) {
                    raw.appendBuffer(event);
                }
            }
        });
    }

    /**
     * The Content-Type header field indicates the media type of the request
     * body sent to the recipient. E.g. {@code Content-Type: text/html;
     * charset=ISO-8859-4}
     *
     * @return the content type of the incoming request.
     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
     */
    @Override
    public String contentType() {
        return request.headers().get(HeaderNames.CONTENT_TYPE);
    }

    /**
     * Gets the encoding that is acceptable for the client. E.g. Accept-Encoding:
     * compress, gzip
     * 

* The Accept-Encoding request-header field is similar to Accept, but * restricts the content-codings that are acceptable in the response. * * @return the encoding that is acceptable for the client * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ @Override public String encoding() { return request.headers().get(HeaderNames.ACCEPT_ENCODING); } /** * Gets the language that is acceptable for the client. E.g. Accept-Language: * da, en-gb;q=0.8, en;q=0.7 *

* The Accept-Language request-header field is similar to Accept, but * restricts the set of natural languages that are preferred as a response * to the request. * * @return the language that is acceptable for the client * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ @Override public String language() { return request.headers().get(HeaderNames.ACCEPT_LANGUAGE); } /** * Gets the charset that is acceptable for the client. E.g. Accept-Charset: * iso-8859-5, unicode-1-1;q=0.8 *

* The Accept-Charset request-header field can be used to indicate what * character sets are acceptable for the response. This field allows clients * capable of understanding more comprehensive or special- purpose character * sets to signal that capability to a server which is capable of * representing documents in those character sets. * * @return the charset that is acceptable for the client * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ @Override public String charset() { return request.headers().get(HeaderNames.ACCEPT_CHARSET); } /** * The complete request URI, containing both path and query string. */ @Override public String uri() { return request.uri(); } /** * Returns the name of the HTTP method with which this * request was made, for example, GET, POST, or PUT. * Same as the value of the CGI variable REQUEST_METHOD. * * @return a String * specifying the name * of the method with which * this request was made (eg GET, POST, PUT...) */ @Override public String method() { return request.method(); } /** * The client IP address. *

* If the X-Forwarded-For header is present, then this method will return the value in that header * if either the local address is 127.0.0.1, or if trustxforwarded is configured to be true in the * application configuration file. */ @Override public String remoteAddress() { if (headers().containsKey(HeaderNames.X_FORWARD_FOR)) { return getHeader(HeaderNames.X_FORWARD_FOR); } else { InetSocketAddress remote = request.remoteAddress(); return remote.getAddress().getHostAddress(); } } /** * The request host. */ @Override public String host() { InetSocketAddress remote = request.remoteAddress(); return remote.getHostName(); } /** * The URI path, without the query part. */ @Override public String path() { try { return new URI(request.uri()).getRawPath(); } catch (URISyntaxException e) { //NOSONAR // Should never be the case. return uri(); } } /** * Get the preferred content media type that is acceptable for the client. For instance, in Accept: text/*;q=0.3, * text/html;q=0.7, text/html;level=1,text/html;level=2;q=0.4, text/html is returned. *

* The Accept request-header field can be used to specify certain media * types which are acceptable for the response. Accept headers can be used * to indicate that the request is specifically limited to a small set of * desired types, as in the case of a request for an in-line image. * * @return a MediaType that is acceptable for the * client or {@see MediaType#HTML_UTF_8} if not set * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ @Override public MediaType mediaType() { Collection types = mediaTypes(); if (types == null || types.isEmpty()) { return MediaType.ANY_TEXT_TYPE; } else if (types.size() == 1 && types.iterator().next().equals(MediaType.ANY_TYPE)) { return MediaType.ANY_TEXT_TYPE; } else { return types.iterator().next(); } } /** * Get the content media type that is acceptable for the client. E.g. Accept: text/*;q=0.3, text/html;q=0.7, * text/html;level=1,text/html;level=2;q=0.4 *

* The Accept request-header field can be used to specify certain media * types which are acceptable for the response. Accept headers can be used * to indicate that the request is specifically limited to a small set of * desired types, as in the case of a request for an in-line image. * * @return a MediaType that is acceptable for the * client or {@see MediaType#ANY_TEXT_TYPE} if not set * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ @Override public Collection mediaTypes() { String contentType = request.headers().get(HeaderNames.ACCEPT); if (contentType == null) { // Any text by default. return ImmutableList.of(MediaType.ANY_TEXT_TYPE); } TreeSet set = new TreeSet<>(new Comparator() { @Override public int compare(MediaType o1, MediaType o2) { double q1 = 1.0, q2 = 1.0; List ql1 = o1.parameters().get("q"); List ql2 = o2.parameters().get("q"); if (ql1 != null && !ql1.isEmpty()) { q1 = Double.parseDouble(ql1.get(0)); } if (ql2 != null && !ql2.isEmpty()) { q2 = Double.parseDouble(ql2.get(0)); } return new Double(q2).compareTo(q1); } }); // Split and sort. String[] segments = contentType.split(","); for (String segment : segments) { MediaType type = MediaType.parse(segment.trim()); set.add(type); } return set; } /** * Check if this request accepts a given media type. * * @return true if mimeType is in the Accept header, otherwise false */ @Override public boolean accepts(String mimeType) { String contentType = request.headers().get(HeaderNames.ACCEPT); if (contentType == null) { contentType = MimeTypes.HTML; } // For performance reason, we first try a full match: if (contentType.contains(mimeType)) { return true; } // Else check the media types: MediaType input = MediaType.parse(mimeType); for (MediaType type : mediaTypes()) { if (input.is(type)) { return true; } } return false; } /** * Gets the list of cookies. * * @return the request cookies */ @Override public Cookies cookies() { return cookies; } /** * Gets a cookie with the given name. * * @param name the cookie to retrieve * @return the cookie, {@code null} if no cookie have the given name */ public Cookie cookie(String name) { return cookies.get(name); } /** * Retrieves all headers. * * @return headers */ @Override public Map> headers() { if (headers != null) { return headers; } headers = new HashMap<>(); final MultiMap requestHeaders = request.headers(); Set names = requestHeaders.names(); for (String name : names) { headers.put(name, requestHeaders.getAll(name)); } return headers; } /** * Get the parameter with the given key from the request. The parameter may * either be a query parameter, or in the case of form submissions, may be a * form parameter. *

* When the parameter is multivalued, returns the first value. *

* The parameter is decoded by default. * * @param name The key of the parameter * @return The value, or null if no parameter was found. * @see #parameterMultipleValues */ @Override public String parameter(String name) { String s = request.params().get(name); if (s == null) { // Check form parameter if (formData != null) { List l = formData.get(name); if (l != null && !l.isEmpty()) { return l.get(0); } } return null; } else { return s; } } /** * Get the parameter with the given key from the request. The parameter may * either be a query parameter, or in the case of form submissions, may be a * form parameter. *

* The parameter is decoded by default. * * @param name The key of the parameter * @return The values, possibly an empty list. */ @Override public List parameterMultipleValues(String name) { return request.params().getAll(name); } /** * Same like {@link #parameter(String)}, but returns given defaultValue * instead of null in case parameter cannot be found. *

* The parameter is decoded by default. * * @param name The name of the post or query parameter * @param defaultValue A default value if parameter not found. * @return The value of the parameter of the defaultValue if not found. */ @Override public String parameter(String name, String defaultValue) { String v = request.params().get(name); return v != null ? v : defaultValue; } /** * Same like {@link #parameter(String)}, but converts the parameter to * Integer if found. *

* The parameter is decoded by default. * * @param name The name of the post or query parameter * @return The value of the parameter or null if not found. */ @Override public Integer parameterAsInteger(String name) { String parameter = parameter(name); try { return Integer.parseInt(parameter); } catch (Exception e) { //NOSONAR return null; } } /** * Like {@link #parameter(String, String)}, but converts the * parameter to Integer if found. *

* The parameter is decoded by default. * * @param name The name of the post or query parameter * @param defaultValue A default value if parameter not found. * @return The value of the parameter of the defaultValue if not found. */ @Override public Integer parameterAsInteger(String name, Integer defaultValue) { Integer parameter = parameterAsInteger(name); if (parameter == null) { return defaultValue; } return parameter; } /** * Like {@link #parameter(String)}, but converts the parameter to * Boolean if found. *

* The parameter is decoded by default. * * @param name The name of the post or query parameter * @return The value of the parameter or {@literal false} if not found. */ @Override public Boolean parameterAsBoolean(String name) { String parameter = parameter(name); try { return Boolean.parseBoolean(parameter); } catch (Exception e) { //NOSONAR return false; } } /** * Same like {@link #parameter(String)}, but converts the parameter to * Boolean if found. *

* The parameter is decoded by default. * * @param name The name of the post or query parameter * @param defaultValue A default value if parameter not found. * @return The value of the parameter or the defaultValue if not found. */ @Override public Boolean parameterAsBoolean(String name, boolean defaultValue) { // We have to check if the map contains the key, as the retrieval method returns false on missing key. if (!request.params().contains(name)) { return defaultValue; } Boolean parameter = parameterAsBoolean(name); if (parameter == null) { return defaultValue; } return parameter; } /** * Gets all the parameters from the request. * * @return The parameters */ @Override public Map> parameters() { Map> result = new HashMap<>(); for (String key : request.params().names()) { result.put(key, request.params().getAll(key)); } return result; } /** * Retrieves the data shared by all the entities participating to the request resolution (i.e. computation of the * response). This method returns a live map, meaning that modification impacts all other participants. It can be * used to let filters or interceptors passing objects to action methods or templates. * * @return the map storing the data. Unlike session or flash, these data are not stored in cookies, * and are cleared once the response is sent back to the client. */ @Override public Map data() { return data; } /** * Gets the underlying Vert.X Request. * * @return the request. */ public HttpServerRequest getVertxRequest() { return request; } /** * Gets the form data. * * @return the form data */ public Map> getFormData() { return formData; } /** * Gets the 'raw' body. * * @return the raw body, {@code null} if there is no body. */ public String getRawBodyAsString() { if (raw == null) { return null; } return raw.toString(Charsets.UTF_8.displayName()); } /** * Gets the 'raw' body. * * @return the raw body, {@code null} if there is no body. */ public byte[] getRawBody() { return raw.getBytes(); } /** * Gets the uploaded files. * * @return the list of uploaded files. */ public List getFiles() { return files; } /** * Callbacks invokes when the request has been read completely. * * @return a boolean indicating if the request was handled correctly. */ public boolean ready() { for (VertxFileUpload file : files) { if (file.getErrorIfAny() != null) { return false; } } String contentType = request.headers().get(HeaderNames.CONTENT_TYPE); if (contentType != null) { contentType = HttpUtils.getContentTypeFromContentTypeAndCharacterSetting(contentType); if ((HttpUtils.isPostOrPut(request)) && (contentType.equalsIgnoreCase(MimeTypes.FORM) || contentType.equalsIgnoreCase(MimeTypes.MULTIPART))) { formData = new HashMap<>(); for (String key : request.formAttributes().names()) { formData.put(key, request.formAttributes().getAll(key)); } return true; } } formData = new HashMap<>(); return true; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy