Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added support for Apache HttpClient based connections #250

Closed
wants to merge 3 commits into from

8 participants

@mohankishore
  • Refactored the Request class to introduce a Strategy pattern.
  • Moved the current URLConnection based logic into a class called URLConnectionStrategy
  • Added a new HttpClientStrategy
  • Also refactored the Response class a little to make it "extendable"
  • Added a HttpClientResponse class that wraps the response from apache httpclient library
@rgngl

This is cool. I had some patches to use MultipartEntity from the same package to work with HttpURLConnection. I guess I'll wait until this gets merged to publish them.

@philihp

good to know i wasn't the only one annoyed by this

Mohan Kishore added content-type 20f84fe
@jplock

+1

@pmenon

+1 on the idea! You'll have to rebase to properly apply the patch against HEAD.

My preference would be to leave this as generic as possible. Allowing clients to use a custom http connection strategy is invaluable, especially for high-volume apps which mostly use existing connection pools. Can we leave out the maven dependency and apache implementation? This might be the easiest way to get it included.

@boncey boncey referenced this pull request in callmeal/Flickr4Java
Open

Unable to upload large files #36

@kipz

+1 from me! This would be awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 19, 2012
  1. Added support for HttpClient based connections

    Mohan Kishore authored
Commits on Apr 25, 2012
  1. fixed a NPE in HttpClientResponse

    Mohan Kishore authored
Commits on May 23, 2012
  1. added content-type

    Mohan Kishore authored
This page is out of date. Refresh to see the latest.
View
10 pom.xml
@@ -4,7 +4,7 @@
<groupId>org.scribe</groupId>
<artifactId>scribe</artifactId>
<packaging>jar</packaging>
- <version>1.3.0</version>
+ <version>1.3.0-patched</version>
<name>Scribe OAuth Library</name>
<description>The best OAuth library out there</description>
<url>http://github.com/fernandezpablo85/scribe-java</url>
@@ -50,6 +50,14 @@
<version>1.4</version>
</dependency>
+ <!-- If available, will be used for HTTP communication by Scribe -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.3</version>
+ <optional>true</optional>
+ </dependency>
+
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
View
1  src/main/java/org/scribe/extractors/JsonTokenExtractor.java
@@ -10,7 +10,6 @@
{
private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\"");
- @Override
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String");
View
36 src/main/java/org/scribe/model/HttpClientResponse.java
@@ -0,0 +1,36 @@
+package org.scribe.model;
+
+import java.util.HashMap;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpResponse;
+import org.scribe.exceptions.OAuthException;
+
+/**
+ * Provides an implementation of Scribe Response class that wraps an
+ * Apache HttpResponse object (instead of the default URLConnection based
+ * implementation)
+ *
+ * @author mkishor
+ */
+public class HttpClientResponse extends Response {
+
+ public HttpClientResponse(HttpResponse response) {
+ this.code = response.getStatusLine().getStatusCode();
+ this.headers = new HashMap<String, String>();
+ for (HeaderIterator iter = response.headerIterator(); iter.hasNext(); ) {
+ Header header = iter.nextHeader();
+ headers.put(header.getName(), header.getValue());
+ }
+ try {
+ this.stream = response.getEntity().getContent();
+ } catch (Exception e) {
+ throw new OAuthException("Error reading the response", e);
+ }
+ }
+
+ // all the other methods are valid as-is - work off of the variables initialized
+ // in the constructor...
+
+}
View
63 src/main/java/org/scribe/model/HttpClientStrategy.java
@@ -0,0 +1,63 @@
+package org.scribe.model;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ByteArrayEntity;
+import org.scribe.exceptions.OAuthException;
+
+/**
+ * This class allows us to use the HttpClient library to make the HTTP calls,
+ * and the scribe library to provide an OAuth abstraction over it.
+ *
+ * @author mkishor
+ */
+public class HttpClientStrategy implements HttpStrategy {
+ private HttpClient httpClient;
+
+ public HttpClientStrategy(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+ public Response send(Request request) {
+ try {
+ String completeUrl = request.getCompleteUrl();
+ String verb = request.getVerb().name();
+ HttpUriRequest httpRequest = newHttpUriRequest(verb, completeUrl);
+ addHeaders(request, httpRequest);
+ if ("POST".equals(verb) || "PUT".equals(verb)) {
+ ByteArrayEntity entity = new ByteArrayEntity(request.getByteBodyContents());
+ entity.setContentType(Request.DEFAULT_CONTENT_TYPE);
+ ((HttpEntityEnclosingRequest) httpRequest).setEntity(entity);
+ }
+ HttpResponse httpResponse = httpClient.execute(httpRequest);
+ return new HttpClientResponse(httpResponse);
+ } catch (Exception e) {
+ throw new OAuthException("Error making the request", e);
+ }
+ }
+
+ private void addHeaders(Request request, HttpUriRequest httpRequest) {
+ for (String key : request.getHeaders().keySet()) {
+ httpRequest.addHeader(key, request.getHeaders().get(key));
+ }
+ }
+
+ protected HttpUriRequest newHttpUriRequest(String verb, String uri) {
+ if ("GET".equals(verb)) {
+ return new HttpGet(uri);
+ } else if ("PUT".equals(verb)) {
+ return new HttpPut(uri);
+ } else if ("DELETE".equals(verb)) {
+ return new HttpDelete(uri);
+ } else {
+ return new HttpPost(uri);
+ }
+ }
+
+}
View
11 src/main/java/org/scribe/model/HttpStrategy.java
@@ -0,0 +1,11 @@
+package org.scribe.model;
+
+/**
+ * Allows the Request class to use a pluggable strategy for making
+ * the actual HTTP Connection.
+ *
+ * @author mkishor
+ */
+public interface HttpStrategy {
+ public Response send(Request request);
+}
View
120 src/main/java/org/scribe/model/Request.java
@@ -13,24 +13,23 @@
*
* @author Pablo Fernandez
*/
-class Request
+public class Request
{
- private static final String CONTENT_LENGTH = "Content-Length";
- private static final String CONTENT_TYPE = "Content-Type";
public static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded";
- private String url;
- private Verb verb;
- private ParameterList querystringParams;
- private ParameterList bodyParams;
- private Map<String, String> headers;
- private String payload = null;
- private HttpURLConnection connection;
- private String charset;
- private byte[] bytePayload = null;
- private boolean connectionKeepAlive = false;
- private Long connectTimeout = null;
- private Long readTimeout = null;
+ protected String url;
+ protected Verb verb;
+ protected ParameterList querystringParams;
+ protected ParameterList bodyParams;
+ protected Map<String, String> headers;
+ protected String payload = null;
+ protected String charset;
+ protected byte[] bytePayload = null;
+ protected boolean connectionKeepAlive = false;
+ protected Long connectTimeout = null;
+ protected Long readTimeout = null;
+
+ protected static HttpStrategy httpStrategy = new URLConnectionStrategy();
/**
* Creates a new Http Request
@@ -56,29 +55,7 @@ public Request(Verb verb, String url)
*/
public Response send()
{
- try
- {
- createConnection();
- return doSend();
- }
- catch (UnknownHostException uhe)
- {
- throw new OAuthException("Could not reach the desired host. Check your network connection.", uhe);
- }
- catch (IOException ioe)
- {
- throw new OAuthException("Problems while creating connection.", ioe);
- }
- }
-
- private void createConnection() throws IOException
- {
- String completeUrl = getCompleteUrl();
- if (connection == null)
- {
- System.setProperty("http.keepAlive", connectionKeepAlive ? "true" : "false");
- connection = (HttpURLConnection) new URL(completeUrl).openConnection();
- }
+ return httpStrategy.send(this);
}
/**
@@ -91,44 +68,6 @@ public String getCompleteUrl()
return querystringParams.appendTo(url);
}
- Response doSend() throws IOException
- {
- connection.setRequestMethod(this.verb.name());
- if (connectTimeout != null)
- {
- connection.setConnectTimeout(connectTimeout.intValue());
- }
- if (readTimeout != null)
- {
- connection.setReadTimeout(readTimeout.intValue());
- }
- addHeaders(connection);
- if (verb.equals(Verb.PUT) || verb.equals(Verb.POST))
- {
- addBody(connection, getByteBodyContents());
- }
- return new Response(connection);
- }
-
- void addHeaders(HttpURLConnection conn)
- {
- for (String key : headers.keySet())
- conn.setRequestProperty(key, headers.get(key));
- }
-
- void addBody(HttpURLConnection conn, byte[] content) throws IOException
- {
- conn.setRequestProperty(CONTENT_LENGTH, String.valueOf(content.length));
-
- // Set default content type if none is set.
- if (conn.getRequestProperty(CONTENT_TYPE) == null)
- {
- conn.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
- }
- conn.setDoOutput(true);
- conn.getOutputStream().write(content);
- }
-
/**
* Add an HTTP Header to the Request
*
@@ -346,17 +285,30 @@ public void setConnectionKeepAlive(boolean connectionKeepAlive)
this.connectionKeepAlive = connectionKeepAlive;
}
- /*
- * We need this in order to stub the connection object for test cases
- */
- void setConnection(HttpURLConnection connection)
- {
- this.connection = connection;
- }
-
@Override
public String toString()
{
return String.format("@Request(%s %s)", getVerb(), getUrl());
}
+
+ public Long getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ public Long getReadTimeout() {
+ return readTimeout;
+ }
+
+ public boolean isConnectionKeepAlive() {
+ return connectionKeepAlive;
+ }
+
+ public static HttpStrategy getHttpStrategy() {
+ return httpStrategy;
+ }
+
+ public static void setHttpStrategy(HttpStrategy httpStrategy) {
+ Request.httpStrategy = httpStrategy;
+ }
+
}
View
18 src/main/java/org/scribe/model/Response.java
@@ -14,13 +14,15 @@
*/
public class Response
{
- private static final String EMPTY = "";
-
- private int code;
- private String body;
- private InputStream stream;
- private Map<String, String> headers;
+ protected int code;
+ protected String body;
+ protected InputStream stream;
+ protected Map<String, String> headers;
+ protected Response() {
+ // default no-arg constructor to support alternative implementations
+ }
+
Response(HttpURLConnection connection) throws IOException
{
try
@@ -36,13 +38,13 @@
}
}
- private String parseBodyContents()
+ protected String parseBodyContents()
{
body = StreamUtils.getStreamContents(getStream());
return body;
}
- private Map<String, String> parseHeaders(HttpURLConnection conn)
+ protected Map<String, String> parseHeaders(HttpURLConnection conn)
{
Map<String, String> headers = new HashMap<String, String>();
for (String key : conn.getHeaderFields().keySet())
View
63 src/main/java/org/scribe/model/URLConnectionStrategy.java
@@ -0,0 +1,63 @@
+package org.scribe.model;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.UnknownHostException;
+
+import org.scribe.exceptions.OAuthException;
+
+public class URLConnectionStrategy implements HttpStrategy {
+ protected static final String CONTENT_LENGTH = "Content-Length";
+ protected static final String CONTENT_TYPE = "Content-Type";
+
+ public Response send(Request request) {
+ try {
+ HttpURLConnection connection = createConnection(request);
+ return doSend(request, connection);
+ } catch (UnknownHostException uhe) {
+ throw new OAuthException(
+ "Could not reach the desired host. Check your network connection.", uhe);
+ } catch (IOException ioe) {
+ throw new OAuthException("Problems while creating connection.", ioe);
+ }
+ }
+
+ protected HttpURLConnection createConnection(Request request) throws IOException {
+ String completeUrl = request.getCompleteUrl();
+ System.setProperty("http.keepAlive", request.isConnectionKeepAlive() ? "true" : "false");
+ return (HttpURLConnection) new URL(completeUrl).openConnection();
+ }
+
+ protected Response doSend(Request request, HttpURLConnection connection) throws IOException {
+ connection.setRequestMethod(request.getVerb().name());
+ if (request.getConnectTimeout() != null) {
+ connection.setConnectTimeout(request.getConnectTimeout().intValue());
+ }
+ if (request.getReadTimeout() != null) {
+ connection.setReadTimeout(request.getReadTimeout().intValue());
+ }
+ addHeaders(request, connection);
+ if (request.getVerb().equals(Verb.PUT) || request.getVerb().equals(Verb.POST)) {
+ addBody(connection, request.getByteBodyContents());
+ }
+ return new Response(connection);
+ }
+
+ protected void addHeaders(Request request, HttpURLConnection conn) {
+ for (String key : request.getHeaders().keySet())
+ conn.setRequestProperty(key, request.getHeaders().get(key));
+ }
+
+ protected void addBody(HttpURLConnection conn, byte[] content) throws IOException {
+ conn.setRequestProperty(CONTENT_LENGTH, String.valueOf(content.length));
+
+ // Set default content type if none is set.
+ if (conn.getRequestProperty(CONTENT_TYPE) == null) {
+ conn.setRequestProperty(CONTENT_TYPE, Request.DEFAULT_CONTENT_TYPE);
+ }
+ conn.setDoOutput(true);
+ conn.getOutputStream().write(content);
+ }
+
+}
View
8 src/test/java/org/scribe/model/RequestTest.java
@@ -14,12 +14,16 @@
public void setup() throws Exception
{
connection = new ConnectionStub();
+ Request.setHttpStrategy(new URLConnectionStrategyStub(connection));
postRequest = new Request(Verb.POST, "http://example.com");
postRequest.addBodyParameter("param", "value");
postRequest.addBodyParameter("param with spaces", "value with spaces");
- postRequest.setConnection(connection);
getRequest = new Request(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces");
- getRequest.setConnection(connection);
+ }
+
+ @After
+ public void tearDown() {
+ Request.setHttpStrategy(new URLConnectionStrategy());
}
@Test
View
22 src/test/java/org/scribe/model/URLConnectionStrategyStub.java
@@ -0,0 +1,22 @@
+package org.scribe.model;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+public class URLConnectionStrategyStub extends URLConnectionStrategy {
+ private ConnectionStub connection;
+
+ public URLConnectionStrategyStub(ConnectionStub connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ protected HttpURLConnection createConnection(Request request) throws IOException {
+ try {
+ return connection;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+}
Something went wrong with that request. Please try again.