package uws.service.request;
/*
* This file is part of UWSLibrary.
*
* UWSLibrary is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UWSLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014-2015 - Astronomisches Rechen Institut (ARI)
*/
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import javax.servlet.http.HttpServletRequest;
import uws.UWSException;
/**
* <p>Extract parameters encoded using the HTTP-GET method or the Content-type application/x-www-form-urlencoded
* with the HTTP-POST or HTTP-PUT method in an {@link HttpServletRequest}.</p>
*
* <p>
* By default, this {@link RequestParser} overwrite parameter occurrences in the map: that's to say if a parameter is provided several times,
* only the last value will be kept. This behavior can be changed by overwriting the function {@link #consumeParameter(String, Object, Map)}
* of this class.
* </p>
*
* <p><i>Note:
* When HTTP-POST is used, these parameters are actually already extracted by the server application (like Apache/Tomcat)
* and are available with {@link HttpServletRequest#getParameterMap()}.
* However, when using HTTP-PUT, the parameters are extracted manually from the request content.
* </i></p>
*
* @author Grégory Mantelet (ARI)
* @version 4.2 (07/2015)
* @since 4.1
*/
public class FormEncodedParser implements RequestParser {
/** HTTP content-type for HTTP request formated in url-form-encoded. */
public final static String EXPECTED_CONTENT_TYPE = "application/x-www-form-urlencoded";
@Override
public final Map<String,Object> parse(HttpServletRequest request) throws UWSException{
if (request == null)
return new HashMap<String,Object>();
HashMap<String,Object> params = new HashMap<String,Object>();
// Normal extraction for HTTP-POST and other HTTP methods:
if (request.getMethod() == null || !request.getMethod().equalsIgnoreCase("put")){
Enumeration<String> names = request.getParameterNames();
String paramName;
String[] values;
int i;
while(names.hasMoreElements()){
paramName = names.nextElement();
values = request.getParameterValues(paramName);
// search for the last non-null occurrence:
i = values.length - 1;
while(i >= 0 && values[i] == null)
i--;
// if there is one, keep it:
if (i >= 0)
consumeParameter(paramName, values[i], params);
}
}
/* Parameters are not extracted when using the HTTP-PUT method.
* This block is doing this extraction manually. */
else{
InputStream input = null;
try{
// Get the character encoding:
String charEncoding = request.getCharacterEncoding();
try{
if (charEncoding == null || charEncoding.trim().length() == 0 || Charset.isSupported(charEncoding))
charEncoding = "UTF-8";
}catch(Exception ex){
charEncoding = "UTF-8";
}
// Get a stream on the request content:
input = new BufferedInputStream(request.getInputStream());
// Read the stream by iterating on each parameter pairs:
Scanner scanner = new Scanner(input);
scanner.useDelimiter("&");
String pair;
int indSep;
while(scanner.hasNext()){
// get the pair:
pair = scanner.next();
// split it between the parameter name and value:
indSep = pair.indexOf('=');
try{
if (indSep >= 0)
consumeParameter(URLDecoder.decode(pair.substring(0, indSep), charEncoding), URLDecoder.decode(pair.substring(indSep + 1), charEncoding), params);
else
consumeParameter(URLDecoder.decode(pair, charEncoding), "", params);
}catch(UnsupportedEncodingException uee){
if (indSep >= 0)
consumeParameter(pair.substring(0, indSep), pair.substring(indSep + 1), params);
else
consumeParameter(pair, "", params);
}
}
}catch(IOException ioe){}finally{
if (input != null){
try{
input.close();
}catch(IOException ioe2){}
}
}
}
return params;
}
/**
* <p>Consume the specified parameter: add it inside the given map.</p>
*
* <p>
* By default, this function is just putting the given value inside the map. So, if the parameter already exists in the map,
* its old value will be overwritten by the given one.
* </p>
*
* @param name Name of the parameter to consume.
* @param value Its value.
* @param allParams The list of all parameters read until now.
*/
protected void consumeParameter(final String name, final Object value, final Map<String,Object> allParams){
allParams.put(name, value);
}
/**
* <p>Utility method that determines whether the content of the given request is a application/x-www-form-urlencoded.</p>
*
* <p><i>Important:
* This function just test the content-type of the request. The HTTP method (e.g. GET, POST, ...) is not tested.
* </i></p>
*
* @param request The servlet request to be evaluated. Must be non-null.
*
* @return <i>true</i> if the request is url-form-encoded,
* <i>false</i> otherwise.
*/
public final static boolean isFormEncodedRequest(final HttpServletRequest request){
// Extract the content type and determine if it is a url-form-encoded request:
String contentType = request.getContentType();
if (contentType == null)
return false;
else if (contentType.toLowerCase().startsWith(EXPECTED_CONTENT_TYPE))
return true;
else
return false;
}
}