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

play.data.parsing.UrlEncodedParser Maven / Gradle / Ivy

There is a newer version: 2.6.3
Show newest version
package play.data.parsing;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Play;
import play.exceptions.UnexpectedException;
import play.mvc.Http;
import play.mvc.results.Status;
import play.utils.Utils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.apache.commons.io.IOUtils.toByteArray;

/**
 * Parse url-encoded requests.
 */
public class UrlEncodedParser extends DataParser {
    private static final Logger logger = LoggerFactory.getLogger(UrlEncodedParser.class);

    // Sets the maximum count of accepted POST params - protection against Hash collision DOS attacks
    private static final int maxParams = Integer.parseInt(Play.configuration.getProperty("http.maxParams", "1000")); // 0 == no limit
    
    boolean forQueryString;
    
    public static Map parse(String urlEncoded, Charset encoding) {
        return new UrlEncodedParser().parse(new ByteArrayInputStream(urlEncoded.getBytes( encoding )), encoding);
    }
    
    public static Map parseQueryString(InputStream is, Charset encoding) {
        UrlEncodedParser parser = new UrlEncodedParser();
        parser.forQueryString = true;
        return parser.parse(is, encoding);
    }

    @Override
    public Map parse(Http.Request request) {
        return parse(request.body, request.encoding);
    }

    public Map parse(InputStream is, Charset encoding) {
        try {
            Map params = new LinkedHashMap<>();
            String data = new String(toByteArray(is), encoding);
            if (data.isEmpty()) {
                //data is empty - can skip the rest
                return new HashMap<>(0);
            }

            // data is o the form:
            // a=b&b=c%12...

            // Let us parse in two phases - we wait until everything is parsed before
            // we decoded it - this makes it possible for use to look for the
            // special _charset_ param which can hold the charset the form is encoded in.
            //
            // http://www.crazysquirrel.com/computing/general/form-encoding.jspx
            // https://bugzilla.mozilla.org/show_bug.cgi?id=18643
            //
            // NB: _charset_ must always be used with accept-charset and it must have the same value

            String[] keyValues = data.split("&");


            // to prevent the Play-server from being vulnerable to POST hash collision DOS-attack (Denial of Service through hash table multi-collisions),
            // we should by default not parse the params into HashMap if the count exceeds a maximum limit
            if(maxParams != 0 && keyValues.length > maxParams) {
                logger.warn("Number of request parameters {} is higher than maximum of {}, aborting. Can be configured using 'http.maxParams'", keyValues.length, maxParams);
                throw new Status(413); //413 Request Entity Too Large
            }

            for (String keyValue : keyValues) {
                // split this key-value on the first '='
                int i = keyValue.indexOf('=');
                String key;
                String value = null;
                if ( i > 0) {
                    key = keyValue.substring(0,i);
                    value = keyValue.substring(i+1);
                } else {
                    key = keyValue;
                }
                if (!key.isEmpty()) {
                    Utils.Maps.mergeValueInMap(params, key, value);
                }
            }

            // Second phase - look for _charset_ param and do the encoding
            Charset charset = encoding;
            if (params.containsKey("_charset_")) {
                // The form contains a _charset_ param - When this is used together
                // with accept-charset, we can use _charset_ to extract the encoding.
                // PS: When rendering the view/form, _charset_ and accept-charset must be given the
                // same value - since only Firefox and sometimes IE actually sets it when Posting
                String providedCharset = params.get("_charset_")[0];
                // Must be sure the providedCharset is a valid encoding..
                try {
                    "test".getBytes(providedCharset);
                    charset = Charset.forName(providedCharset); // it works..
                } catch (Exception e) {
                    logger.debug("Got invalid _charset_ in form: {}", providedCharset, e);
                    // lets just use the default one..
                }
            }

            // We're ready to decode the params
            Map decodedParams = new LinkedHashMap<>(params.size());
            for (Map.Entry e : params.entrySet()) {
                String key = e.getKey();
                try {
                    key = URLDecoder.decode(e.getKey(), charset);
                } catch (Throwable z) {
                    // Nothing we can do about, ignore
                }
                for (String value : e.getValue()) {
                    try {
                        Utils.Maps.mergeValueInMap(decodedParams, key, (value == null ? null : URLDecoder.decode(value, charset)));
                    } catch (Throwable z) {
                        // Nothing we can do about, lets fill in with the non decoded value
                        Utils.Maps.mergeValueInMap(decodedParams, key, value);
                    }
                }
            }

            // add the complete body as a parameters
            if (!forQueryString) {
                decodedParams.put("body", new String[] {data});
            }

            return decodedParams;
        } catch (Status s) {
            // just pass it along
            throw s;
        } catch (Exception e) {
            throw new UnexpectedException(e);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy