Skip to content

Commit

Permalink
Added support for HttpClient based connections
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohan Kishore committed Apr 19, 2012
1 parent 1208fd8 commit 9e74946
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 95 deletions.
8 changes: 8 additions & 0 deletions pom.xml
Expand Up @@ -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>
Expand Down
Expand Up @@ -10,7 +10,6 @@ public class JsonTokenExtractor implements AccessTokenExtractor
{
private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\"");

@Override

This comment has been minimized.

Copy link
@philihp

philihp Apr 19, 2012

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

public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String");
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/scribe/model/HttpClientResponse.java
@@ -0,0 +1,33 @@
package org.scribe.model;

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();
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...

}
62 changes: 62 additions & 0 deletions src/main/java/org/scribe/model/HttpClientStrategy.java
@@ -0,0 +1,62 @@
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());
((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);
}
}

}
11 changes: 11 additions & 0 deletions 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);
}
120 changes: 36 additions & 84 deletions src/main/java/org/scribe/model/Request.java
Expand Up @@ -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
Expand All @@ -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);
}

/**
Expand All @@ -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
*
Expand Down Expand Up @@ -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;
}

}
18 changes: 10 additions & 8 deletions src/main/java/org/scribe/model/Response.java
Expand Up @@ -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
Expand All @@ -36,13 +38,13 @@ public class Response
}
}

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())
Expand Down
63 changes: 63 additions & 0 deletions 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);
}

}

1 comment on commit 9e74946

@rgngl
Copy link

@rgngl rgngl commented on 9e74946 Apr 19, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Please sign in to comment.