package ddth.dasp.servlet.rp; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import ddth.dasp.common.rp.AcfuUploadedFile; import ddth.dasp.common.rp.IRequestParser; import ddth.dasp.common.rp.MalformedRequestException; import ddth.dasp.common.rp.RequestParsingInteruptedException; import ddth.dasp.common.tempdir.TempDir; import ddth.dasp.common.utils.DaspConstants; import ddth.dasp.common.utils.ServletUtils; /** * This implementation of {@link IRequestParser} is used to parse a HTTP * request. * * @author NBThanh <btnguyen2k@gmail.com> */ public class HttpRequestParser extends AbstractRequestParser { private final static Pattern PATTERN_JSESSIONID = Pattern.compile(";jsessionid=[0-9a-f]+", Pattern.CASE_INSENSITIVE); private static Log LOGGER = LogFactory.getLog(HttpRequestParser.class); private HttpServletRequest httpRequest; private boolean isMultipart; private String contextPath; private String[] virtualPathParams; private Map<String, String> urlParams = new HashMap<String, String>(); private Map<String, Object> formFields = new HashMap<String, Object>(); private String requestUri; /** * Gets the associated HTTP request instance. * * @return HttpServletRequest */ public HttpServletRequest getHttpRequest() { return httpRequest; } /** * Associates the parser with a HTTP request instance. * * @param httpRequest * HttpServletRequeset */ public void setHttpRequest(HttpServletRequest httpRequest) { this.httpRequest = httpRequest; this.isMultipart = ServletFileUpload.isMultipartContent(httpRequest); } /** * Checks if the request is "multipart". * * @return */ public boolean isMultipart() { return isMultipart; } /** * {@inheritDoc} */ @Override public Map<String, Object> getFormFields() { return Collections.unmodifiableMap(formFields); } /** * {@inheritDoc} */ @Override public Object getFormField(String name) { return formFields.get(name); } /** * {@inheritDoc} */ @Override public String getRequestUri() { return requestUri; } /** * {@inheritDoc} */ @Override public String getRequestModule() { return getVirtualParameter(DaspConstants.PARAM_INDEX_MODULE); } /** * {@inheritDoc} */ public String getRequestAction() { return getVirtualParameter(DaspConstants.PARAM_INDEX_ACTION); } /** * {@inheritDoc} */ public String getRequestAuthKey() { return getVirtualParameter(DaspConstants.PARAM_INDEX_AUTHKEY); } /** * {@inheritDoc} */ @Override public Map<String, String> getUrlParameters() { return Collections.unmodifiableMap(urlParams); } /** * {@inheritDoc} */ @Override public String getUrlParameter(String name) { return urlParams.get(name); } /** * Gets an URL parameter as a Boolean. * * @param name * String * @return Boolean */ public Boolean getUrlParameterAsBoolean(String name) { String value = getUrlParameter(name); if (value == null) { return null; } value = value.trim(); // special cases (English!) if (value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true")) return true; if (value.equalsIgnoreCase("no") || value.equalsIgnoreCase("false")) return false; try { return Double.parseDouble(value) != 0; } catch (Exception e1) { try { return Boolean.parseBoolean(value); } catch (Exception e2) { return false; } } } /** * Gets an URL parameter as a Number. * * @param name * String * @return Number an instance of Number, null if the input can not be parsed */ public Double getUrlParameterAsNumber(String name) { String value = getUrlParameter(name); if (value == null) { return null; } value = value.trim(); try { return Double.parseDouble(value); } catch (Exception e) { return null; } } /** * {@inheritDoc} */ @Override public String[] getVirtualParameters() { return virtualPathParams; } /** * {@inheritDoc} */ @Override public String getVirtualParameter(int index) { try { return this.virtualPathParams[index]; } catch (Exception e) { // it must be either NullPointerException or // IndexOutOfBoundsException return null; } } /** * Gets a virtual parameter as a Boolean. * * @param index * int * @return Boolean */ public Boolean getVirtualParameterAsBoolean(int index) { String value = getVirtualParameter(index); if (value == null) { return null; } value = value.trim(); // special cases (English!) if (value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true")) return true; if (value.equalsIgnoreCase("no") || value.equalsIgnoreCase("false")) return false; try { return Double.parseDouble(value) != 0; } catch (Exception e1) { try { return Boolean.parseBoolean(value); } catch (Exception e2) { return false; } } } /** * Gets a virtual parameter as a Number. * * @param index * int * @return Number an instance of Number, null if the input can not be parsed */ public Number getVirtualParameterAsNumber(int index) { String value = getVirtualParameter(index); if (value == null) { return null; } value = value.trim(); try { return Double.parseDouble(value); } catch (Exception e) { return null; } } /** * {@inheritDoc} */ @Override protected void internalParseRequest() throws RequestParsingInteruptedException, MalformedRequestException { if (httpRequest == null) { throw new MalformedRequestException("The HTTP request is null!"); } this.contextPath = httpRequest.getContextPath(); if (this.contextPath == null) { this.contextPath = ""; } String encoding = httpRequest.getCharacterEncoding(); try { requestUri = ServletUtils.decodeURL(httpRequest.getRequestURI(), encoding); } catch (UnsupportedEncodingException e) { LOGGER.warn("Client suplied an unknown encoding: " + encoding, e); requestUri = ""; } // remove jsessionid if it exists requestUri = PATTERN_JSESSIONID.matcher(requestUri).replaceAll(""); if (this.contextPath.length() > 0) { requestUri = requestUri.substring(this.contextPath.length()); } this.virtualPathParams = requestUri.replaceAll("^/+", "").replaceAll("/+$", "").split("/"); if (virtualPathParams.length > 0) { requestUri = requestUri.substring(virtualPathParams[0].length() + 1); } String queryString; try { queryString = ServletUtils.decodeURL(httpRequest.getQueryString(), encoding); } catch (UnsupportedEncodingException e) { LOGGER.warn("Client suplied an unknown encoding: " + encoding, e); queryString = ""; } urlParams.clear(); String[] queries = queryString != null && queryString.trim().length() > 0 ? queryString .trim().split("&") : new String[0]; for (String query : queries) { String[] st = query.split("="); if (st.length > 0) { if (st.length > 1) { this.urlParams.put(st[0].trim(), st[1].trim()); } else { this.urlParams.put(st[0].trim(), ""); } } } parseRequestContent(); if (isInterrupted()) { throw new RequestParsingInteruptedException(); } } /** * Parses the request content. * * @throws MalformedRequestException */ protected void parseRequestContent() throws MalformedRequestException { this.formFields.clear(); if (this.isMultipart) { parseRequestContentMultipart(); } else { ServletInputStream sis = null; try { byte[] buffer = new byte[1024]; int counter = 0; int contentLength = httpRequest.getContentLength(); sis = httpRequest.getInputStream(); while (!isInterrupted() && counter < contentLength) { /* * Note: checking sis.available() is not working as expected * as the method returns 0 usually! */ int bytesRead = sis.read(buffer, 0, 1024); write(buffer, 0, bytesRead); counter += bytesRead; if (System.currentTimeMillis() - getStartParsingTimestamp() > getTimeout()) { interrupt(); } } } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { IOUtils.closeQuietly(sis); } // populate form fields if ("POST".equalsIgnoreCase(httpRequest.getMethod())) { String encoding = httpRequest.getCharacterEncoding(); if (StringUtils.isBlank(encoding)) { encoding = DaspConstants.DEFAULT_CHARSET; } try { String content = getRequestContent(encoding); List<NameValuePair> formFields = URLEncodedUtils.parse(content, Charset.forName(encoding)); for (NameValuePair formField : formFields) { this.formFields.put(formField.getName(), formField.getValue()); } } catch (UnsupportedEncodingException e) { throw new MalformedRequestException(e.getMessage(), e); } } } } protected void parseRequestContentMultipart() throws MalformedRequestException { String encoding = httpRequest.getCharacterEncoding(); if (StringUtils.isBlank(encoding)) { encoding = DaspConstants.DEFAULT_CHARSET; } TempDir tempDir = (TempDir) httpRequest .getAttribute(DaspConstants.REQ_ATTR_REQUEST_TEMP_DIR); FileItemFactory factory = new DiskFileItemFactory(8192, tempDir.get()); // DASP will remove temp files. ((DiskFileItemFactory) factory).setFileCleaningTracker(null); ServletFileUpload upload = new ServletFileUpload(factory); // TODO: control max upload file size try { List<?> items = upload.parseRequest(httpRequest); for (Object item : items) { FileItem fileItem = (FileItem) item; String fieldName = fileItem.getFieldName(); if (fileItem.isFormField()) { try { String fieldValue = fileItem.getString(encoding); this.formFields.put(fieldName, fieldValue); } catch (UnsupportedEncodingException e) { LOGGER.warn("Client suplied an unknown encoding: " + encoding, e); } } else { this.formFields.put(fieldName, new AcfuUploadedFile(fileItem)); } if (System.currentTimeMillis() - getStartParsingTimestamp() > getTimeout()) { interrupt(); } } } catch (FileUploadException fue) { throw new MalformedRequestException(fue.getMessage(), fue); } } }