diff --git a/.gitignore b/.gitignore
index dc94cc4..b7cb0f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+*.iml
+.idea/
+
/build/expath-http-client-java-*/
/build/expath-http-client-saxon-*/
/build/expath-http-client-java-*.zip
@@ -13,3 +16,6 @@
/http-client-saxon/build/
/http-client-saxon/dist/
/http-client-saxon/src/org/expath/httpclient/saxon/version.properties
+
+/http-client-java/target/
+/http-client-saxon/target/
diff --git a/http-client-java/pom.xml b/http-client-java/pom.xml
new file mode 100644
index 0000000..33ac73b
--- /dev/null
+++ b/http-client-java/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+
+ org.expath.http.client
+ http-client-parent
+ 1.0-SNAPSHOT
+ ../http-client-parent/pom.xml
+
+
+ http-client-java
+ jar
+
+ EXPath HTTP Client Java Library
+ Java Library implementing the core HTTP Client module features
+
+
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ http://github.com/adamretter/expath-http-client-java
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ src/main/assembly/assembly.xml
+
+
+
+
+
+
+
+
+ org.expath.tools
+ tools-java
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.ccil.cowan.tagsoup
+ tagsoup
+ 1.2.1
+
+
+ org.apache.james
+ apache-mime4j-core
+ 0.8.3
+
+
+ commons-logging
+ commons-logging
+
+
+ net.jcip
+ jcip-annotations
+
+
+ com.google.code.findbugs
+ jsr305
+
+
+ junit
+ junit
+ test
+
+
+
+
diff --git a/http-client-java/src/main/assembly/assembly.xml b/http-client-java/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..001c3d9
--- /dev/null
+++ b/http-client-java/src/main/assembly/assembly.xml
@@ -0,0 +1,19 @@
+
+
+ application
+
+ dir
+
+
+ true
+
+
+
+ /
+ true
+ runtime
+
+
+
\ No newline at end of file
diff --git a/http-client-java/src/net/iharder/Base64.java b/http-client-java/src/main/java/net/iharder/Base64.java
similarity index 100%
rename from http-client-java/src/net/iharder/Base64.java
rename to http-client-java/src/main/java/net/iharder/Base64.java
diff --git a/http-client-java/src/main/java/org/expath/httpclient/ContentType.java b/http-client-java/src/main/java/org/expath/httpclient/ContentType.java
new file mode 100644
index 0000000..94af7b9
--- /dev/null
+++ b/http-client-java/src/main/java/org/expath/httpclient/ContentType.java
@@ -0,0 +1,177 @@
+/****************************************************************************/
+/* File: ContentType.java */
+/* Author: F. Georges - fgeorges.org */
+/* Date: 2009-02-22 */
+/* Tags: */
+/* Copyright (c) 2009 Florent Georges (see end of file.) */
+/* ------------------------------------------------------------------------ */
+
+
+package org.expath.httpclient;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represent a Content-Type header.
+ *
+ * Provide the ability to get the boundary param in case of a multipart
+ * content type on the one hand, and the ability to get only the MIME type
+ * string without any param on the other hand.
+ *
+ * @author Florent Georges
+ */
+public class ContentType {
+
+ public ContentType(final String type, final String charset, final String boundary) {
+ this.myType = type;
+ this.myCharset = charset;
+ this.myBoundary = boundary;
+ }
+
+ public static ContentType parse(@Nullable final Header header, @Nullable final String overrideType, @Nullable final String defaultCharset) throws HttpClientException {
+ final String type;
+ final String charset;
+ final String boundary;
+
+ if (overrideType != null) {
+ // get the internet media type from the override
+ type = extractMediaTypeFromContentType(overrideType);
+
+ // does the override contain a charset?
+ if (overrideType.indexOf("charset=") > -1) {
+ // get the charset from the override
+ charset = overrideType.replaceFirst(".+charset=([^;\\s]+).*", "$1");
+ } else {
+ // get the charset from the header or the default
+ if (header == null || !"Content-Type".equalsIgnoreCase(header.getName())) {
+ throw new HttpClientException("Header is not content type");
+ }
+ final HeaderElement[] headerElements = header.getElements();
+ if (headerElements.length > 1) {
+ throw new HttpClientException("Multiple Content-Type headers");
+ }
+
+ final NameValuePair headerCharset = headerElements[0].getParameterByName("charset");
+ if (headerCharset != null) {
+ charset = headerCharset.getValue();
+ } else {
+ charset = defaultCharset;
+ }
+ }
+
+ // does the override contain a boundary?
+ if (overrideType.indexOf("boundary=") > -1) {
+ boundary = overrideType.replaceFirst(".+boundary=([^;\\s]+).*", "$1");
+ } else {
+ // get the boundary from the header or null
+ if (header == null || !"Content-Type".equalsIgnoreCase(header.getName())) {
+ throw new HttpClientException("Header is not content type");
+ }
+ final HeaderElement[] headerElements = header.getElements();
+ if (headerElements.length > 1) {
+ throw new HttpClientException("Multiple Content-Type headers");
+ }
+
+ final NameValuePair headerBoundary = headerElements[0].getParameterByName("boundary");
+ boundary = headerBoundary == null ? null : headerBoundary.getValue();
+ }
+
+ } else {
+ // get the internet media type from the header
+ if (header == null || !"Content-Type".equalsIgnoreCase(header.getName())) {
+ throw new HttpClientException("Header is not content type");
+ }
+ final HeaderElement[] headerElements = header.getElements();
+ if (headerElements.length > 1) {
+ throw new HttpClientException("Multiple Content-Type headers");
+ }
+
+ type = extractMediaTypeFromContentType(header.getValue());
+
+ // get the charset from the header or the default
+ final NameValuePair headerCharset = headerElements[0].getParameterByName("charset");
+ if (headerCharset != null) {
+ charset = headerCharset.getValue();
+ } else {
+ charset = defaultCharset;
+ }
+
+ // get the boundary from the header
+ final NameValuePair headerBoundary = headerElements[0].getParameterByName("boundary");
+ boundary = headerBoundary == null ? null : headerBoundary.getValue();
+ }
+
+ return new ContentType(type, charset, boundary);
+ }
+
+ private static String extractMediaTypeFromContentType(final String contentType) {
+ final int idxParamSeparator = contentType.indexOf(';');
+ if (idxParamSeparator > -1) {
+ return contentType.substring(0, idxParamSeparator);
+ } else {
+ return contentType;
+ }
+ }
+
+ @Nullable
+ public String getType() {
+ return myType;
+ }
+
+ @Nullable
+ public String getCharset() {
+ return myCharset;
+ }
+
+ @Nullable
+ public String getBoundary() {
+ return myBoundary;
+ }
+
+ @Nullable
+ public String getValue() {
+ final StringBuilder builder = new StringBuilder(myType);
+ if (myCharset != null) {
+ builder.append("; charset=").append(myCharset);
+ }
+ if (myBoundary != null) {
+ builder.append("; boundary=").append(myCharset);
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public String toString() {
+ return getValue();
+ }
+
+ private final String myType;
+ private final String myCharset;
+ private final String myBoundary;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT. */
+/* */
+/* The contents of this file are subject to the Mozilla Public License */
+/* Version 1.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.mozilla.org/MPL/. */
+/* */
+/* Software distributed under the License is distributed on an "AS IS" */
+/* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */
+/* the License for the specific language governing rights and limitations */
+/* under the License. */
+/* */
+/* The Original Code is: all this file. */
+/* */
+/* The Initial Developer of the Original Code is Florent Georges. */
+/* */
+/* Contributor(s): none. */
+/* ------------------------------------------------------------------------ */
diff --git a/http-client-java/src/org/expath/httpclient/HeaderSet.java b/http-client-java/src/main/java/org/expath/httpclient/HeaderSet.java
similarity index 80%
rename from http-client-java/src/org/expath/httpclient/HeaderSet.java
rename to http-client-java/src/main/java/org/expath/httpclient/HeaderSet.java
index 5354cfc..8fb4933 100644
--- a/http-client-java/src/org/expath/httpclient/HeaderSet.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/HeaderSet.java
@@ -101,39 +101,6 @@ public Header getFirstHeader(String name)
return null;
}
- public String getFirstHeaderWithoutParam(String name)
- throws HttpClientException
- {
- Header h = getFirstHeader(name);
- return getHeaderWithoutParam(h);
- }
-
- public static String getValueWithoutParam(String header_value)
- throws HttpClientException
- {
- Header h = new BasicHeader("X-Dummy", header_value);
- return getHeaderWithoutParam(h);
- }
-
- public static String getHeaderWithoutParam(Header header)
- throws HttpClientException
- {
- // get the content type, only the mime string, like "type/subtype"
- if ( header != null ) {
- HeaderElement[] elems = header.getElements();
- if ( elems == null ) {
- return null;
- }
- else if ( elems.length == 1 ) {
- return elems[0].getName();
- }
- else {
- throw new HttpClientException("Multiple Content-Type headers");
- }
- }
- return null;
- }
-
private List myHeaders;
}
diff --git a/http-client-java/src/org/expath/httpclient/HttpClient.java b/http-client-java/src/main/java/org/expath/httpclient/HttpClient.java
similarity index 92%
rename from http-client-java/src/org/expath/httpclient/HttpClient.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpClient.java
index 4de2782..7ee5233 100644
--- a/http-client-java/src/org/expath/httpclient/HttpClient.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/HttpClient.java
@@ -118,19 +118,14 @@ private Result sendOnce(Result result, URI uri, HttpRequest request, RequestPars
{
HttpConnection conn = new ApacheHttpConnection(uri);
try {
- if ( parser.getSendAuth() ) {
+ HttpResponse response = request.send(result, conn, parser.getCredentials());
+ if ( response.getStatus() == 401 ) {
+ conn.disconnect();
+ conn = new ApacheHttpConnection(uri);
+ // create a new result, and throw the old one away
+ result = result.makeNewResult();
request.send(result, conn, parser.getCredentials());
}
- else {
- HttpResponse response = request.send(result, conn, null);
- if ( response.getStatus() == 401 ) {
- conn.disconnect();
- conn = new ApacheHttpConnection(uri);
- // create a new result, and throw the old one away
- result = result.makeNewResult();
- request.send(result, conn, parser.getCredentials());
- }
- }
}
finally {
conn.disconnect();
diff --git a/http-client-java/src/org/expath/httpclient/HttpClientException.java b/http-client-java/src/main/java/org/expath/httpclient/HttpClientException.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpClientException.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpClientException.java
diff --git a/http-client-java/src/org/expath/httpclient/HttpConnection.java b/http-client-java/src/main/java/org/expath/httpclient/HttpConnection.java
similarity index 95%
rename from http-client-java/src/org/expath/httpclient/HttpConnection.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpConnection.java
index 1e662e1..7ea9632 100644
--- a/http-client-java/src/org/expath/httpclient/HttpConnection.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/HttpConnection.java
@@ -34,6 +34,9 @@ public void setRequestMethod(String method, boolean with_content)
throws HttpClientException;
public void setFollowRedirect(boolean follow);
public void setTimeout(int seconds);
+ public void setGzip(boolean gzip);
+ public void setChunked(boolean chunked);
+ public void setPreemptiveAuthentication(boolean preemptiveAuthentication);
// responses...
public int getResponseStatus()
throws HttpClientException;
diff --git a/http-client-java/src/org/expath/httpclient/HttpConstants.java b/http-client-java/src/main/java/org/expath/httpclient/HttpConstants.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpConstants.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpConstants.java
diff --git a/http-client-java/src/org/expath/httpclient/HttpCredentials.java b/http-client-java/src/main/java/org/expath/httpclient/HttpCredentials.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpCredentials.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpCredentials.java
diff --git a/http-client-java/src/org/expath/httpclient/HttpRequest.java b/http-client-java/src/main/java/org/expath/httpclient/HttpRequest.java
similarity index 91%
rename from http-client-java/src/org/expath/httpclient/HttpRequest.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpRequest.java
index 3c25686..46a8030 100644
--- a/http-client-java/src/org/expath/httpclient/HttpRequest.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/HttpRequest.java
@@ -27,6 +27,7 @@ public HttpResponse send(Result result, HttpConnection conn, HttpCredentials cre
public String getHttpVersion();
public void setHttpVersion(String ver)
throws HttpClientException;
+ public void setDefaultCharset(String charset);
public void setOverrideType(String type);
public void setHeaders(HeaderSet headers);
public void setBody(HttpRequestBody body)
@@ -34,6 +35,11 @@ public void setBody(HttpRequestBody body)
public void setStatusOnly(boolean only);
public void setFollowRedirect(boolean follow);
public void setTimeout(Integer seconds);
+ public void setGzip(boolean gzip);
+ public boolean isChunked();
+ public void setChunked(boolean chunked);
+ boolean isPreemptiveAuthentication();
+ void setPreemptiveAuthentication(final boolean preemptiveAuthentication);
}
diff --git a/http-client-java/src/org/expath/httpclient/HttpRequestBody.java b/http-client-java/src/main/java/org/expath/httpclient/HttpRequestBody.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpRequestBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpRequestBody.java
diff --git a/http-client-java/src/org/expath/httpclient/HttpResponse.java b/http-client-java/src/main/java/org/expath/httpclient/HttpResponse.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpResponse.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpResponse.java
diff --git a/http-client-java/src/org/expath/httpclient/HttpResponseBody.java b/http-client-java/src/main/java/org/expath/httpclient/HttpResponseBody.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/HttpResponseBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/HttpResponseBody.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/AnyEmptyMethod.java b/http-client-java/src/main/java/org/expath/httpclient/impl/AnyEmptyMethod.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/impl/AnyEmptyMethod.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/AnyEmptyMethod.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/AnyEntityMethod.java b/http-client-java/src/main/java/org/expath/httpclient/impl/AnyEntityMethod.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/impl/AnyEntityMethod.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/AnyEntityMethod.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/ApacheHttpConnection.java b/http-client-java/src/main/java/org/expath/httpclient/impl/ApacheHttpConnection.java
similarity index 55%
rename from http-client-java/src/org/expath/httpclient/impl/ApacheHttpConnection.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/ApacheHttpConnection.java
index f22db54..139173f 100644
--- a/http-client-java/src/org/expath/httpclient/impl/ApacheHttpConnection.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/ApacheHttpConnection.java
@@ -9,42 +9,47 @@
package org.expath.httpclient.impl;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ProxySelector;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.net.Socket;
import java.net.URI;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
+
+import net.jcip.annotations.NotThreadSafe;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
+import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
+import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
import org.apache.http.client.CookieStore;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpHead;
-import org.apache.http.client.methods.HttpOptions;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.client.methods.HttpTrace;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.GzipCompressingEntity;
+import org.apache.http.client.methods.*;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
-import org.apache.http.impl.client.AbstractHttpClient;
-import org.apache.http.impl.client.BasicCookieStore;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.DigestScheme;
+import org.apache.http.impl.client.*;
+import org.apache.http.impl.conn.*;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.ssl.SSLContexts;
import org.expath.httpclient.HeaderSet;
import org.expath.httpclient.HttpClientException;
import org.expath.httpclient.HttpConnection;
@@ -52,11 +57,15 @@
import org.expath.httpclient.HttpCredentials;
import org.expath.httpclient.HttpRequestBody;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+
/**
* An implementation of an HTTP connection using Apachhe HTTP Client.
*
* @author Florent Georges
*/
+@NotThreadSafe
public class ApacheHttpConnection
implements HttpConnection
{
@@ -66,20 +75,31 @@ public ApacheHttpConnection(URI uri)
myRequest = null;
myResponse = null;
myVersion = DEFAULT_HTTP_VERSION;
- myClient = null;
}
- public void connect(HttpRequestBody body, HttpCredentials cred)
+ @Override
+ public void connect(final HttpRequestBody body, final HttpCredentials cred)
throws HttpClientException
{
if ( myRequest == null ) {
throw new HttpClientException("setRequestMethod has not been called before");
}
+
+ myRequest.setProtocolVersion(myVersion);
+
try {
// make a new client
- myClient = makeClient();
+ if(myClient == null) {
+ myClient = makeClient();
+ }
+
+ if(myResponse != null) {
+ // close any previous response
+ myResponse.close();
+ }
+
// set the credentials (if any)
- setCredentials(cred);
+ final HttpClientContext clientContext = setCredentials(cred);
// set the request entity body (if any)
setRequestEntity(body);
// log the request headers?
@@ -90,7 +110,7 @@ public void connect(HttpRequestBody body, HttpCredentials cred)
LoggerHelper.logCookies(LOG, "COOKIES", COOKIES.getCookies());
}
// send the request
- myResponse = myClient.execute(myRequest);
+ myResponse = myClient.execute(myRequest, clientContext);
// TODO: Handle 'Connection' headers (for instance "Connection: close")
// See for instance http://www.jmarshall.com/easy/http/.
@@ -104,21 +124,56 @@ public void connect(HttpRequestBody body, HttpCredentials cred)
}
}
catch ( IOException ex ) {
- throw new HttpClientException("Error executing the HTTP method: " + ex.getMessage(), ex);
+ final String message = getMessage(ex);
+ throw new HttpClientException("Error executing the HTTP method: " + message != null ? message : "", ex);
+ } finally {
+ state = State.POST_CONNECT;
}
}
- public void disconnect()
- {
- if ( myClient != null ) {
- myClient.getConnectionManager().shutdown();
+ /**
+ * Retrieves a message from the Throwable
+ * or its cause (recursively).
+ *
+ * @param throwable A thrown exception
+ *
+ * @return The first message, or null if there are no messages
+ * at all.
+ */
+ private String getMessage(final Throwable throwable) {
+ if(throwable.getMessage() != null) {
+ return throwable.getMessage();
+ }
+
+ final Throwable cause = throwable.getCause();
+ if(cause == null || cause == throwable) {
+ return null;
}
+
+ return getMessage(cause);
}
- public void setHttpVersion(String ver)
+ @Override
+ public void disconnect() throws HttpClientException {
+ try {
+ if(myResponse != null) {
+ myResponse.close();
+ myResponse = null;
+ }
+
+ myClient.close();
+ myClient = null;
+ } catch (final IOException ex) {
+ final String message = getMessage(ex);
+ throw new HttpClientException(message, ex);
+ }
+ }
+
+ @Override
+ public void setHttpVersion(final String ver)
throws HttpClientException
{
- if ( myClient != null ) {
+ if ( state != State.INITIAL ) {
String msg = "Internal error, HTTP version cannot been "
+ "set after connect() has been called.";
throw new HttpClientException(msg);
@@ -193,6 +248,21 @@ public void setTimeout(int seconds)
myTimeout = seconds;
}
+ @Override
+ public void setGzip(final boolean gzip) {
+ myGzip = gzip;
+ }
+
+ @Override
+ public void setChunked(final boolean chunked) {
+ myChunked = chunked;
+ }
+
+ @Override
+ public void setPreemptiveAuthentication(final boolean preemptiveAuthentication) {
+ myPreemptiveAuthentication = preemptiveAuthentication;
+ }
+
/**
* Check the method name does match the HTTP/1.1 production rules.
*
@@ -283,64 +353,106 @@ public InputStream getResponseStream()
/**
* Make a new Apache HTTP client, in order to serve this request.
*/
- private AbstractHttpClient makeClient()
- {
- AbstractHttpClient client = new DefaultHttpClient();
- HttpParams params = client.getParams();
+ private CloseableHttpClient makeClient() {
+
+ final HttpClientBuilder clientBuilder = HttpClientBuilder.create()
+ .setConnectionManager(POOLING_CONNECTION_MANAGER)
+ .setConnectionManagerShared(true);
+
// use the default JVM proxy settings (http.proxyHost, etc.)
- HttpRoutePlanner route = new ProxySelectorRoutePlanner(
- client.getConnectionManager().getSchemeRegistry(),
- ProxySelector.getDefault());
- client.setRoutePlanner(route);
+ clientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(null));
+
// do follow redirections?
- params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, myFollowRedirect);
+ if(myFollowRedirect) {
+ clientBuilder.setRedirectStrategy(LaxRedirectStrategy.INSTANCE);
+ }
+
+ // the shared cookie store
+ clientBuilder.setDefaultCookieStore(COOKIES);
+
// set the timeout if any
- if ( myTimeout != null ) {
+ final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+ if(myTimeout != null) {
// See http://blog.jayway.com/2009/03/17/configuring-timeout-with-apache-httpclient-40/
- HttpConnectionParams.setConnectionTimeout(params, myTimeout * 1000);
- HttpConnectionParams.setSoTimeout(params, myTimeout * 1000);
+ requestConfigBuilder
+ .setConnectTimeout(myTimeout * 1000)
+ .setSocketTimeout(myTimeout * 1000);
}
- // the shared cookie store
- client.setCookieStore(COOKIES);
- // the HTTP version (1.0 or 1.1)
- params.setParameter("http.protocol.version", myVersion);
- // return the just built client
+ clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
+
+ final CloseableHttpClient client = clientBuilder.build();
return client;
}
/**
* Set the credentials on the client, based on the {@link HttpCredentials} object.
*/
- private void setCredentials(HttpCredentials cred)
- throws HttpClientException
- {
- if ( cred == null ) {
- return;
+ private HttpClientContext setCredentials(HttpCredentials cred)
+ throws HttpClientException {
+ final HttpClientContext clientContext = HttpClientContext.create();
+
+ if (cred == null) {
+ return clientContext;
}
- URI uri = myRequest.getURI();
+
+ final URI uri = myRequest.getURI();
+ final String scheme = uri.getScheme();
int port = uri.getPort();
- if ( port == -1 ) {
- String scheme = uri.getScheme();
- if ( "http".equals(scheme) ) {
+ if (port == -1) {
+ if ("http".equals(scheme)) {
port = 80;
- }
- else if ( "https".equals(scheme) ) {
+ } else if ("https".equals(scheme)) {
port = 443;
- }
- else {
+ } else {
throw new HttpClientException("Unknown scheme: " + uri);
}
}
- String host = uri.getHost();
- String user = cred.getUser();
- String pwd = cred.getPwd();
- if ( LOG.isDebugEnabled() ) {
- LOG.debug("Set credentials for " + host + ":" + port
+ final String host = uri.getHost();
+
+ final HttpHost targetHost = new HttpHost(host, port, scheme);
+
+ final String user = cred.getUser();
+ final String pwd = cred.getPwd();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Set credentials for " + targetHost.getHostName() + ":" + targetHost.getPort()
+ " - " + user + " - ***");
}
- Credentials c = new UsernamePasswordCredentials(user, pwd);
- AuthScope scope = new AuthScope(host, port);
- myClient.getCredentialsProvider().setCredentials(scope, c);
+
+ final Credentials c = new UsernamePasswordCredentials(user, pwd);
+ final AuthScope scope = new AuthScope(targetHost);
+
+ if (clientContext.getCredentialsProvider() == null) {
+ clientContext.setCredentialsProvider(new BasicCredentialsProvider());
+ } else {
+ clientContext.getCredentialsProvider().clear();
+ }
+
+ clientContext.getCredentialsProvider().setCredentials(scope, c);
+
+ // force preemptive authentication?
+ // see - https://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html#d5e717
+ if (myPreemptiveAuthentication) {
+
+ // is there already an auth cache?
+ if (clientContext.getAuthCache() == null) {
+ // no, so create one
+ final AuthCache authCache = new BasicAuthCache();
+ clientContext.setAuthCache(authCache);
+ }
+
+ // set the auth cache scheme
+ final AuthScheme authScheme;
+ if (cred.getMethod().equals("DIGEST")) {
+ authScheme = new DigestScheme();
+ } else {
+ authScheme = new BasicScheme();
+ }
+
+ clientContext.getAuthCache().put(targetHost, authScheme);
+ }
+
+ return clientContext;
}
/**
@@ -353,23 +465,58 @@ private void setRequestEntity(HttpRequestBody body)
return;
}
// make the entity from a new producer
- HttpEntity entity;
+ final HttpEntity entity;
if ( myVersion == HttpVersion.HTTP_1_1 ) {
- // Take advantage of HTTP 1.1 chunked encoding to stream the
- // payload directly to the request.
- ContentProducer producer = new RequestBodyProducer(body);
- EntityTemplate template = new EntityTemplate(producer);
- template.setContentType(body.getContentType());
- entity = template;
+
+ final HttpEntity template;
+ if(myChunked) {
+ // Take advantage of HTTP 1.1 chunked encoding to stream the
+ // payload directly to the request.
+ final ContentProducer producer = new RequestBodyProducer(body);
+ final EntityTemplate entityTemplate = new EntityTemplate(producer);
+ entityTemplate.setContentType(body.getContentType());
+ entityTemplate.setChunked(true);
+ template = entityTemplate;
+
+ } else {
+ /*
+ NOTE: for some reason even if you set EntityTemplate#setChunked(false),
+ Apache insists on chunking anyway... So, instead we manually buffer here
+ to foce non-chunked transfer encoding.
+ */
+ try (final ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
+ body.serialize(buffer);
+ template = new ByteArrayEntity(buffer.toByteArray());
+ } catch (final IOException e) {
+ throw new HttpClientException(e.getMessage(), e);
+ }
+ }
+
+ if(myGzip) {
+ entity = new GzipCompressingEntity(template);
+ } else {
+ entity = template;
+ }
}
else {
// With HTTP 1.0, chunked encoding is not supported, so first
// serialize into memory and use the resulting byte array as the
// entity payload.
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- body.serialize(buffer);
- entity = new ByteArrayEntity(buffer.toByteArray());
+ try (final ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
+ if(myGzip) {
+ try (final GZIPOutputStream gzip = new GZIPOutputStream(buffer)) {
+ body.serialize(gzip);
+ }
+ myRequest.setHeader(HTTP.CONTENT_ENCODING, "gzip");
+ } else {
+ body.serialize(buffer);
+ }
+ entity = new ByteArrayEntity(buffer.toByteArray());
+ } catch (final IOException e) {
+ throw new HttpClientException(e.getMessage(), e);
+ }
}
+
// cast the request
HttpEntityEnclosingRequestBase req = null;
if ( ! (myRequest instanceof HttpEntityEnclosingRequestBase) ) {
@@ -383,20 +530,80 @@ private void setRequestEntity(HttpRequestBody body)
req.setEntity(entity);
}
+ private static PoolingHttpClientConnectionManager setupConnectionPool() {
+ final SSLContext sslContext = SSLContexts.
+ createSystemDefault();
+
+ final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLSocketFactoryWithSNI(sslContext);
+
+ final Registry socketFactoryRegistry = RegistryBuilder.create()
+ .register("https", sslConnectionSocketFactory)
+ .register("http", PlainConnectionSocketFactory.INSTANCE)
+ .build();
+
+ final PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, 15, TimeUnit.MINUTES); //TODO(AR) TTL is currently 15 minutes, make configurable?
+ poolingHttpClientConnectionManager.setMaxTotal(40); //TODO(AR) total pooled connections is 40, make configurable?
+ poolingHttpClientConnectionManager.setDefaultMaxPerRoute(2); //TODO(AR) max default connections per route is 2, make configurable?
+ return poolingHttpClientConnectionManager;
+ }
+
+ /**
+ * Implements SNI (Server Name Identification) for SSL
+ *
+ * @see https://github.com/fgeorges/expath-http-client-java/issues/5
+ */
+ private static class SSLSocketFactoryWithSNI extends SSLConnectionSocketFactory {
+ public SSLSocketFactoryWithSNI(final SSLContext sslContext) {
+ super(sslContext);
+ }
+
+ @Override
+ public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host,
+ final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context)
+ throws IOException {
+ if (socket instanceof SSLSocket) {
+ try {
+ final Class socketClazz = socket.getClass();
+ final Method m = socketClazz.getDeclaredMethod("setHost", String.class);
+ m.setAccessible(true);
+ m.invoke(socket, host.getHostName());
+ } catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ LOG.warn("Problem whilst setting SNI: " + e.getMessage(), e);
+ }
+ }
+
+ return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
+ }
+ }
+
+ private enum State {
+ INITIAL,
+ POST_CONNECT
+ }
+
+ private static final PoolingHttpClientConnectionManager POOLING_CONNECTION_MANAGER = setupConnectionPool();
+
+ private State state = State.INITIAL;
+
/** The target URI. */
private URI myUri;
+ /** The Apache client. */
+ private CloseableHttpClient myClient;
/** The Apache request. */
- private HttpUriRequest myRequest;
+ private HttpRequestBase myRequest;
/** The Apache response. */
- private HttpResponse myResponse;
+ private CloseableHttpResponse myResponse;
/** The HTTP protocol version. */
private HttpVersion myVersion;
- /** The Apache client. */
- private AbstractHttpClient myClient;
/** Follow HTTP redirect? */
private boolean myFollowRedirect = true;
/** The timeout to use, in seconds, or null for default. */
private Integer myTimeout = null;
+ /** whether we should use gzip transfer encoding */
+ private boolean myGzip = false;
+ private boolean myChunked = true;
+ private boolean myPreemptiveAuthentication = false;
+
/**
* The shared cookie store.
*
diff --git a/http-client-java/src/org/expath/httpclient/impl/BinaryResponseBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/BinaryResponseBody.java
similarity index 84%
rename from http-client-java/src/org/expath/httpclient/impl/BinaryResponseBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/BinaryResponseBody.java
index 06f12f4..f274da6 100644
--- a/http-client-java/src/org/expath/httpclient/impl/BinaryResponseBody.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/BinaryResponseBody.java
@@ -28,13 +28,6 @@
public class BinaryResponseBody
implements HttpResponseBody
{
- public BinaryResponseBody(Result result, byte[] value, ContentType type, HeaderSet headers)
- throws HttpClientException
- {
- myContentType = type;
- myHeaders = headers;
- result.add(value);
- }
// TODO: Work only for binary response. What if the response is encoded
// with base64?
@@ -48,19 +41,7 @@ public BinaryResponseBody(Result result, InputStream in, ContentType type, Heade
{
myContentType = type;
myHeaders = headers;
- try {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[4096];
- int read = 0;
- while ( (read = in.read(buf)) > 0 ) {
- out.write(buf, 0, read);
- }
- byte[] bytes = out.toByteArray();
- result.add(bytes);
- }
- catch ( IOException ex ) {
- throw new HttpClientException("error reading HTTP response", ex);
- }
+ result.add(in);
}
@Override
diff --git a/http-client-java/src/org/expath/httpclient/impl/BodyFactory.java b/http-client-java/src/main/java/org/expath/httpclient/impl/BodyFactory.java
similarity index 65%
rename from http-client-java/src/org/expath/httpclient/impl/BodyFactory.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/BodyFactory.java
index e9aab45..649d5bd 100644
--- a/http-client-java/src/org/expath/httpclient/impl/BodyFactory.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/BodyFactory.java
@@ -12,9 +12,10 @@
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+
import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.message.BasicHeader;
import org.expath.httpclient.ContentType;
import org.expath.httpclient.HeaderSet;
import org.expath.httpclient.HttpClientException;
@@ -31,19 +32,17 @@
*
* @author Florent Georges
*/
-public class BodyFactory
-{
+public class BodyFactory {
// TODO: Take new methods into account (XHTML, BASE64 and HEX).
- public static HttpRequestBody makeRequestBody(Element elem, Sequence bodies, String ns)
- throws HttpClientException
- {
+ public static HttpRequestBody makeRequestBody(final Element elem, final Sequence bodies, final String ns)
+ throws HttpClientException {
// method is got from @method if any...
Type method = parseMethod(elem);
// ...or from @media-type if no @method
- if ( method == null ) {
+ if (method == null) {
method = parseType(elem);
}
- switch ( method ) {
+ switch (method) {
case MULTIPART:
return new MultipartRequestBody(elem, bodies, ns);
case SRC:
@@ -61,36 +60,33 @@ public static HttpRequestBody makeRequestBody(Element elem, Sequence bodies, Str
}
}
- public static HttpResponseBody makeResponseBody(Result result, ContentType type, HttpConnection conn)
- throws HttpClientException
- {
- if ( type == null ) {
+ public static HttpResponseBody makeResponseBody(final Result result, final ContentType type, final HttpConnection conn)
+ throws HttpClientException {
+ if (type == null) {
// it is legitimate to not have a body in a response; for instance
// on a "304 Not Modified"
return null;
}
String t = type.getType();
- if ( t == null ) {
+ if (t == null) {
return null;
}
- InputStream in = conn.getResponseStream();
- if ( in == null ) {
+ final InputStream in = conn.getResponseStream();
+ if (in == null) {
return null;
}
- if ( t.startsWith("multipart/") ) {
- return new MultipartResponseBody(result, in, type, conn);
- }
- else {
+ if (t.startsWith("multipart/")) {
+ return new MultipartResponseBody(result, in, type);
+ } else {
return makeResponsePart(result, null, in, type);
}
}
// package-level to be used within MultipartResponseBody ctor
// TODO: Take new methods into account (XHTML, BASE64 and HEX).
- static HttpResponseBody makeResponsePart(Result result, HeaderSet headers, InputStream in, ContentType ctype)
- throws HttpClientException
- {
- switch ( parseType(ctype) ) {
+ static HttpResponseBody makeResponsePart(final Result result, final HeaderSet headers, final InputStream in, final ContentType ctype)
+ throws HttpClientException {
+ switch (parseType(ctype)) {
case XML:
// TODO: 'content_type' is the header Content-Type without any param
// (i.e. "text/xml".) Should we keep this, or put the whole header
@@ -107,8 +103,7 @@ static HttpResponseBody makeResponsePart(Result result, HeaderSet headers, Input
}
}
- public static enum Type
- {
+ public enum Type {
XML,
HTML,
XHTML,
@@ -120,20 +115,22 @@ public static enum Type
SRC
}
- /** Media types that must be treated as text types (in addition to text/*). */
- private static Set TEXT_TYPES;
+ /**
+ * Media types that must be treated as text types (in addition to text/*).
+ */
+ private static final Set TEXT_TYPES = new HashSet<>();
static {
- TEXT_TYPES = new HashSet();
TEXT_TYPES.add("application/x-www-form-urlencoded");
TEXT_TYPES.add("application/xml-dtd");
}
- /** Media types that must be treated as XML types (in addition to *+xml). */
- private static Set XML_TYPES;
+ /**
+ * Media types that must be treated as XML types (in addition to *+xml).
+ */
+ private static final Set XML_TYPES = new HashSet<>();
static {
// Doc: does not handle "application/xml-dtd" as XML
// TODO: What about ".../xml-external-parsed-entity" ?
- XML_TYPES = new HashSet();
XML_TYPES.add("text/xml");
XML_TYPES.add("application/xml");
XML_TYPES.add("text/xml-external-parsed-entity");
@@ -142,53 +139,44 @@ public static enum Type
/**
* Decode the content type from a MIME type string.
- *
+ *
* TODO: Take new methods into account (XHTML, BASE64 and HEX).
*/
- private static Type parseType(String type)
- {
- if ( type.startsWith("multipart/") ) {
+ private static Type parseType(final String type) {
+ if (type.startsWith("multipart/")) {
return Type.MULTIPART;
- }
- else if ( "text/html".equals(type) ) {
+ } else if ("text/html".equals(type)) {
return Type.HTML;
- }
- else if ( type.endsWith("+xml") || XML_TYPES.contains(type) ) {
+ } else if (type.endsWith("+xml") || XML_TYPES.contains(type)) {
return Type.XML;
- }
- else if ( type.startsWith("text/") || TEXT_TYPES.contains(type) ) {
+ } else if (type.startsWith("text/") || TEXT_TYPES.contains(type)) {
return Type.TEXT;
- }
- else {
+ } else {
return Type.BINARY;
}
}
/**
- * Look for the header COntent-Type in a header set and decode it.
+ * Look for the header Content-Type in a header set and decode it.
*/
- public static Type parseType(HeaderSet headers)
- throws HttpClientException
- {
- Header h = headers.getFirstHeader("Content-Type");
- if ( h == null ) {
+ public static Type parseType(final HeaderSet headers) throws HttpClientException {
+ final Header h = headers.getFirstHeader("Content-Type");
+ if (h == null) {
throw new HttpClientException("impossible to find the content type");
}
- ContentType ct = new ContentType(h);
+ final ContentType ct = ContentType.parse(h, null, null);
return parseType(ct);
}
/**
* Decode the content type from a ContentType object.
*/
- public static Type parseType(ContentType type)
- throws HttpClientException
- {
- if ( type == null ) {
+ public static Type parseType(final ContentType type) throws HttpClientException {
+ if (type == null) {
throw new HttpClientException("impossible to find the content type");
}
- String t = type.getType();
- if ( t == null ) {
+ final String t = type.getType();
+ if (t == null) {
throw new HttpClientException("impossible to find the content type");
}
return parseType(t);
@@ -197,74 +185,67 @@ public static Type parseType(ContentType type)
/**
* Parse the @media-type from a http:body or http:multipart element.
*/
- private static Type parseType(Element elem)
- throws HttpClientException
- {
- String local = elem.getLocalName();
- if ( "multipart".equals(local) ) {
+ private static Type parseType(final Element elem) throws HttpClientException {
+ final String local = elem.getLocalName();
+ if ("multipart".equals(local)) {
return Type.MULTIPART;
- }
- else if ( ! "body".equals(local) ) {
+ } else if (!"body".equals(local)) {
throw new HttpClientException("INTERNAL ERROR: cannot happen, checked before");
- }
- else {
- if ( elem.getAttribute("src") != null ) {
+ } else {
+ if (elem.getAttribute("src") != null) {
return Type.SRC;
}
- String content_type = elem.getAttribute("media-type");
- if ( content_type == null ) {
+ final String mediaType = elem.getAttribute("media-type");
+ if (mediaType == null) {
throw new HttpClientException("@media-type is not set on http:body");
}
- Type type = parseType(HeaderSet.getValueWithoutParam(content_type));
- if ( type == Type.MULTIPART ) {
- String msg = "multipart type not allowed for http:body: " + content_type;
- throw new HttpClientException(msg);
+ final Header mediaTypeHeader = new BasicHeader("Media-Type", mediaType);
+ final HeaderElement[] mediaTypeHeaderElems = mediaTypeHeader.getElements();
+ if (mediaTypeHeaderElems == null || mediaTypeHeaderElems.length == 0) {
+ throw new HttpClientException("@media-type is not set on http:body");
+ } else if (mediaTypeHeaderElems.length > 1) {
+ throw new HttpClientException("Multiple @media-type internet media types present");
+ } else {
+ final Type type = parseType(mediaTypeHeaderElems[0].getName());
+ if (type == Type.MULTIPART) {
+ final String msg = "multipart type not allowed for http:body: " + mediaType;
+ throw new HttpClientException(msg);
+ }
+ return type;
}
- return type;
}
}
/**
* Parse the @method from a http:body or http:multipart element.
- *
+ *
* Return null if there is no @method.
*/
- private static Type parseMethod(Element elem)
- throws HttpClientException
- {
- String m = elem.getAttribute("method");
- if ( m == null ) {
+ private static Type parseMethod(final Element elem) throws HttpClientException {
+ final String m = elem.getAttribute("method");
+ if (m == null) {
return null;
- }
- else if ( "xml".equals(m) ) {
+ } else if ("xml".equals(m)) {
return Type.XML;
- }
- else if ( "html".equals(m) ) {
+ } else if ("html".equals(m)) {
return Type.HTML;
- }
- else if ( "xhtml".equals(m) ) {
+ } else if ("xhtml".equals(m)) {
return Type.XHTML;
- }
- else if ( "text".equals(m) ) {
+ } else if ("text".equals(m)) {
return Type.TEXT;
- }
- else if ( "binary".equals(m) ) {
+ } else if ("binary".equals(m)) {
return Type.BINARY;
}
// FIXME: The spec says "binary", but I think we need "base64" and "hex"
// instead (or in addition, if "binary" is left implementation-defined).
- else if ( "base64".equals(m) ) {
+ else if ("base64".equals(m)) {
return Type.BASE64;
- }
- else if ( "hex".equals(m) ) {
+ } else if ("hex".equals(m)) {
return Type.HEX;
- }
- else {
+ } else {
throw new HttpClientException("Incorrect value for @method: " + m);
}
}
-
- private static Log LOG = LogFactory.getLog(BodyFactory.class);
}
diff --git a/http-client-java/src/org/expath/httpclient/impl/HrefRequestBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/HrefRequestBody.java
similarity index 86%
rename from http-client-java/src/org/expath/httpclient/impl/HrefRequestBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/HrefRequestBody.java
index fbcdb5a..34df06f 100644
--- a/http-client-java/src/org/expath/httpclient/impl/HrefRequestBody.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/HrefRequestBody.java
@@ -62,23 +62,18 @@ public void serialize(OutputStream out)
{
try {
String filename = new URI(myHref).getPath();
- InputStream in = new FileInputStream(new File(filename));
- byte[] buf = new byte[4096];
- int l = -1;
- while ( (l = in.read(buf)) != -1 ) {
- out.write(buf, 0, l);
+ try (final InputStream in = new FileInputStream(new File(filename))) {
+ byte[] buf = new byte[4096];
+ int l = -1;
+ while ((l = in.read(buf)) != -1) {
+ out.write(buf, 0, l);
+ }
+ } catch (IOException ex) {
+ throw new HttpClientException("Error sending the file content", ex);
}
- in.close();
- }
- catch ( URISyntaxException ex ) {
+ } catch ( URISyntaxException ex ) {
throw new HttpClientException("Bad URI: " + myHref, ex);
}
- catch ( FileNotFoundException ex ) {
- throw new HttpClientException("Error sending the file content", ex);
- }
- catch ( IOException ex ) {
- throw new HttpClientException("Error sending the file content", ex);
- }
}
private String myHref;
diff --git a/http-client-java/src/org/expath/httpclient/impl/HttpRequestImpl.java b/http-client-java/src/main/java/org/expath/httpclient/impl/HttpRequestImpl.java
similarity index 81%
rename from http-client-java/src/org/expath/httpclient/impl/HttpRequestImpl.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/HttpRequestImpl.java
index ba5e1b4..23ff4b5 100644
--- a/http-client-java/src/org/expath/httpclient/impl/HttpRequestImpl.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/HttpRequestImpl.java
@@ -47,6 +47,12 @@ public HttpResponse send(Result result, HttpConnection conn, HttpCredentials cre
if ( myTimeout != null ) {
conn.setTimeout(myTimeout);
}
+ if ( myGzip ) {
+ conn.setGzip(true);
+ }
+ conn.setChunked(isChunked());
+ conn.setPreemptiveAuthentication(isPreemptiveAuthentication());
+
conn.setFollowRedirect(myFollowRedirect);
conn.connect(myBody, cred);
int status = conn.getResponseStatus();
@@ -69,21 +75,11 @@ public HttpResponse send(Result result, HttpConnection conn, HttpCredentials cre
return resp;
}
- private ContentType getContentType(HeaderSet headers)
+ private ContentType getContentType(final HeaderSet headers)
throws HttpClientException
{
- if ( myOverrideType == null ) {
- Header h = headers.getFirstHeader("Content-Type");
- if ( h == null ) {
- return null;
- }
- else {
- return new ContentType(h);
- }
- }
- else {
- return new ContentType(myOverrideType, null);
- }
+ final Header header = headers.getFirstHeader("Content-Type");
+ return ContentType.parse(header, myOverrideType, myDefaultCharset);
}
@Override
@@ -131,6 +127,11 @@ else if ( HttpConstants.HTTP_1_1.equals(ver) ) {
}
}
+ @Override
+ public void setDefaultCharset(final String charset) {
+ myDefaultCharset = charset;
+ }
+
@Override
public void setOverrideType(String type)
{
@@ -169,13 +170,50 @@ public void setTimeout(Integer seconds)
myTimeout = seconds;
}
+ @Override
+ public void setGzip(final boolean gzip) {
+ myGzip = gzip;
+ }
+
+ @Override
+ public boolean isChunked() {
+ if(myChunked == null) {
+ if(myHttpVer != null && myHttpVer.equals(HttpConstants.HTTP_1_0)) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return myChunked;
+ }
+ }
+
+ @Override
+ public void setChunked(boolean chunked) {
+ this.myChunked = chunked;
+ }
+
+ @Override
+ public boolean isPreemptiveAuthentication() {
+ return myPreemptiveAuthentication;
+ }
+
+ @Override
+ public void setPreemptiveAuthentication(final boolean preemptiveAuthentication) {
+ this.myPreemptiveAuthentication = preemptiveAuthentication;
+ }
+
private String myMethod;
private String myHref;
private String myHttpVer;
+ private String myDefaultCharset;
private String myOverrideType;
private boolean myStatusOnly;
private boolean myFollowRedirect = true;
private Integer myTimeout = null;
+ private boolean myGzip = false;
+ private Boolean myChunked = null;
+ private boolean myPreemptiveAuthentication = false;
private HeaderSet myHeaders;
private HttpRequestBody myBody;
private static final Log LOG = LogFactory.getLog(HttpRequestImpl.class);
diff --git a/http-client-java/src/org/expath/httpclient/impl/LoggerHelper.java b/http-client-java/src/main/java/org/expath/httpclient/impl/LoggerHelper.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/impl/LoggerHelper.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/LoggerHelper.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/MultipartRequestBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/MultipartRequestBody.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/impl/MultipartRequestBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/MultipartRequestBody.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/MultipartResponseBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/MultipartResponseBody.java
similarity index 62%
rename from http-client-java/src/org/expath/httpclient/impl/MultipartResponseBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/MultipartResponseBody.java
index 6e0f783..61f11ba 100644
--- a/http-client-java/src/org/expath/httpclient/impl/MultipartResponseBody.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/MultipartResponseBody.java
@@ -12,8 +12,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
@@ -24,7 +26,6 @@
import org.expath.httpclient.ContentType;
import org.expath.httpclient.HeaderSet;
import org.expath.httpclient.HttpClientException;
-import org.expath.httpclient.HttpConnection;
import org.expath.httpclient.HttpResponseBody;
import org.expath.httpclient.model.Result;
import org.expath.httpclient.model.TreeBuilder;
@@ -35,80 +36,75 @@
*
* @author Florent Georges
*/
-public class MultipartResponseBody
- implements HttpResponseBody
-{
- public MultipartResponseBody(Result result, InputStream in, ContentType type, HttpConnection conn)
- throws HttpClientException
- {
- myContentType = type;
- myParts = new ArrayList();
- Header h = conn.getResponseHeaders().getFirstHeader("Content-Type");
- if ( h == null ) {
+public class MultipartResponseBody implements HttpResponseBody {
+
+ public MultipartResponseBody(final Result result, final InputStream in, final ContentType type)
+ throws HttpClientException {
+ if (type == null || type.getType() == null) {
throw new HttpClientException("No content type");
}
+
+ myContentType = type;
+ myParts = new ArrayList<>();
+
myBoundary = type.getBoundary();
- if ( myBoundary == null ) {
+ if (myBoundary == null) {
throw new HttpClientException("No boundary");
}
try {
- analyzeParts(result, in, h.getValue());
- }
- catch ( IOException ex ) {
+ analyzeParts(result, in);
+ } catch (IOException ex) {
throw new HttpClientException("error reading the response stream", ex);
}
}
@Override
- public void outputBody(TreeBuilder b)
- throws HttpClientException
- {
+ public void outputBody(final TreeBuilder b) throws HttpClientException {
try {
b.startElem("multipart");
b.attribute("media-type", myContentType.getValue());
b.attribute("boundary", myBoundary);
b.startContent();
- for ( HttpResponseBody part : myParts ) {
+ for (final HttpResponseBody part : myParts) {
part.outputBody(b);
}
b.endElem();
- }
- catch ( ToolsException ex ) {
+ } catch (final ToolsException ex) {
throw new HttpClientException("Error building the body", ex);
}
}
- private void analyzeParts(Result result, InputStream in, String type)
- throws IOException
- , HttpClientException
- {
- MimeTokenStream parser = new MimeTokenStream();
- parser.parseHeadless(in, type);
+ private void analyzeParts(final Result result, final InputStream in) throws IOException, HttpClientException {
+ final MimeTokenStream parser = new MimeTokenStream();
+
+ final String contentType;
+ if (myContentType.getCharset() != null) {
+ contentType = myContentType.getType() + "; charset=" + myContentType.getCharset();
+ } else {
+ contentType = myContentType.getType();
+ }
+ parser.parseHeadless(in, contentType);
try {
HeaderSet headers = null;
- for ( EntityState state = parser.getState();
- state != EntityState.T_END_OF_STREAM;
- state = parser.next() )
- {
- if ( state == EntityState.T_START_HEADER ) {
+ for (EntityState state = parser.getState();
+ state != EntityState.T_END_OF_STREAM;
+ state = parser.next()) {
+ if (state == EntityState.T_START_HEADER) {
headers = new HeaderSet();
}
handleParserState(result, parser, headers);
}
- }
- catch ( MimeException ex ) {
+ } catch (final MimeException ex) {
throw new HttpClientException("The response content is ill-formed.", ex);
}
}
- private void handleParserState(Result result, MimeTokenStream parser, HeaderSet headers)
- throws HttpClientException
- {
- EntityState state = parser.getState();
- if ( LOG.isDebugEnabled() ) {
+ private void handleParserState(final Result result, final MimeTokenStream parser, final HeaderSet headers) throws HttpClientException {
+ final EntityState state = parser.getState();
+ if (LOG.isDebugEnabled()) {
LOG.debug(MimeTokenStream.stateToString(state));
}
- switch ( state ) {
+ switch (state) {
// It seems that in a headless parsing, END_HEADER appears
// right after START_MESSAGE (without the corresponding
// START_HEADER). So if headers == null, we can just ignore
@@ -117,17 +113,17 @@ private void handleParserState(Result result, MimeTokenStream parser, HeaderSet
// TODO: Just ignore anyway...?
break;
case T_FIELD:
- Field f = parser.getField();
- if ( LOG.isDebugEnabled() ) {
+ final Field f = parser.getField();
+ if (LOG.isDebugEnabled()) {
LOG.debug(" field: " + f);
}
headers.add(f.getName(), parseFieldBody(f));
break;
case T_BODY:
- if ( LOG.isDebugEnabled() ) {
+ if (LOG.isDebugEnabled()) {
LOG.debug(" body desc: " + parser.getBodyDescriptor());
}
- HttpResponseBody b = makeResponsePart(result, headers, parser);
+ final HttpResponseBody b = makeResponsePart(result, headers, parser);
myParts.add(b);
break;
// START_HEADER is handled in the calling analyzeParts()
@@ -147,21 +143,19 @@ private void handleParserState(Result result, MimeTokenStream parser, HeaderSet
// should discover slowly that we should probably just
// ignore some of them.
default:
- String s = MimeTokenStream.stateToString(state);
+ final String s = MimeTokenStream.stateToString(state);
throw new HttpClientException("Unknown parsing state: " + s);
}
}
- private String parseFieldBody(Field f)
- throws HttpClientException
- {
+ private String parseFieldBody(final Field f) {
// try {
- // WHy did I use AbstractField in the first place?
- String b = f.getBody() /* AbstractField.parse(f.getRaw()).getBody() */;
- if ( LOG.isDebugEnabled() ) {
- LOG.debug("Field: " + f.getName() + ": [" + b + "]");
- }
- return b;
+ // WHy did I use AbstractField in the first place?
+ final String b = f.getBody() /* AbstractField.parse(f.getRaw()).getBody() */;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Field: " + f.getName() + ": [" + b + "]");
+ }
+ return b;
// }
// catch ( MimeException ex ) {
// LOG.error("Field value parsing error (" + f + ")", ex);
@@ -169,37 +163,36 @@ private String parseFieldBody(Field f)
// }
}
- private HttpResponseBody makeResponsePart(Result result, HeaderSet headers, MimeTokenStream parser)
- throws HttpClientException
- {
- Header h = headers.getFirstHeader("Content-Type");
- if ( h == null ) {
+ private HttpResponseBody makeResponsePart(final Result result, final HeaderSet headers, final MimeTokenStream parser)
+ throws HttpClientException {
+ final Header h = headers.getFirstHeader("Content-Type");
+ if (h == null) {
throw new HttpClientException("impossible to find the content type");
}
- ContentType type = new ContentType(h);
- switch ( BodyFactory.parseType(type) ) {
- case XML: {
- // TODO: 'content_type' is the header Content-Type without any
- // param (i.e. "text/xml".) Should we keep this, or put the
- // whole header (i.e. "text/xml; charset=utf-8")? (and for
- // other types as well...)
- Reader in = parser.getReader();
- return new XmlResponseBody(result, in, type, headers, false);
- }
- case HTML: {
- Reader in = parser.getReader();
- return new XmlResponseBody(result, in, type, headers, true);
- }
- case TEXT: {
- Reader in = parser.getReader();
- return new TextResponseBody(result, in, type, headers);
- }
- case BINARY: {
- InputStream in = parser.getInputStream();
- return new BinaryResponseBody(result, in, type, headers);
+ final ContentType type = ContentType.parse(h, null, null);
+ try {
+ switch (BodyFactory.parseType(type)) {
+ case XML: {
+ final Reader in = parser.getReader();
+ return new XmlResponseBody(result, in, type, headers, false);
+ }
+ case HTML: {
+ final Reader in = parser.getReader();
+ return new XmlResponseBody(result, in, type, headers, true);
+ }
+ case TEXT: {
+ final Reader in = parser.getReader();
+ return new TextResponseBody(result, in, type, headers);
+ }
+ case BINARY: {
+ final InputStream in = parser.getInputStream();
+ return new BinaryResponseBody(result, in, type, headers);
+ }
+ default:
+ throw new HttpClientException("INTERNAL ERROR: cannot happen");
}
- default:
- throw new HttpClientException("INTERNAL ERROR: cannot happen");
+ } catch (final UnsupportedEncodingException ex) {
+ throw new HttpClientException("Unable to parse response part", ex);
}
}
diff --git a/http-client-java/src/org/expath/httpclient/impl/RequestParser.java b/http-client-java/src/main/java/org/expath/httpclient/impl/RequestParser.java
similarity index 88%
rename from http-client-java/src/org/expath/httpclient/impl/RequestParser.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/RequestParser.java
index ccca30a..6423baa 100644
--- a/http-client-java/src/org/expath/httpclient/impl/RequestParser.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/RequestParser.java
@@ -62,11 +62,6 @@ public HttpCredentials getCredentials()
return myCredentials;
}
- public boolean getSendAuth()
- {
- return mySendAuth;
- }
-
public HttpRequest parse(Sequence bodies, String href)
throws HttpClientException
{
@@ -89,9 +84,11 @@ public HttpRequest parse(Sequence bodies, String href)
// override-media-type? = string
// follow-redirect? = boolean
// timeout? = integer
+ // gzip? = boolean
+ // chunked? = boolean
for ( Attribute a : myRequest.attributes() ) {
String local = a.getLocalName();
- if ( !"".equals(a.getNamespaceUri()) ) {
+ if ( !(a.getNamespaceUri() == null || a.getNamespaceUri().isEmpty()) ) {
// ignore namespace qualified attributes
}
else if ( "method".equals(local) ) {
@@ -116,7 +113,10 @@ else if ( "auth-method".equals(local) ) {
auth_method = a.getValue();
}
else if ( "send-authorization".equals(local) ) {
- mySendAuth = toBoolean(a);
+ req.setPreemptiveAuthentication(toBoolean(a));
+ }
+ else if ( "default-charset".equals(local) ) {
+ req.setDefaultCharset(a.getValue());
}
else if ( "override-media-type".equals(local) ) {
req.setOverrideType(a.getValue());
@@ -127,6 +127,12 @@ else if ( "follow-redirect".equals(local) ) {
else if ( "timeout".equals(local) ) {
req.setTimeout(toInteger(a));
}
+ else if ( "gzip".equals(local) ) {
+ req.setGzip(toBoolean(a));
+ }
+ else if ( "chunked".equals(local) ) {
+ req.setChunked(toBoolean(a));
+ }
else {
throw new HttpClientException("Unknown attribute http:request/@" + local);
}
@@ -140,6 +146,9 @@ else if ( "timeout".equals(local) ) {
if ( username != null || password != null || auth_method != null ) {
setAuthentication(username, password, auth_method);
}
+ if(req.getHttpVersion() != null && req.getHttpVersion().equals(HttpConstants.HTTP_1_0) && req.isChunked()) {
+ throw new HttpClientException("Chunked transfer encoding can only be used with HTTP 1.1");
+ }
// walk the elements
// TODO: Check element structure validity (header*, (multipart|body)?)
@@ -148,7 +157,7 @@ else if ( "timeout".equals(local) ) {
for ( Element child : myRequest.children() ) {
String local = child.getLocalName();
String ns = child.getNamespaceUri();
- if ( "".equals(ns) ) {
+ if ( ns == null || ns.isEmpty() ) {
// elements in no namespace are an error
throw new HttpClientException("Element in no namespace: " + local);
}
@@ -200,7 +209,7 @@ private void addHeader(HeaderSet headers, Element e)
String value = null;
for ( Attribute a : e.attributes() ) {
String local = a.getLocalName();
- if ( !"".equals(a.getNamespaceUri()) ) {
+ if ( !(a.getNamespaceUri() == null || a.getNamespaceUri().isEmpty()) ) {
// ignore namespace qualified attributes
}
else if ( "name".equals(local) ) {
@@ -217,6 +226,15 @@ else if ( "value".equals(local) ) {
if ( name == null || value == null ) {
throw new HttpClientException("@name and @value are required on http:header");
}
+
+ if(name.equalsIgnoreCase("Content-Length")) {
+ throw new HttpClientException("Content-Length should not be explicitly provided, either it will automatically be added or Transfer-Encoding will be used.");
+ }
+
+ if(name.equalsIgnoreCase("Transfer-Encoding")) {
+ throw new HttpClientException("Transfer-Encoding should not be explicitly provided, it will automatically be added if required.");
+ }
+
// actually add the header
headers.add(name, value);
}
@@ -265,8 +283,6 @@ private int toInteger(Attribute a)
private String myOtherNs;
/** User credentials in case of authentication (from @username, @password and @auth-method). */
private HttpCredentials myCredentials = null;
- /** The value of @send-authorization. */
- private boolean mySendAuth = false;
}
diff --git a/http-client-java/src/org/expath/httpclient/impl/SinglePartRequestBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/SinglePartRequestBody.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/impl/SinglePartRequestBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/SinglePartRequestBody.java
diff --git a/http-client-java/src/org/expath/httpclient/impl/TextResponseBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/TextResponseBody.java
similarity index 60%
rename from http-client-java/src/org/expath/httpclient/impl/TextResponseBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/TextResponseBody.java
index 7c33c4c..cc5c418 100644
--- a/http-client-java/src/org/expath/httpclient/impl/TextResponseBody.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/TextResponseBody.java
@@ -9,12 +9,10 @@
package org.expath.httpclient.impl;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
import org.expath.httpclient.ContentType;
import org.expath.httpclient.HeaderSet;
import org.expath.httpclient.HttpClientException;
@@ -28,58 +26,44 @@
*
* @author Florent Georges
*/
-public class TextResponseBody
- implements HttpResponseBody
-{
- public TextResponseBody(Result result, InputStream in, ContentType type, HeaderSet headers)
- throws HttpClientException
- {
- // FIXME: ...
- String charset = "utf-8";
- try {
- Reader reader = new InputStreamReader(in, charset);
- init(result, reader, type, headers);
- }
- catch ( UnsupportedEncodingException ex ) {
- String msg = "not supported charset reading HTTP response: " + charset;
- throw new HttpClientException(msg, ex);
+public class TextResponseBody implements HttpResponseBody {
+
+ public static final Charset DEFAULT_HTTP_TEXT_CHARSET = StandardCharsets.ISO_8859_1;
+
+ public TextResponseBody(final Result result, final InputStream in, final ContentType type, final HeaderSet headers)
+ throws HttpClientException {
+ myContentType = type;
+ myHeaders = headers;
+
+ final Charset contentCharset;
+ if (type.getCharset() != null) {
+ contentCharset = Charset.forName(type.getCharset());
+ } else {
+ contentCharset = DEFAULT_HTTP_TEXT_CHARSET;
}
- }
- public TextResponseBody(Result result, Reader in, ContentType type, HeaderSet headers)
- throws HttpClientException
- {
- init(result, in, type, headers);
+ final Reader reader = new InputStreamReader(in, contentCharset);
+ result.add(reader, contentCharset);
}
- private void init(Result result, Reader in, ContentType type, HeaderSet headers)
- throws HttpClientException
- {
+ public TextResponseBody(final Result result, final Reader in, final ContentType type, final HeaderSet headers)
+ throws HttpClientException {
myContentType = type;
myHeaders = headers;
- // BufferedReader handles the ends of line (all \n, \r, and \r\n are
- // transformed to \n)
- try {
- StringBuilder builder = new StringBuilder();
- BufferedReader buf_in = new BufferedReader(in);
- String buf = null;
- while ( (buf = buf_in.readLine()) != null ) {
- builder.append(buf);
- builder.append('\n');
- }
- String value = builder.toString();
- result.add(value);
- }
- catch ( IOException ex ) {
- throw new HttpClientException("error reading HTTP response", ex);
+
+ final Charset contentCharset;
+ if (type.getCharset() != null) {
+ contentCharset = Charset.forName(type.getCharset());
+ } else {
+ contentCharset = DEFAULT_HTTP_TEXT_CHARSET;
}
+
+ result.add(in, contentCharset);
}
@Override
- public void outputBody(TreeBuilder b)
- throws HttpClientException
- {
- if ( myHeaders != null ) {
+ public void outputBody(final TreeBuilder b) throws HttpClientException {
+ if (myHeaders != null) {
b.outputHeaders(myHeaders);
}
try {
@@ -88,14 +72,13 @@ public void outputBody(TreeBuilder b)
// TODO: Support other attributes as well?
b.startContent();
b.endElem();
- }
- catch ( ToolsException ex ) {
+ } catch (ToolsException ex) {
throw new HttpClientException("Error building the body", ex);
}
}
- private ContentType myContentType;
- private HeaderSet myHeaders;
+ private final ContentType myContentType;
+ private final HeaderSet myHeaders;
}
diff --git a/http-client-java/src/org/expath/httpclient/impl/XmlResponseBody.java b/http-client-java/src/main/java/org/expath/httpclient/impl/XmlResponseBody.java
similarity index 72%
rename from http-client-java/src/org/expath/httpclient/impl/XmlResponseBody.java
rename to http-client-java/src/main/java/org/expath/httpclient/impl/XmlResponseBody.java
index 6473876..400fe29 100644
--- a/http-client-java/src/org/expath/httpclient/impl/XmlResponseBody.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/impl/XmlResponseBody.java
@@ -12,10 +12,12 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
+
import org.ccil.cowan.tagsoup.Parser;
import org.expath.httpclient.ContentType;
import org.expath.httpclient.HeaderSet;
@@ -32,61 +34,56 @@
*
* @author Florent Georges
*/
-public class XmlResponseBody
- implements HttpResponseBody
-{
- public XmlResponseBody(Result result, InputStream in, ContentType type, HeaderSet headers, boolean html)
- throws HttpClientException
- {
- // TODO: ...
- String charset = "utf-8";
- try {
- Reader reader = new InputStreamReader(in, charset);
- init(result, reader, type, headers, html);
- }
- catch ( UnsupportedEncodingException ex ) {
- String msg = "not supported charset reading HTTP response: " + charset;
- throw new HttpClientException(msg, ex);
+public class XmlResponseBody implements HttpResponseBody {
+
+ public static final Charset DEFAULT_HTTP_APPLICATION_XML_CHARSET = StandardCharsets.UTF_8;
+
+ public XmlResponseBody(final Result result, final InputStream in, final ContentType type, final HeaderSet headers, final boolean html)
+ throws HttpClientException {
+
+ final Charset contentCharset;
+ if (type.getCharset() != null) {
+ contentCharset = Charset.forName(type.getCharset());
+ } else {
+ contentCharset = DEFAULT_HTTP_APPLICATION_XML_CHARSET;
}
+
+ final Reader reader = new InputStreamReader(in, contentCharset);
+ init(result, reader, type, headers, html);
}
- public XmlResponseBody(Result result, Reader in, ContentType type, HeaderSet headers, boolean html)
- throws HttpClientException
- {
+ public XmlResponseBody(final Result result, final Reader in, final ContentType type, final HeaderSet headers, final boolean html)
+ throws HttpClientException {
init(result, in, type, headers, html);
}
- private void init(Result result, Reader in, ContentType type, HeaderSet headers, boolean html)
- throws HttpClientException
- {
+ private void init(final Result result, final Reader in, final ContentType type, final HeaderSet headers, final boolean html)
+ throws HttpClientException {
myContentType = type;
myHeaders = headers;
String sys_id = "TODO-find-a-useful-systemId";
try {
Source src;
- if ( html ) {
- Parser parser = new Parser();
+ if (html) {
+ final Parser parser = new Parser();
parser.setFeature(Parser.namespacesFeature, true);
parser.setFeature(Parser.namespacePrefixesFeature, true);
- InputSource input = new InputSource(in);
+ final InputSource input = new InputSource(in);
src = new SAXSource(parser, input);
src.setSystemId(sys_id);
- }
- else {
+ } else {
src = new StreamSource(in, sys_id);
}
result.add(src);
- }
- catch ( SAXException ex ) {
+ } catch (SAXException ex) {
throw new HttpClientException("error parsing result HTML", ex);
}
}
@Override
- public void outputBody(TreeBuilder b)
- throws HttpClientException
- {
- if ( myHeaders != null ) {
+ public void outputBody(final TreeBuilder b)
+ throws HttpClientException {
+ if (myHeaders != null) {
b.outputHeaders(myHeaders);
}
try {
@@ -95,8 +92,7 @@ public void outputBody(TreeBuilder b)
// TODO: Support other attributes as well?
b.startContent();
b.endElem();
- }
- catch ( ToolsException ex ) {
+ } catch (ToolsException ex) {
throw new HttpClientException("Error building the body", ex);
}
}
diff --git a/http-client-java/src/org/expath/httpclient/model/Result.java b/http-client-java/src/main/java/org/expath/httpclient/model/Result.java
similarity index 93%
rename from http-client-java/src/org/expath/httpclient/model/Result.java
rename to http-client-java/src/main/java/org/expath/httpclient/model/Result.java
index c16dc9f..642bddb 100644
--- a/http-client-java/src/org/expath/httpclient/model/Result.java
+++ b/http-client-java/src/main/java/org/expath/httpclient/model/Result.java
@@ -9,7 +9,11 @@
package org.expath.httpclient.model;
+import java.io.Reader;
+import java.io.InputStream;
+import java.nio.charset.Charset;
import javax.xml.transform.Source;
+
import org.expath.httpclient.HttpClientException;
import org.expath.httpclient.HttpResponse;
@@ -63,22 +67,22 @@ public Result makeNewResult()
throws HttpClientException;
/**
- * Add an {@code xs:string} to the result sequence.
+ * Add a string value to the result sequence.
*
* @param string The string to add to the result sequence.
* @throws HttpClientException If any error occurs.
*/
- public void add(String string)
+ public void add(Reader string, Charset encoding)
throws HttpClientException;
/**
- * Add an {@code xs:base64Binary} to the result sequence.
+ * Add raw binary to the result sequence.
*
- * @param bytes The bytes representing the base64 binary item to add to the
+ * @param bytes The bytes representing the binary item to add to the
* result sequence.
* @throws HttpClientException If any error occurs.
*/
- public void add(byte[] bytes)
+ public void add(InputStream bytes)
throws HttpClientException;
/**
@@ -124,5 +128,5 @@ public void add(HttpResponse response)
/* */
/* The Initial Developer of the Original Code is Florent Georges. */
/* */
-/* Contributor(s): none. */
+/* Contributor(s): Adam Retter */
/* ------------------------------------------------------------------------ */
diff --git a/http-client-java/src/org/expath/httpclient/model/TreeBuilder.java b/http-client-java/src/main/java/org/expath/httpclient/model/TreeBuilder.java
similarity index 100%
rename from http-client-java/src/org/expath/httpclient/model/TreeBuilder.java
rename to http-client-java/src/main/java/org/expath/httpclient/model/TreeBuilder.java
diff --git a/http-client-java/src/org/expath/httpclient/ContentType.java b/http-client-java/src/org/expath/httpclient/ContentType.java
deleted file mode 100644
index df9e0de..0000000
--- a/http-client-java/src/org/expath/httpclient/ContentType.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************************/
-/* File: ContentType.java */
-/* Author: F. Georges - fgeorges.org */
-/* Date: 2009-02-22 */
-/* Tags: */
-/* Copyright (c) 2009 Florent Georges (see end of file.) */
-/* ------------------------------------------------------------------------ */
-
-
-package org.expath.httpclient;
-
-import org.apache.http.Header;
-import org.apache.http.HeaderElement;
-import org.apache.http.NameValuePair;
-
-/**
- * Represent a Content-Type header.
- *
- * Provide the ability to get the boundary param in case of a multipart
- * content type on the one hand, and the ability to get only the MIME type
- * string without any param on the other hand.
- *
- * @author Florent Georges
- */
-public class ContentType
-{
- public ContentType(String type, String boundary)
- {
- myHeader = null;
- myType = type;
- myBoundary = boundary;
- }
-
- public ContentType(Header h)
- throws HttpClientException
- {
- if ( h == null ) {
- throw new HttpClientException("Header is null");
- }
- if ( ! "Content-Type".equalsIgnoreCase(h.getName()) ) {
- throw new HttpClientException("Header is not content type");
- }
- myHeader = h;
- myType = HeaderSet.getHeaderWithoutParam(myHeader);
- HeaderElement[] elems = h.getElements();
- if ( elems != null ) {
- for ( HeaderElement e : elems ) {
- for ( NameValuePair p : e.getParameters() ) {
- if ( "boundary".equals(p.getName()) ) {
- myBoundary = p.getValue();
- }
- }
- }
- }
- }
-
- @Override
- public String toString()
- {
- if ( myHeader == null ) {
- return "Content-Type: " + getValue();
- }
- else {
- return myHeader.toString();
- }
- }
-
- public String getType()
- {
- return myType;
- }
-
- public String getBoundary()
- {
- return myBoundary;
- }
-
- public String getValue()
- {
- // TODO: Why did I add the boundary before...?
-// if ( myHeader == null ) {
-// StringBuilder b = new StringBuilder();
-// b.append(myType);
-// if ( myBoundary != null ) {
-// b.append("; boundary=\"");
-// // TODO: Is that correct escaping sequence?
-// b.append(myBoundary.replace("\"", "\\\""));
-// b.append("\"");
-// }
-// return b.toString();
-// }
- if ( myType != null ) {
- return myType;
- }
- if ( myHeader != null ) {
- return myHeader.getValue();
- }
- else {
- return null;
- }
- }
-
- private Header myHeader;
- private String myType;
- private String myBoundary;
-}
-
-
-/* ------------------------------------------------------------------------ */
-/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT. */
-/* */
-/* The contents of this file are subject to the Mozilla Public License */
-/* Version 1.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.mozilla.org/MPL/. */
-/* */
-/* Software distributed under the License is distributed on an "AS IS" */
-/* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */
-/* the License for the specific language governing rights and limitations */
-/* under the License. */
-/* */
-/* The Original Code is: all this file. */
-/* */
-/* The Initial Developer of the Original Code is Florent Georges. */
-/* */
-/* Contributor(s): none. */
-/* ------------------------------------------------------------------------ */
diff --git a/http-client-java/test/tmp/tests/ApacheHttpClientTest.java b/http-client-java/src/test/java/tmp/tests/ApacheHttpClientTest.java
similarity index 61%
rename from http-client-java/test/tmp/tests/ApacheHttpClientTest.java
rename to http-client-java/src/test/java/tmp/tests/ApacheHttpClientTest.java
index afe0a3c..6d0ef39 100644
--- a/http-client-java/test/tmp/tests/ApacheHttpClientTest.java
+++ b/http-client-java/src/test/java/tmp/tests/ApacheHttpClientTest.java
@@ -7,7 +7,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
-import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
@@ -21,22 +20,21 @@
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
-import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.conn.routing.HttpRoutePlanner;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
-import org.apache.http.impl.client.AbstractHttpClient;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
+import org.apache.http.ssl.SSLContextBuilder;
import org.junit.Ignore;
import org.junit.Test;
+import javax.net.ssl.SSLContext;
+
/**
*
* @author Florent Georges - fgeorges.org
@@ -45,14 +43,16 @@ public class ApacheHttpClientTest
{
@Test
public void testGetMethod()
- throws ClientProtocolException, IOException
+ throws IOException
{
HttpGet get = new HttpGet("http://www.fgeorges.org/");
- HttpResponse response = getClient().execute(get);
- System.err.println("Status: " + response.getStatusLine().getStatusCode());
- System.err.print("Content: ");
- response.getEntity().writeTo(System.err);
- System.err.println();
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse response = client.execute(get);
+ System.err.println("Status: " + response.getStatusLine().getStatusCode());
+ System.err.print("Content: ");
+ response.getEntity().writeTo(System.err);
+ System.err.println();
+ }
}
/**
@@ -60,70 +60,80 @@ public void testGetMethod()
*/
@Test
public void testGoogleAuth()
- throws ClientProtocolException, IOException
+ throws IOException
{
HttpPost post = new HttpPost("https://www.google.com/accounts/ClientLogin");
ContentProducer producer = new StringProducer(AUTH_CONTENT);
EntityTemplate entity = new EntityTemplate(producer);
entity.setContentType(FORM_TYPE);
post.setEntity(entity);
- HttpResponse resp = getClient().execute(post);
- System.err.println("Status: " + resp.getStatusLine().getStatusCode());
- System.err.print("Content: ");
- resp.getEntity().writeTo(System.err);
- System.err.println();
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(post);
+ System.err.println("Status: " + resp.getStatusLine().getStatusCode());
+ System.err.print("Content: ");
+ resp.getEntity().writeTo(System.err);
+ System.err.println();
+ }
}
@Test
public void testGoogleRedirect()
- throws ClientProtocolException, IOException
+ throws IOException
{
HttpPost post = new HttpPost("https://www.google.com/accounts/ClientLogin");
ContentProducer producer = new StringProducer(AUTH_CONTENT);
EntityTemplate entity = new EntityTemplate(producer);
entity.setContentType(FORM_TYPE);
post.setEntity(entity);
- AbstractHttpClient client = getClient();
- HttpResponse resp = client.execute(post);
- System.err.println("POST status: " + resp.getStatusLine().getStatusCode());
String token = null;
- for ( String s : getStringContent(resp).split("\n") ) {
- if ( s.startsWith("Auth=") ) {
- token = s.substring(5);
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(post);
+ System.err.println("POST status: " + resp.getStatusLine().getStatusCode());
+ for (String s : getStringContent(resp).split("\n")) {
+ if (s.startsWith("Auth=")) {
+ token = s.substring(5);
+ }
}
+ System.err.println("Token: " + token);
}
- System.err.println("Token: " + token);
+
// GetMethod get = new GetMethod("https://www.google.com/calendar/feeds/default/allcalendars/full");
HttpGet get = new HttpGet("http://www.google.com/calendar/feeds/xmlprague.cz_k0rlr8da52ivmgp6eujip041s8%40group.calendar.google.com/private/full");
get.setHeader("GData-Version", "2");
get.setHeader("Authorization", "GoogleLogin auth=" + token);
// get.setFollowRedirects(false);
- resp = client.execute(get);
- System.err.println("GET status: " + resp.getStatusLine().getStatusCode());
- for ( Cookie c : client.getCookieStore().getCookies() ) {
- System.err.println("Cookie: " + c.getName() + ", " + c.getValue());
- }
- for ( String s : client.getCookieSpecs().getSpecNames() ) {
- System.err.println("Cookie spec: " + s);
- }
- if ( resp.getStatusLine().getStatusCode() == 302 ) {
- client = getClient();
- resp = client.execute(get);
+ HttpClientContext context = HttpClientContext.create();
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(get, context);
System.err.println("GET status: " + resp.getStatusLine().getStatusCode());
+
+ for (Cookie c : context.getCookieStore().getCookies()) {
+ System.err.println("Cookie: " + c.getName() + ", " + c.getValue());
+ }
+ System.err.println("Cookie spec: " + context.getCookieSpec());
+
+ if (resp.getStatusLine().getStatusCode() == 302) {
+ try(final CloseableHttpClient client2 = getClient()) {
+ final HttpResponse resp2 = client2.execute(get);
+ System.err.println("GET status: " + resp2.getStatusLine().getStatusCode());
+ }
+ }
}
+
HttpGet get2 = new HttpGet("http://www.google.com/calendar/feeds/xmlprague.cz_k0rlr8da52ivmgp6eujip041s8%40group.calendar.google.com/private/full");
get2.setHeader("GData-Version", "2");
get2.setHeader("Authorization", "GoogleLogin auth=" + token);
// get2.setFollowRedirects(false);
- client = getClient();
- resp = client.execute(get2);
- System.err.println("GET 2 status: " + resp.getStatusLine().getStatusCode());
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(get2);
+ System.err.println("GET 2 status: " + resp.getStatusLine().getStatusCode());
+ }
}
@Ignore("Broken authentication?!?")
@Test
public void testGoogleAddAgenda()
- throws ClientProtocolException, IOException
+ throws IOException
{
System.err.println();
System.err.println("***** [testGoogleAddAgenda]");
@@ -137,8 +147,8 @@ public void testGoogleAddAgenda()
System.err.println("***** [/testGoogleAddAgenda]");
}
- public HttpResponse testGoogleAddAgenda_1(String token, String uri)
- throws ClientProtocolException, IOException
+ private HttpResponse testGoogleAddAgenda_1(String token, String uri)
+ throws IOException
{
HttpPost post = new HttpPost(uri);
post.setHeader("GData-Version", "2");
@@ -146,19 +156,21 @@ public HttpResponse testGoogleAddAgenda_1(String token, String uri)
EntityTemplate entity = new EntityTemplate(new StringProducer(AGENDA_ENTRY));
entity.setContentType(ATOM_TYPE);
post.setEntity(entity);
- HttpResponse resp = getClient().execute(post);
- System.err.println("POST status: " + resp.getStatusLine().getStatusCode());
- System.err.println("POST message: " + resp.getStatusLine().getReasonPhrase());
- System.err.print("POST response content: ");
- resp.getEntity().writeTo(System.err);
- System.err.println();
- return resp;
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(post);
+ System.err.println("POST status: " + resp.getStatusLine().getStatusCode());
+ System.err.println("POST message: " + resp.getStatusLine().getReasonPhrase());
+ System.err.print("POST response content: ");
+ resp.getEntity().writeTo(System.err);
+ System.err.println();
+ return resp;
+ }
}
@Ignore("Broken authentication?!?")
@Test
public void testGoogleAddAgendaStd()
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
System.err.println();
System.err.println("***** [testGoogleAddAgendaStd]");
@@ -174,7 +186,7 @@ public void testGoogleAddAgendaStd()
}
private HttpURLConnection testGoogleAddAgendaStd_1(String token, URI uri)
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection();
conn.setRequestMethod("POST");
@@ -200,44 +212,48 @@ private String authenticate()
EntityTemplate entity = new EntityTemplate(new StringProducer(AUTH_CONTENT));
entity.setContentType(FORM_TYPE);
auth.setEntity(entity);
- HttpResponse resp = getClient().execute(auth);
- System.err.println("AUTH status: " + resp.getStatusLine().getStatusCode());
- String token = null;
- for ( String s : getStringContent(resp).split("\n") ) {
- System.err.println("AUTH content line: " + s);
- if ( s.startsWith("Auth=") ) {
- token = s.substring(5);
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(auth);
+ System.err.println("AUTH status: " + resp.getStatusLine().getStatusCode());
+ String token = null;
+ for (String s : getStringContent(resp).split("\n")) {
+ System.err.println("AUTH content line: " + s);
+ if (s.startsWith("Auth=")) {
+ token = s.substring(5);
+ }
}
+ if (token == null) {
+ throw new RuntimeException("Token is null!");
+ }
+ return token;
}
- if ( token == null ) {
- throw new RuntimeException("Token is null!");
- }
- return token;
}
@Test
public void testResponseBody()
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
System.err.println();
System.err.println("***** [testResponseBody]");
HttpGet get = new HttpGet("http://www.fgeorges.org/tmp/xproc-fixed-alternative.mpr-");
- HttpResponse resp = getClient().execute(get);
- System.err.println("Status: " + resp.getStatusLine().getStatusCode());
- HttpEntity entity = resp.getEntity();
- System.err.println("Entity class: " + entity.getClass());
- System.err.println("Entity type: " + entity.getContentType());
- System.err.println("Entity encoding: " + entity.getContentEncoding());
- System.err.println("Entity is chunck: " + entity.isChunked());
- System.err.println("Entity is repeat: " + entity.isRepeatable());
- System.err.println("Entity is stream: " + entity.isStreaming());
- System.err.println("***** [/testResponseBody]");
+ try(final CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(get);
+ System.err.println("Status: " + resp.getStatusLine().getStatusCode());
+ HttpEntity entity = resp.getEntity();
+ System.err.println("Entity class: " + entity.getClass());
+ System.err.println("Entity type: " + entity.getContentType());
+ System.err.println("Entity encoding: " + entity.getContentEncoding());
+ System.err.println("Entity is chunck: " + entity.isChunked());
+ System.err.println("Entity is repeat: " + entity.isRepeatable());
+ System.err.println("Entity is stream: " + entity.isStreaming());
+ System.err.println("***** [/testResponseBody]");
+ }
}
@Ignore("Need a certificate file, see 'Unable to find the certificate file' below")
@Test
public void testTrustSelfSignedKeys()
- throws ClientProtocolException, IOException, URISyntaxException,
+ throws IOException, URISyntaxException,
KeyStoreException, NoSuchAlgorithmException,
CertificateException, KeyManagementException, UnrecoverableKeyException
{
@@ -255,24 +271,28 @@ public void testTrustSelfSignedKeys()
InputStream in = new FileInputStream(in_f);
Certificate certif = factory.generateCertificate(in);
trustStore.setCertificateEntry("fgeorges.org", certif);
- SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
- Scheme sch = new Scheme("https", socketFactory, 443);
- AbstractHttpClient client = getClient();
- client.getConnectionManager().getSchemeRegistry().register(sch);
- //
- System.err.println();
- System.err.println("***** [testTrustSelfSignedKeys]");
- HttpGet get = new HttpGet("https://www.fgeorges.org/");
- //HttpGet get = new HttpGet("https://mail.google.com/");
- HttpResponse resp = client.execute(get);
- System.err.println("Status: " + resp.getStatusLine().getStatusCode());
- System.err.println("***** [/testTrustSelfSignedKeys]");
+ final SSLContext sslContext = SSLContextBuilder.create()
+ .loadTrustMaterial(trustStore, null)
+ .build();
+
+ try (CloseableHttpClient client = getClient(sslContext)) {
+
+ //
+
+ System.err.println();
+ System.err.println("***** [testTrustSelfSignedKeys]");
+ HttpGet get = new HttpGet("https://www.fgeorges.org/");
+ //HttpGet get = new HttpGet("https://mail.google.com/");
+ HttpResponse resp = client.execute(get);
+ System.err.println("Status: " + resp.getStatusLine().getStatusCode());
+ System.err.println("***** [/testTrustSelfSignedKeys]");
+ }
}
@Test
public void testXProcPost()
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
System.err.println();
System.err.println("***** [testXProcPost]");
@@ -284,7 +304,7 @@ public void testXProcPost()
@Test
public void testFGeorgesPost()
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
System.err.println();
System.err.println("***** [testFGeorgesPost]");
@@ -295,31 +315,32 @@ public void testFGeorgesPost()
}
private void doPost(String uri, String content, String type)
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
HttpPost post = new HttpPost(uri);
EntityTemplate entity = new EntityTemplate(new StringProducer(content));
entity.setContentType(type);
post.setEntity(entity);
- AbstractHttpClient client = getClient();
- System.err.println("DEBUG: CLIENT: " + client.getClass());
- HttpResponse resp = client.execute(post);
- System.err.println("Status: " + resp.getStatusLine().getStatusCode());
- HttpEntity body = resp.getEntity();
- System.err.println("Entity class: " + body.getClass());
- System.err.println("Entity type: " + body.getContentType());
- System.err.println("Entity encoding: " + body.getContentEncoding());
- System.err.println("Entity is chunck: " + body.isChunked());
- System.err.println("Entity is repeat: " + body.isRepeatable());
- System.err.println("Entity is stream: " + body.isStreaming());
- System.err.println("Entity body: ");
- body.writeTo(System.err);
- System.err.println();
+ try(CloseableHttpClient client = getClient()) {
+ System.err.println("DEBUG: CLIENT: " + client.getClass());
+ HttpResponse resp = client.execute(post);
+ System.err.println("Status: " + resp.getStatusLine().getStatusCode());
+ HttpEntity body = resp.getEntity();
+ System.err.println("Entity class: " + body.getClass());
+ System.err.println("Entity type: " + body.getContentType());
+ System.err.println("Entity encoding: " + body.getContentEncoding());
+ System.err.println("Entity is chunck: " + body.isChunked());
+ System.err.println("Entity is repeat: " + body.isRepeatable());
+ System.err.println("Entity is stream: " + body.isStreaming());
+ System.err.println("Entity body: ");
+ body.writeTo(System.err);
+ System.err.println();
+ }
}
@Test
public void testPost()
- throws ClientProtocolException, IOException, URISyntaxException
+ throws IOException, URISyntaxException
{
System.err.println();
System.err.println("***** [testPost]");
@@ -328,10 +349,12 @@ public void testPost()
EntityTemplate entity = new EntityTemplate(new StringProducer(""));
entity.setContentType(XML_TYPE);
post.setEntity(entity);
- HttpResponse resp = getClient().execute(post);
- System.err.println("Status: " + resp.getStatusLine().getStatusCode());
- System.err.println();
- System.err.println("***** [/testPost]");
+ try(CloseableHttpClient client = getClient()) {
+ HttpResponse resp = client.execute(post);
+ System.err.println("Status: " + resp.getStatusLine().getStatusCode());
+ System.err.println();
+ System.err.println("***** [/testPost]");
+ }
}
private String getStringContent(HttpResponse resp)
@@ -364,23 +387,30 @@ public void writeTo(OutputStream out) throws IOException {
private byte[] myContent;
}
- private static AbstractHttpClient getClient()
+ private static CloseableHttpClient getClient()
+ {
+ // FIXME: TODO: How to manage and reuse connections? In test cases, but
+ // also in production code...
+ return getClient(null);
+// return CLIENT;
+ }
+
+ private static CloseableHttpClient getClient(final SSLContext sslContext)
{
// FIXME: TODO: How to manage and reuse connections? In test cases, but
// also in production code...
- return makeNewClient();
+ return makeNewClient(sslContext);
// return CLIENT;
}
- private static AbstractHttpClient makeNewClient()
+ private static CloseableHttpClient makeNewClient(final SSLContext sslContext)
{
- AbstractHttpClient client = new DefaultHttpClient();
- HttpRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
- client.getConnectionManager().getSchemeRegistry(),
- ProxySelector.getDefault());
- client.setRoutePlanner(routePlanner);
- client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
- return client;
+ final HttpClientBuilder clientBuilder = HttpClientBuilder.create();
+ if(sslContext != null) {
+ clientBuilder.setSSLContext(sslContext);
+ }
+ clientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(null));
+ return clientBuilder.build();
}
static {
@@ -397,7 +427,7 @@ private static AbstractHttpClient makeNewClient()
private static final String FORM_TYPE = "application/x-www-form-urlencoded";
private static final String ATOM_TYPE = "application/atom+xml";
private static final String XML_TYPE = "application/xml";
- private static final AbstractHttpClient CLIENT = makeNewClient();
+ private static final CloseableHttpClient CLIENT = makeNewClient(null);
// private static final AbstractHttpClient CLIENT = new DefaultHttpClient();
// static {
// HttpRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
diff --git a/http-client-parent/pom.xml b/http-client-parent/pom.xml
new file mode 100644
index 0000000..45a33a4
--- /dev/null
+++ b/http-client-parent/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+
+
+ org.expath
+ expath-parent
+ 1.0-SNAPSHOT
+
+
+
+ org.expath.http.client
+ http-client-parent
+ pom
+
+ EXPath HTTP Client POM
+ The base POM for all EXPath HTTP Client modules.
+
+
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ http://github.com/adamretter/expath-http-client-java
+
+
+
+ 1.0-SNAPSHOT
+
+
+
+
+
+
+ maven-assembly-plugin
+ 3.1.1
+
+
+
+
+
+
+
+
+ org.expath.tools
+ tools-java
+ ${expath.tools.version}
+
+
+ org.apache.httpcomponents
+ httpcore
+ 4.4.11
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.8
+
+
+
+
+
diff --git a/http-client-saxon/pom.xml b/http-client-saxon/pom.xml
new file mode 100644
index 0000000..3c139be
--- /dev/null
+++ b/http-client-saxon/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+
+
+ org.expath.http.client
+ http-client-parent
+ 1.0-SNAPSHOT
+ ../http-client-parent/pom.xml
+
+
+ http-client-saxon
+ jar
+
+ EXPath HTTP Client Saxon Implementation
+ Implementation of HTTP Client module for Saxon
+
+
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ scm:git:git://github.com/adamretter/expath-http-client-java.git
+ http://github.com/adamretter/expath-http-client-java
+
+
+
+
+ org.expath.tools
+ tools-java
+
+
+ org.expath.tools
+ tools-saxon
+ ${expath.tools.version}
+
+
+ ${project.groupId}
+ http-client-java
+ ${project.version}
+
+
+ org.expath.packaging
+ pkg-saxon
+ 1.0-SNAPSHOT
+
+
+ net.sf.saxon
+ Saxon-HE
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+
+ junit
+ junit
+ test
+
+
+
+
\ No newline at end of file
diff --git a/http-client-saxon/src/org/expath/httpclient/saxon/SaxonResult.java b/http-client-saxon/src/main/java/org/expath/httpclient/saxon/SaxonResult.java
similarity index 78%
rename from http-client-saxon/src/org/expath/httpclient/saxon/SaxonResult.java
rename to http-client-saxon/src/main/java/org/expath/httpclient/saxon/SaxonResult.java
index 14f71a9..9a5e676 100644
--- a/http-client-saxon/src/org/expath/httpclient/saxon/SaxonResult.java
+++ b/http-client-saxon/src/main/java/org/expath/httpclient/saxon/SaxonResult.java
@@ -9,6 +9,8 @@
package org.expath.httpclient.saxon;
+import java.io.*;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.xml.transform.Source;
@@ -50,19 +52,44 @@ public Result makeNewResult()
}
@Override
- public void add(String string)
+ public void add(Reader reader, Charset charset)
throws HttpClientException
{
- Item item = new StringValue(string);
- myItems.add(item);
+ try(final BufferedReader buf_in = new BufferedReader(reader)) {
+ final StringBuilder builder = new StringBuilder();
+
+ String buf = null;
+ while ( (buf = buf_in.readLine()) != null ) {
+ builder.append(buf);
+ builder.append('\n');
+ }
+ final String value = builder.toString();
+
+ Item item = new StringValue(value);
+ myItems.add(item);
+ }
+ catch ( final IOException ex ) {
+ throw new HttpClientException("error reading HTTP response", ex);
+ }
}
@Override
- public void add(byte[] bytes)
+ public void add(InputStream inputStream)
throws HttpClientException
{
- Item item = new Base64BinaryValue(bytes);
- myItems.add(item);
+ try(final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ final byte[] buf = new byte[4096];
+ int read = -1;
+ while ( (read = inputStream.read(buf)) > 0 ) {
+ out.write(buf, 0, read);
+ }
+ final byte[] bytes = out.toByteArray();
+
+ Item item = new Base64BinaryValue(bytes);
+ myItems.add(item);
+ } catch(final IOException e) {
+ throw new HttpClientException(e.getMessage(), e);
+ }
}
@Override
diff --git a/http-client-saxon/src/org/expath/httpclient/saxon/SaxonTreeBuilder.java b/http-client-saxon/src/main/java/org/expath/httpclient/saxon/SaxonTreeBuilder.java
similarity index 100%
rename from http-client-saxon/src/org/expath/httpclient/saxon/SaxonTreeBuilder.java
rename to http-client-saxon/src/main/java/org/expath/httpclient/saxon/SaxonTreeBuilder.java
diff --git a/http-client-saxon/src/org/expath/httpclient/saxon/SendRequestCall.java b/http-client-saxon/src/main/java/org/expath/httpclient/saxon/SendRequestCall.java
similarity index 100%
rename from http-client-saxon/src/org/expath/httpclient/saxon/SendRequestCall.java
rename to http-client-saxon/src/main/java/org/expath/httpclient/saxon/SendRequestCall.java
diff --git a/http-client-saxon/src/org/expath/httpclient/saxon/SendRequestFunction.java b/http-client-saxon/src/main/java/org/expath/httpclient/saxon/SendRequestFunction.java
similarity index 100%
rename from http-client-saxon/src/org/expath/httpclient/saxon/SendRequestFunction.java
rename to http-client-saxon/src/main/java/org/expath/httpclient/saxon/SendRequestFunction.java
diff --git a/http-client-saxon/test/org/fgeorges/google/GContactTest.java b/http-client-saxon/src/test/java/org/fgeorges/google/GContactTest.java
similarity index 100%
rename from http-client-saxon/test/org/fgeorges/google/GContactTest.java
rename to http-client-saxon/src/test/java/org/fgeorges/google/GContactTest.java
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7a0c9d7
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+
+ org.expath.http.client
+ http-client-parent
+ 1.0-SNAPSHOT
+ http-client-parent/pom.xml
+
+
+ http-client
+ pom
+
+ EXPath HTTP Client
+
+ EXPath HTTP Client module.
+
+
+ http-client-parent
+ http-client-java
+ http-client-saxon
+
+
+
\ No newline at end of file