diff options
Diffstat (limited to 'bkucommon/src/main/java/at/gv/egiz/bku/binding')
8 files changed, 748 insertions, 393 deletions
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java index 1db8c836..d3945253 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java @@ -16,7 +16,6 @@ */ package at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configuration; import at.gv.egiz.bku.conf.Configurator; import java.net.MalformedURLException; import java.net.URL; @@ -89,13 +88,7 @@ public class DataUrl { if (configuration != null) { String className = configuration.getProperty(Configurator.DATAURLCONNECTION_CONFIG_P); if (className != null) { - try { - log.info("set DataURLConnection class: " + className); - Class c = Class.forName(className); - connection = (DataUrlConnectionSPI) c.newInstance(); - } catch (Exception ex) { - log.error("failed to instantiate DataURL connection " + className, ex); - } + log.warn("Set DataURLConnection class not supported!"); } } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java index f954a017..384cf71c 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java @@ -62,7 +62,7 @@ public interface DataUrlConnection { * @param transferEncoding may be null */ public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding); - + /** * @pre httpHeaders != null * @throws java.net.SocketTimeoutException diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java index 4f2d2e00..b092ba41 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java @@ -18,10 +18,14 @@ package at.gv.egiz.bku.binding; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.Charset; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; @@ -34,6 +38,7 @@ import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; +import javax.xml.transform.stream.StreamResult; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.Part; @@ -47,32 +52,92 @@ import at.gv.egiz.bku.conf.Configurator; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slcommands.SLResult.SLResultType; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.utils.URLEncodingWriter; import at.gv.egiz.bku.utils.binding.Protocol; /** - * not thread-safe thus newInsance always returns a new object + * An implementation of the DataUrlConnectionSPI that supports + * <code>multipart/form-data</code> encoding and + * <code>application/x-www-form-urlencoded</code> for compatibility with legacy + * systems. * */ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { private final static Log log = LogFactory.getLog(DataUrlConnectionImpl.class); + + public static final byte[] B_DEFAULT_RESPONSETYPE = DEFAULT_RESPONSETYPE.getBytes(Charset.forName("UTF-8")); + /** + * Supported protocols are HTTP and HTTPS. + */ public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, Protocol.HTTPS }; + /** + * The X509 certificate of the DataURL server. + */ protected X509Certificate serverCertificate; + + /** + * The protocol of the DataURL. + */ protected Protocol protocol; + + /** + * Use <code>application/x-www-form-urlencoded</code> instead of + * standard conform <code>application/x-www-form-urlencoded</code>. + */ + protected boolean urlEncoded = true; + + /** + * The value of the DataURL. + */ protected URL url; + + /** + * The URLConnection used for communication with the DataURL server. + */ private HttpURLConnection connection; + + /** + * The HTTP request headers. + */ protected Map<String, String> requestHttpHeaders; - protected ArrayList<Part> formParams; + + /** + * The HTTP form parameters. + */ + protected ArrayList<HTTPFormParameter> httpFormParameter; + + /** + * The boundary for multipart/form-data requests. + */ protected String boundary; + + /** + * The configuration properties. + */ protected Properties config = null; + + /** + * The SSLSocketFactory for HTTPS connections. + */ protected SSLSocketFactory sslSocketFactory; + + /** + * The HostnameVerifier for HTTPS connections. + */ protected HostnameVerifier hostnameVerifier; + /** + * The response of the DataURL server. + */ protected DataUrlResponse result; + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#getProtocol() + */ public String getProtocol() { if (protocol == null) { return null; @@ -80,13 +145,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { return protocol.toString(); } - /** - * opens a connection sets the headers gets the server certificate - * - * @throws java.net.SocketTimeoutException - * @throws java.io.IOException - * @pre url != null - * @pre httpHeaders != null + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#connect() */ public void connect() throws SocketTimeoutException, IOException { connection = (HttpURLConnection) url.openConnection(); @@ -104,9 +164,26 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { https.setHostnameVerifier(hostnameVerifier); } } else { - log.trace("No secure connection with: "+url+ " class="+connection.getClass()); + log.trace("No secure connection with: " + url + " class=" + + connection.getClass()); } connection.setDoOutput(true); + // Transfer-Encoding: chunked is problematic ... + // e.g. https://issues.apache.org/bugzilla/show_bug.cgi?id=37794 + // ... therefore disabled. + // connection.setChunkedStreamingMode(5*1024); + if (urlEncoded) { + log.debug("Setting DataURL Content-Type to " + + HttpUtil.APPLICATION_URL_ENCODED); + connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, + HttpUtil.APPLICATION_URL_ENCODED); + } else { + log.debug("Setting DataURL Content-Type to " + + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY); + connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, + HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0] + + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary); + } Set<String> headers = requestHttpHeaders.keySet(); Iterator<String> headerIt = headers.iterator(); while (headerIt.hasNext()) { @@ -125,51 +202,128 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#getServerCertificate() + */ public X509Certificate getServerCertificate() { return serverCertificate; } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPHeader(java.lang.String, java.lang.String) + */ public void setHTTPHeader(String name, String value) { if (name != null && value != null) { requestHttpHeaders.put(name, value); } } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPFormParameter(java.lang.String, java.io.InputStream, java.lang.String, java.lang.String, java.lang.String) + */ public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding) { - InputStreamPartSource source = new InputStreamPartSource(null, data); - FilePart formParam = new FilePart(name, source, contentType, charSet); - if (transferEncoding != null) { - formParam.setTransferEncoding(transferEncoding); - } else { - formParam.setTransferEncoding(null); + // if a content type is specified we have to switch to multipart/formdata encoding + if (contentType != null && contentType.length() > 0) { + urlEncoded = false; } - formParams.add(formParam); + httpFormParameter.add(new HTTPFormParameter(name, data, contentType, + charSet, transferEncoding)); } - /** - * send all formParameters - * - * @throws java.io.IOException + + + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#transmit(at.gv.egiz.bku.slcommands.SLResult) */ public void transmit(SLResult slResult) throws IOException { - SLResultPart slResultPart = new SLResultPart(slResult, - XML_RESPONSE_ENCODING); - if (slResult.getResultType() == SLResultType.XML) { - slResultPart.setTransferEncoding(null); - slResultPart.setContentType(slResult.getMimeType()); - slResultPart.setCharSet(XML_RESPONSE_ENCODING); + log.trace("Sending data"); + if (urlEncoded) { + // + // application/x-www-form-urlencoded (legacy, SL < 1.2) + // + + OutputStream os = connection.getOutputStream(); + OutputStreamWriter streamWriter = new OutputStreamWriter(os, HttpUtil.DEFAULT_CHARSET); + + // ResponseType + streamWriter.write(FORMPARAM_RESPONSETYPE); + streamWriter.write("="); + streamWriter.write(URLEncoder.encode(DEFAULT_RESPONSETYPE, "UTF-8")); + streamWriter.write("&"); + + // XMLResponse / Binary Response + if (slResult.getResultType() == SLResultType.XML) { + streamWriter.write(DataUrlConnection.FORMPARAM_XMLRESPONSE); + } else { + streamWriter.write(DataUrlConnection.FORMPARAM_BINARYRESPONSE); + } + streamWriter.write("="); + streamWriter.flush(); + URLEncodingWriter urlEnc = new URLEncodingWriter(streamWriter); + slResult.writeTo(new StreamResult(urlEnc), false); + urlEnc.flush(); + + // transfer parameters + char[] cbuf = new char[512]; + int len; + for (HTTPFormParameter formParameter : httpFormParameter) { + streamWriter.write("&"); + streamWriter.write(URLEncoder.encode(formParameter.getName(), "UTF-8")); + streamWriter.write("="); + InputStreamReader reader = new InputStreamReader(formParameter.getData(), + (formParameter.getCharSet() != null) + ? formParameter.getCharSet() + : null); + while ((len = reader.read(cbuf)) != -1) { + urlEnc.write(cbuf, 0, len); + } + urlEnc.flush(); + } + streamWriter.close(); + } else { - slResultPart.setTransferEncoding(null); - slResultPart.setContentType(slResult.getMimeType()); - } - formParams.add(slResultPart); + // + // multipart/form-data (conforming to SL 1.2) + // - OutputStream os = connection.getOutputStream(); - log.trace("Sending data"); - Part[] parts = new Part[formParams.size()]; - Part.sendParts(os, formParams.toArray(parts), boundary.getBytes()); - os.close(); + ArrayList<Part> parts = new ArrayList<Part>(); + + // ResponseType + StringPart responseType = new StringPart(FORMPARAM_RESPONSETYPE, + DEFAULT_RESPONSETYPE, "UTF-8"); + responseType.setTransferEncoding(null); + parts.add(responseType); + + // XMLResponse / Binary Response + SLResultPart slResultPart = new SLResultPart(slResult, + XML_RESPONSE_ENCODING); + if (slResult.getResultType() == SLResultType.XML) { + slResultPart.setTransferEncoding(null); + slResultPart.setContentType(slResult.getMimeType()); + slResultPart.setCharSet(XML_RESPONSE_ENCODING); + } else { + slResultPart.setTransferEncoding(null); + slResultPart.setContentType(slResult.getMimeType()); + } + parts.add(slResultPart); + + // transfer parameters + for (HTTPFormParameter formParameter : httpFormParameter) { + InputStreamPartSource source = new InputStreamPartSource(null, + formParameter.getData()); + FilePart part = new FilePart(formParameter.getName(), source, + formParameter.getContentType(), formParameter.getCharSet()); + part.setTransferEncoding(formParameter.getTransferEncoding()); + parts.add(part); + } + + OutputStream os = connection.getOutputStream(); + Part.sendParts(os, parts.toArray(new Part[parts.size()]), boundary.getBytes()); + os.close(); + + } + // MultipartRequestEntity PostMethod InputStream is = null; try { @@ -241,16 +395,9 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT); } - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, - HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0] - + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary); - - formParams = new ArrayList<Part>(); - StringPart responseType = new StringPart(FORMPARAM_RESPONSETYPE, - DEFAULT_RESPONSETYPE); - responseType.setCharSet("UTF-8"); - responseType.setTransferEncoding(null); - formParams.add(responseType); + + httpFormParameter = new ArrayList<HTTPFormParameter>(); + } @Override @@ -281,4 +428,107 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; } + + public class HTTPFormParameter { + + private String name; + + private InputStream data; + + private String contentType; + + private String charSet; + + private String transferEncoding; + + /** + * @param name + * @param data + * @param contentType + * @param charSet + * @param transferEncoding + */ + public HTTPFormParameter(String name, InputStream data, String contentType, + String charSet, String transferEncoding) { + super(); + this.name = name; + this.data = data; + this.contentType = contentType; + this.charSet = charSet; + this.transferEncoding = transferEncoding; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the data + */ + public InputStream getData() { + return data; + } + + /** + * @param data the data to set + */ + public void setData(InputStream data) { + this.data = data; + } + + /** + * @return the contentType + */ + public String getContentType() { + return contentType; + } + + /** + * @param contentType the contentType to set + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * @return the charSet + */ + public String getCharSet() { + return charSet; + } + + /** + * @param charSet the charSet to set + */ + public void setCharSet(String charSet) { + this.charSet = charSet; + } + + /** + * @return the transferEncoding + */ + public String getTransferEncoding() { + return transferEncoding; + } + + /** + * @param transferEncoding the transferEncoding to set + */ + public void setTransferEncoding(String transferEncoding) { + this.transferEncoding = transferEncoding; + } + + + + } }
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java index ef603fc7..a1c4d5fc 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java @@ -22,6 +22,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; +import java.io.Writer; import java.net.URL; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -46,6 +47,7 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.bku.slcommands.ErrorResult; import at.gv.egiz.bku.slcommands.SLCommand; import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLCommandFactory; @@ -635,7 +637,6 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements throw new SLBindingException(2006); } InputDecoder id = InputDecoderFactory.getDecoder(cl, is); - id.setContentType(cl); if (id == null) { log.error("Cannot get inputdecoder for is"); throw new SLException(2006); @@ -730,9 +731,20 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements Templates templates) throws IOException { log.debug("Writing error as result"); ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError, locale); - error.writeTo(new StreamResult(new OutputStreamWriter(os, encoding)), templates); + Writer writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + error.writeTo(new StreamResult(writer), templates, true); } + protected Writer writeXMLDeclarationAndProcessingInstruction(OutputStream os, String encoding) throws IOException { + if (encoding == null) { + encoding = HttpUtil.DEFAULT_CHARSET; + } + OutputStreamWriter writer = new OutputStreamWriter(os, encoding); + writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"); + writer.write("<?xml-stylesheet type=\"text/css\" href=\"errorresponse.css\"?>\n"); + return writer; + } + @Override public void writeResultTo(OutputStream os, String encoding) throws IOException { @@ -772,9 +784,16 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements return; } else { log.debug("Getting result from invoker"); - OutputStreamWriter osw = new OutputStreamWriter(os, encoding); - slResult.writeTo(new StreamResult(osw), templates); - osw.flush(); + boolean fragment = false; + Writer writer; + if (slResult instanceof ErrorResult) { + writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + fragment = true; + } else { + writer = new OutputStreamWriter(os, encoding); + } + slResult.writeTo(new StreamResult(writer), templates, fragment); + writer.flush(); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java deleted file mode 100644 index cfccb7f1..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java +++ /dev/null @@ -1,259 +0,0 @@ -package at.gv.egiz.bku.binding; - - -import at.gv.egiz.bku.conf.Configurator; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLEncoder; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; -import javax.xml.transform.stream.StreamResult; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.slcommands.SLResult; -import at.gv.egiz.bku.slcommands.SLResult.SLResultType; -import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.bku.utils.binding.Protocol; - -/** - * not thread-safe thus newInsance always returns a new object - * - */ -public class LegacyDataUrlConnectionImpl implements DataUrlConnectionSPI { - - private final static Log log = LogFactory.getLog(LegacyDataUrlConnectionImpl.class); - - public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, - Protocol.HTTPS }; - protected X509Certificate serverCertificate; - protected Protocol protocol; - protected URL url; - private HttpURLConnection connection; - protected Map<String, String> requestHttpHeaders; - protected Map<String, String> formParams; - protected String boundary; - protected Properties config = null; - protected SSLSocketFactory sslSocketFactory; - protected HostnameVerifier hostnameVerifier; - - protected DataUrlResponse result; - - public String getProtocol() { - if (protocol == null) { - return null; - } - return protocol.toString(); - } - - /** - * opens a connection sets the headers gets the server certificate - * - * @throws java.net.SocketTimeoutException - * @throws java.io.IOException - * @pre url != null - * @pre httpHeaders != null - */ - public void connect() throws SocketTimeoutException, IOException { - connection = (HttpURLConnection) url.openConnection(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection https = (HttpsURLConnection) connection; - if (sslSocketFactory != null) { - log.debug("Setting custom ssl socket factory for ssl connection"); - https.setSSLSocketFactory(sslSocketFactory); - } - if (hostnameVerifier != null) { - log.debug("Setting custom hostname verifier"); - https.setHostnameVerifier(hostnameVerifier); - } - } - connection.setDoOutput(true); - Set<String> headers = requestHttpHeaders.keySet(); - Iterator<String> headerIt = headers.iterator(); - while (headerIt.hasNext()) { - String name = headerIt.next(); - connection.setRequestProperty(name, requestHttpHeaders.get(name)); - } - log.trace("Connecting to: "+url); - connection.connect(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection ssl = (HttpsURLConnection) connection; - X509Certificate[] certs = (X509Certificate[]) ssl.getServerCertificates(); - if ((certs != null) && (certs.length >= 1)) { - log.trace("Server certificate: "+certs[0]); - serverCertificate = certs[0]; - } - } - } - - public X509Certificate getServerCertificate() { - return serverCertificate; - } - - public void setHTTPHeader(String name, String value) { - if (name != null && value != null) { - requestHttpHeaders.put(name, value); - } - } - - public void setHTTPFormParameter(String name, InputStream data, - String contentType, String charSet, String transferEncoding) { - StringBuilder sb = new StringBuilder(); - try { - InputStreamReader reader = new InputStreamReader(data, (charSet != null) ? charSet : "UTF-8"); - char[] c = new char[512]; - for (int l; (l = reader.read(c)) != -1;) { - sb.append(c, 0, l); - } - } catch (IOException e) { - throw new SLRuntimeException("Failed to set HTTP form parameter.", e); - } - formParams.put(name, sb.toString()); - } - - /** - * send all formParameters - * - * @throws java.io.IOException - */ - public void transmit(SLResult slResult) throws IOException { - StringWriter writer = new StringWriter(); - slResult.writeTo(new StreamResult(writer)); - formParams.put( - (slResult.getResultType() == SLResultType.XML) - ? DataUrlConnection.FORMPARAM_XMLRESPONSE - : DataUrlConnection.FORMPARAM_BINARYRESPONSE, - writer.toString()); - - OutputStream os = connection.getOutputStream(); - OutputStreamWriter streamWriter = new OutputStreamWriter(os, HttpUtil.DEFAULT_CHARSET); - - log.trace("Sending data"); - Iterator<String> keys = formParams.keySet().iterator(); - while(keys.hasNext()) { - String key = keys.next(); - streamWriter.write(URLEncoder.encode(key, "UTF-8")); - streamWriter.write("="); - streamWriter.write(URLEncoder.encode(formParams.get(key), "UTF-8")); - if (keys.hasNext()) { - streamWriter.write("&"); - } - } - streamWriter.flush(); - os.close(); - - // MultipartRequestEntity PostMethod - InputStream is = null; - try { - is = connection.getInputStream(); - } catch (IOException iox) { - log.info(iox); - } - log.trace("Reading response"); - result = new DataUrlResponse(url.toString(), connection.getResponseCode(), is); - Map<String, String> responseHttpHeaders = new HashMap<String, String>(); - Map<String, List<String>> httpHeaders = connection.getHeaderFields(); - for (Iterator<String> keyIt = httpHeaders.keySet().iterator(); keyIt - .hasNext();) { - String key = keyIt.next(); - StringBuffer value = new StringBuffer(); - for (String val : httpHeaders.get(key)) { - value.append(val); - value.append(HttpUtil.SEPERATOR[0]); - } - String valString = value.substring(0, value.length() - 1); - if ((key != null) && (value.length() > 0)) { - responseHttpHeaders.put(key, valString); - } - } - result.setResponseHttpHeaders(responseHttpHeaders); - } - - @Override - public DataUrlResponse getResponse() throws IOException { - return result; - } - - /** - * inits protocol, url, httpHeaders, formParams - * - * @param url - * must not be null - */ - @Override - public void init(URL url) { - - for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) { - if (SUPPORTED_PROTOCOLS[i].toString().equalsIgnoreCase(url.getProtocol())) { - protocol = SUPPORTED_PROTOCOLS[i]; - break; - } - } - if (protocol == null) { - throw new SLRuntimeException("Protocol " + url.getProtocol() - + " not supported for data url"); - } - this.url = url; - requestHttpHeaders = new HashMap<String, String>(); - if ((config != null) - && (config.getProperty(Configurator.USERAGENT_CONFIG_P) != null)) { - log.debug("setting User-Agent header: " + config.getProperty(Configurator.USERAGENT_CONFIG_P)); - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_USER_AGENT, config - .getProperty(Configurator.USERAGENT_CONFIG_P)); - } else { - requestHttpHeaders - .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT); - - } - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, - HttpUtil.APPLICATION_URL_ENCODED); - - formParams = new HashMap<String, String>(); - } - - @Override - public DataUrlConnectionSPI newInstance() { - DataUrlConnectionSPI uc = new LegacyDataUrlConnectionImpl(); - uc.setConfiguration(config); - uc.setSSLSocketFactory(sslSocketFactory); - uc.setHostnameVerifier(hostnameVerifier); - return uc; - } - - @Override - public URL getUrl() { - return url; - } - - @Override - public void setConfiguration(Properties config) { - this.config = config; - } - - @Override - public void setSSLSocketFactory(SSLSocketFactory socketFactory) { - this.sslSocketFactory = socketFactory; - } - - @Override - public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { - this.hostnameVerifier = hostnameVerifier; - } -}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java index f4ebe288..69c659e1 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java @@ -16,86 +16,43 @@ */ package at.gv.egiz.bku.binding;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URLDecoder;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.fileupload.ParameterParser; -import org.apache.commons.fileupload.ParameterParser;
-
-import at.gv.egiz.bku.slexceptions.SLRuntimeException;
-import at.gv.egiz.bku.utils.StreamUtil;
-
-/**
- * Implementation based on Java's URLDecoder class
- *
- */
-// FIXME replace this code by a streaming variant
public class XWWWFormUrlInputDecoder implements InputDecoder {
-
- public final static String CHAR_SET = "charset";
- public final static String NAME_VAL_SEP = "=";
- public final static String SEP = "\\&";
-
- private String contentType;
- private InputStream dataStream;
- private String charset = "UTF-8";
-
- protected List<FormParameter> decodeInput(InputStream is) throws IOException {
- List<FormParameter> result = new LinkedList<FormParameter>();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- StreamUtil.copyStream(is, bos);
- String inputString = new String(bos.toByteArray());
- String[] nameValuePairs = inputString.split(SEP);
- //inputString = URLDecoder.decode(inputString, charset);
- for (int i = 0; i < nameValuePairs.length; i++) {
- String[] fields = nameValuePairs[i].split(NAME_VAL_SEP, 2);
- if (fields.length != 2) {
- throw new SLRuntimeException("Invalid form encoding, missing value");
- }
- String name = URLDecoder.decode(fields[0], charset);
- String value =URLDecoder.decode(fields[1], charset);
- ByteArrayInputStream bais = new ByteArrayInputStream(value
- .getBytes(charset));
- FormParameterImpl fpi = new FormParameterImpl(contentType, name, bais, null);
- result.add(fpi);
- }
- return result;
- }
-
- @SuppressWarnings("unchecked")
+ + /** + * The MIME type 'application/x-www-form-urlencoded'. + */ + public static final String CONTENT_TYPE = "application/x-www-form-urlencoded"; + + /** + * The form parameter iterator. + */ + protected XWWWFormUrlInputIterator iterator; +
+ @SuppressWarnings("unchecked") @Override
public void setContentType(String contentType) {
ParameterParser pp = new ParameterParser();
pp.setLowerCaseNames(true);
Map<String, String> params = pp.parse(contentType, new char[] { ':', ';' });
- if (!params.containsKey("application/x-www-form-urlencoded")) {
+ if (!params.containsKey(CONTENT_TYPE)) {
throw new IllegalArgumentException(
"not a url encoded content type specification: " + contentType);
}
- String cs = params.get(CHAR_SET);
- if (cs != null) {
- charset = cs;
- }
- this.contentType = contentType;
}
@Override
public Iterator<FormParameter> getFormParameterIterator() {
- try {
- return decodeInput(dataStream).iterator();
- } catch (IOException e) {
- throw new SLRuntimeException(e);
- }
+ return iterator;
}
@Override
public void setInputStream(InputStream is) {
- dataStream = is;
+ iterator = new XWWWFormUrlInputIterator(is);
}
}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java new file mode 100644 index 00000000..f052ce05 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java @@ -0,0 +1,376 @@ +package at.gv.egiz.bku.binding; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +public class XWWWFormUrlInputIterator implements Iterator<FormParameter> { + + public static final byte NAME_VALUE_SEP = '='; + + public static final byte PARAM_SEP = '&'; + + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + /** + * The default buffer size. + */ + protected static final int DEFAULT_BUFFER_SIZE = 4096; + + /** + * Are we done with parsing the input. + */ + protected boolean done = false; + + /** + * The x-www-formdata-urlencoded input stream to be parsed. + */ + protected final InputStream in; + + /** + * The buffer size. + */ + protected int bufferSize = DEFAULT_BUFFER_SIZE; + + /** + * The read buffer. + */ + protected final byte[] buf = new byte[bufferSize]; + + /** + * The read position. + */ + protected int pos; + + /** + * The number of valid bytes in the buffer; + */ + protected int count; + + /** + * The parameter returned by the last call of {@link #next()}; + */ + protected XWWWFormUrlEncodedParameter currentParameter; + + /** + * An IOException that cannot be reported immediately. + */ + protected IOException deferredIOException; + + /** + * Creates a new instance of this x-www-formdata-urlencoded input iterator + * with the given InputStream <code>in</code> to be parsed. + * + * @param in the InputStream to be parsed + */ + public XWWWFormUrlInputIterator(InputStream in) { + this.in = in; + } + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() { + if (done) { + return false; + } + if (currentParameter != null) { + // we have to disconnect the current parameter + // to look for further parameters + try { + currentParameter.formParameterValue.disconnect(); + // fill buffer if empty + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + // done + done = true; + return false; + } + pos = 0; + } + } catch (IOException e) { + deferredIOException = e; + } + } + return true; + } + + @Override + public FormParameter next() { + if (hasNext()) { + // skip separator + pos++; + currentParameter = new XWWWFormUrlEncodedParameter(); + return currentParameter; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + public class XWWWFormUrlEncodedParameter implements FormParameter { + + /** + * The list of header names. + */ + // x-www-form-urlencoded parameters do not provide headers + protected final List<String> headers = Collections.emptyList(); + + /** + * The name of the form parameter. + */ + protected String formParameterName; + + /** + * The value of the form parameter. + */ + protected URLDecodingInputStream formParameterValue; + + public XWWWFormUrlEncodedParameter() { + // parse parameter name + URLDecodingInputStream urldec = new URLDecodingInputStream(in, NAME_VALUE_SEP); + InputStreamReader reader = new InputStreamReader(urldec, UTF_8); + try { + StringBuilder sb = new StringBuilder(); + char[] b = new char[128]; + for (int l = 0; (l = reader.read(b)) != -1;) { + sb.append(b, 0, l); + } + formParameterName = sb.toString(); + // fill buffer if empty + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + // skip separator + pos++; + } catch (IOException e) { + deferredIOException = e; + formParameterName = ""; + } + formParameterValue = new URLDecodingInputStream(in, PARAM_SEP); + } + + @Override + public String getFormParameterContentType() { + // x-www-form-urlencoded parameters do not specify a content type + return null; + } + + @Override + public String getFormParameterName() { + return formParameterName; + } + + @Override + public InputStream getFormParameterValue() { + if (deferredIOException != null) { + final IOException e = deferredIOException; + deferredIOException = null; + return new InputStream() { + @Override + public int read() throws IOException { + throw e; + } + }; + } else { + return formParameterValue; + } + } + + @Override + public Iterator<String> getHeaderNames() { + return headers.iterator(); + } + + @Override + public String getHeaderValue(String headerName) { + return null; + } + + } + + public class URLDecodingInputStream extends FilterInputStream { + + /** + * Has this stream already been closed. + */ + private boolean closed = false; + + /** + * Has this stream been disconnected. + */ + private boolean disconnected = false; + + /** + * Read until this byte occurs. + */ + protected final byte term; + + /** + * Creates a new instance of this URLDecodingInputStream. + * + * @param in + * @param separator + */ + protected URLDecodingInputStream(InputStream in, byte separator) { + super(in); + this.term = separator; + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#read() + */ + @Override + public int read() throws IOException { + if (closed) { + throw new IOException("The stream has already been closed."); + } + if (disconnected) { + return in.read(); + } + + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + return -1; + } + pos = 0; + } if (buf[pos] == term) { + return -1; + } else if (buf[pos] == '+') { + pos++; + return ' '; + } else if (buf[pos] == '%') { + if (++pos == count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + int c1 = Character.digit(buf[pos], 16); + if (++pos == count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + int c2 = Character.digit(buf[pos], 16); + return ((c1 << 4) | c2); + } else { + return buf[pos++]; + } + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new IOException("The stream has already been closed."); + } + if (disconnected) { + return in.read(b, off, len); + } + + if ((off | len | (off + len) | (b.length - (off + len))) < 0) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + return -1; + } + pos = 0; + } + if (buf[pos] == term) { + return -1; + } + + int l = 0; + for (;;) { + while (pos < count) { + if (l == len || buf[pos] == term) { + return l; + } else if (buf[pos] == '+') { + b[off] = ' '; + } else if (buf[pos] == '%') { + if (++pos == count && (count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c1 = Character.digit(buf[pos], 16); + if (++pos == count && (count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c2 = Character.digit(buf[pos], 16); + b[off] = (byte) ((c1 << 4) | c2); + } else { + b[off] = buf[pos]; + } + pos++; + off++; + l++; + } + if ((count = in.read(buf)) == -1) { + return l; + } + pos = 0; + } + } + + /** + * Disconnect from the InputStream and buffer all remaining data. + * + * @throws IOException + */ + public void disconnect() throws IOException { + if (!disconnected) { + // don't waste space for a buffer if end of stream has already been + // reached + byte[] b = new byte[1]; + if ((read(b)) != -1) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write(b); + b = new byte[1024]; + for (int l; (l = read(b, 0, b.length)) != -1;) { + os.write(b, 0, l); + } + super.in = new ByteArrayInputStream(os.toByteArray()); + } + disconnected = true; + } + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#close() + */ + @Override + public void close() throws IOException { + if (!hasNext()) { + // don't close the underlying stream until all parts are read + super.close(); + } + disconnect(); + closed = true; + } + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java index 5585f02e..d896ea9f 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java @@ -16,37 +16,56 @@ */ package at.gv.egiz.bku.binding.multipart; +import at.gv.egiz.bku.binding.DataUrlConnection; import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slcommands.SLResult.SLResultType; + import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import javax.xml.transform.stream.StreamResult; -import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource; import org.apache.commons.httpclient.methods.multipart.FilePart; +import org.apache.commons.httpclient.methods.multipart.PartSource; -/** - * - * @author clemens - */ public class SLResultPart extends FilePart { protected SLResult slResult; protected String encoding; public SLResultPart(SLResult slResult, String encoding) { - super("XMLResponse", - new ByteArrayPartSource(null, "dummySource".getBytes())); + super((slResult.getResultType() == SLResultType.XML) + ? DataUrlConnection.FORMPARAM_XMLRESPONSE + : DataUrlConnection.FORMPARAM_BINARYRESPONSE, + new PartSource() { + + @Override + public long getLength() { + // may return null, as sendData() is overridden + return 0; + } + + @Override + public String getFileName() { + // return null, to prevent content-disposition header + return null; + } + + @Override + public InputStream createInputStream() throws IOException { + // may return null, as sendData() is overridden below + return null; + } + } + ); this.slResult = slResult; this.encoding = encoding; } @Override protected void sendData(OutputStream out) throws IOException { - slResult.writeTo(new StreamResult(new OutputStreamWriter(out, encoding))); - // slResult.writeTo(new StreamResult(new OutputStreamWriter(System.out, - // encoding))); - // super.sendData(out); + slResult.writeTo(new StreamResult(new OutputStreamWriter(out, encoding)), false); } } |