-
gmantele authored
[UWS,TAP] 3 MAJOR DEPENDENT FIX: improve significantly the parameters extraction from HTTP request in UWS (1) AND move the file-upload ability into the UWS library (2) AND the modification of parameters in UWS is now conform with the standard (3). (1) Only application/x-form-urlencoded content-type was supported. However a UWS must accept a request body containing only an XML document as a single byReference parameter. It is now done when the content-type is not known. (2) Besides multipart/form-data is now fully supported in UWS and so is still possible in TAP. (3) In the UWS standard, parameters can not be added after creation: they can just be modified. This rule is now respected in the UWS library.
gmantele authored[UWS,TAP] 3 MAJOR DEPENDENT FIX: improve significantly the parameters extraction from HTTP request in UWS (1) AND move the file-upload ability into the UWS library (2) AND the modification of parameters in UWS is now conform with the standard (3). (1) Only application/x-form-urlencoded content-type was supported. However a UWS must accept a request body containing only an XML document as a single byReference parameter. It is now done when the content-type is not known. (2) Besides multipart/form-data is now fully supported in UWS and so is still possible in TAP. (3) In the UWS standard, parameters can not be added after creation: they can just be modified. This rule is now respected in the UWS library.
TAPRequestParser.java 8.51 KiB
package tap;
/*
* This file is part of TAPLibrary.
*
* TAPLibrary 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.
*
* TAPLibrary 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 TAPLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014 - Astronomisches Rechen Institut (ARI)
*/
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import uws.UWSException;
import uws.UWSToolBox;
import uws.service.file.UWSFileManager;
import uws.service.request.FormEncodedParser;
import uws.service.request.MultipartParser;
import uws.service.request.NoEncodingParser;
import uws.service.request.RequestParser;
import uws.service.request.UploadFile;
/**
* <p>This parser adapts the request parser to use in function of the request content-type:</p>
* <ul>
* <li><b>application/x-www-form-urlencoded</b>: {@link FormEncodedParser}</li>
* <li><b>multipart/form-data</b>: {@link MultipartParser}</li>
* <li><b>other</b>: {@link NoEncodingParser} (the whole request body will be stored as one single parameter)</li>
* </ul>
*
* <p>
* The request body size is limited for the multipart AND the no-encoding parsers. If you want to change this limit,
* you MUST do it for each of these parsers, setting the following static attributes: resp. {@link MultipartParser#SIZE_LIMIT}
* and {@link NoEncodingParser#SIZE_LIMIT}.
* </p>
*
* <p><i>Note:
* If you want to change the support other request parsing, you will have to write your own {@link RequestParser} implementation.
* </i></p>
*
* @author Grégory Mantelet (ARI)
* @version 2.0 (12/2014)
* @since 2.0
*/
public class TAPRequestParser implements RequestParser {
/** File manager to use to create {@link UploadFile} instances.
* It is required by this new object to execute open, move and delete operations whenever it could be asked. */
private final UWSFileManager fileManager;
/** {@link RequestParser} to use when a application/x-www-form-urlencoded request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
* only when needed, by calling the function {@link #getFormParser()}. */
private RequestParser formParser = null;
/** {@link RequestParser} to use when a multipart/form-data request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
* only when needed, by calling the function {@link #getMultipartParser()}. */
private RequestParser multipartParser = null;
/** {@link RequestParser} to use when none of the other parsers can be used ; it will then transform the whole request body in a parameter called "JDL"
* (Job Description Language). This attribute is set by {@link #parse(HttpServletRequest)} only when needed, by calling the function
* {@link #getNoEncodingParser()}. */
private RequestParser noEncodingParser = null;
/**
* Build a {@link RequestParser} able to choose the most appropriate {@link RequestParser} in function of the request content-type.
*
* @param fileManager The file manager to use in order to store any eventual upload. <b>MUST NOT be NULL</b>
*/
public TAPRequestParser(final UWSFileManager fileManager){
if (fileManager == null)
throw new NullPointerException("Missing file manager => can not create a TAPRequestParser!");
this.fileManager = fileManager;
}
@Override
public Map<String,Object> parse(final HttpServletRequest req) throws UWSException{
if (req == null)
return new HashMap<String,Object>();
// Get the method:
String method = (req.getMethod() == null) ? "" : req.getMethod().toLowerCase();
if (method.equals("post") || method.equals("put")){
Map<String,Object> params = null;
// Get the parameters:
if (FormEncodedParser.isFormEncodedRequest(req))
params = getFormParser().parse(req);
else if (MultipartParser.isMultipartContent(req))
params = getMultipartParser().parse(req);
else
params = getNoEncodingParser().parse(req);
// Only for POST requests, the parameters specified in the URL must be added:
if (method.equals("post"))
params = UWSToolBox.addGETParameters(req, (params == null) ? new HashMap<String,Object>() : params);
return params;
}else
return UWSToolBox.addGETParameters(req, new HashMap<String,Object>());
}
/**
* Get the {@link RequestParser} to use for application/x-www-form-urlencoded HTTP requests.
* This parser may be created if not already done.
*
* @return The {@link RequestParser} to use for application/x-www-form-urlencoded requests. <i>Never NULL</i>
*/
private synchronized final RequestParser getFormParser(){
return (formParser != null) ? formParser : (formParser = new FormEncodedParser(){
@Override
protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
// Modify the value if it is an UPLOAD parameter:
if (name != null && name.equalsIgnoreCase("upload")){
// if no value, ignore this parameter:
if (value == null)
return;
// put in lower case the parameter name:
name = name.toLowerCase();
// transform the value in a String array:
value = append((String)value, (allParams.containsKey("upload") ? (String[])allParams.get("upload") : null));
}
// Update the map, normally:
super.consumeParameter(name, value, allParams);
}
});
}
/**
* Get the {@link RequestParser} to use for multipart/form-data HTTP requests.
* This parser may be created if not already done.
*
* @return The {@link RequestParser} to use for multipart/form-data requests. <i>Never NULL</i>
*/
private synchronized final RequestParser getMultipartParser(){
return (multipartParser != null) ? multipartParser : (multipartParser = new MultipartParser(fileManager){
@Override
protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
// Modify the value if it is an UPLOAD parameter:
if (name != null && name.equalsIgnoreCase(TAPJob.PARAM_UPLOAD)){
// if no value, ignore this parameter:
if (value == null)
return;
// ignore also parameter having the same name in the same case and which is a file (only strings can be processed as DALI UPLOAD parameter):
else if (name.equals(TAPJob.PARAM_UPLOAD) && value instanceof UploadFile){
try{
((UploadFile)value).deleteFile();
}catch(IOException ioe){}
return;
}
// use the same case for the parameter name:
name = TAPJob.PARAM_UPLOAD;
// transform the value in a String array:
value = append((String)value, (allParams.containsKey(TAPJob.PARAM_UPLOAD) ? (String[])allParams.get(TAPJob.PARAM_UPLOAD) : null));
}
// Update the map, normally:
super.consumeParameter(name, value, allParams);
}
});
}
/**
* Get the {@link RequestParser} to use for HTTP requests whose the content type is neither application/x-www-form-urlencoded nor multipart/form-data.
* This parser may be created if not already done.
*
* @return The {@link RequestParser} to use for requests whose the content-type is not supported. <i>Never NULL</i>
*/
private synchronized final RequestParser getNoEncodingParser(){
return (noEncodingParser == null) ? (noEncodingParser = new NoEncodingParser(fileManager)) : noEncodingParser;
}
/**
* Create a new array in which the given String is appended at the end of the given array.
*
* @param value String to append in the array.
* @param oldValue The array after which the given String must be appended.
*
* @return The new array containing the values of the array and then the given String.
*/
private final static String[] append(final String value, final String[] oldValue){
// Create the corresponding array of Strings:
// ...if the array already exists, extend it:
String[] newValue;
if (oldValue != null){
newValue = new String[oldValue.length + 1];
for(int i = 0; i < oldValue.length; i++)
newValue[i] = oldValue[i];
}
// ...otherwise, create a new array:
else
newValue = new String[1];
// Add the new value in the array:
newValue[newValue.length - 1] = value;
// Update the value to put inside the map:
return newValue;
}
}