summaryrefslogtreecommitdiff
path: root/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java')
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java539
1 files changed, 539 insertions, 0 deletions
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
new file mode 100644
index 00000000..82c1be53
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+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;
+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.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.StringPart;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.bku.binding.multipart.InputStreamPartSource;
+import at.gv.egiz.bku.binding.multipart.SLResultPart;
+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;
+
+/**
+ * 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;
+
+ /**
+ * 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;
+ }
+ return protocol.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.bku.binding.DataUrlConnection#connect()
+ */
+ public void connect() throws SocketTimeoutException, IOException {
+ connection = (HttpURLConnection) url.openConnection();
+ if (connection instanceof HttpsURLConnection) {
+ log.trace("Detected ssl connection");
+ HttpsURLConnection https = (HttpsURLConnection) connection;
+ if (sslSocketFactory != null) {
+ log.debug("Setting custom ssl socket factory for ssl connection");
+ https.setSSLSocketFactory(sslSocketFactory);
+ } else {
+ log.trace("No custom socket factory set");
+ }
+ if (hostnameVerifier != null) {
+ log.debug("Setting custom hostname verifier");
+ https.setHostnameVerifier(hostnameVerifier);
+ }
+ } else {
+ 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()) {
+ 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];
+ }
+ }
+ }
+
+ /* (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) {
+ // if a content type is specified we have to switch to multipart/formdata encoding
+ if (contentType != null && contentType.length() > 0) {
+ urlEncoded = false;
+ }
+ httpFormParameter.add(new HTTPFormParameter(name, data, contentType,
+ charSet, transferEncoding));
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.bku.binding.DataUrlConnection#transmit(at.gv.egiz.bku.slcommands.SLResult)
+ */
+ public void transmit(SLResult slResult) throws IOException {
+ 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()
+ : "UTF-8");
+ // Note, using UTF-8 as fallback for decoding is safe.
+ // If the request was x-www-form-urlencoded,
+ // UTF-8 has been used for encoding of non-ASCII characters.
+ // If the request was multipart/form-data and contains any transfer parameters,
+ // the data URL request is going to be multipart/form-data encoded (see below).
+ while ((len = reader.read(cbuf)) != -1) {
+ urlEnc.write(cbuf, 0, len);
+ }
+ urlEnc.flush();
+ }
+ streamWriter.close();
+
+ } else {
+ //
+ // multipart/form-data (conforming to SL 1.2)
+ //
+
+ 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 {
+ 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;
+ boundary = "--" + IdFactory.getInstance().createId().toString();
+ requestHttpHeaders = new HashMap<String, String>();
+
+ if (config != null) {
+ String version = config.getProperty(Configurator.SIGNATURE_LAYOUT);
+ if ((version != null) && (!"".equals(version.trim()))) {
+ log.debug("setting SignatureLayout header to " + version);
+ requestHttpHeaders.put(Configurator.SIGNATURE_LAYOUT, version);
+ } else {
+ log.debug("do not set SignatureLayout header");
+ }
+ String userAgent = config.getProperty(Configurator.USERAGENT_CONFIG_P, Configurator.USERAGENT_DEFAULT);
+ requestHttpHeaders.put(HttpUtil.HTTP_HEADER_USER_AGENT, userAgent);
+ } else {
+ requestHttpHeaders
+ .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT);
+
+ }
+
+ httpFormParameter = new ArrayList<HTTPFormParameter>();
+
+ }
+
+ @Override
+ public DataUrlConnectionSPI newInstance() {
+ DataUrlConnectionSPI uc = new DataUrlConnectionImpl();
+ 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;
+ }
+
+ 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