Skip to content

Commit

Permalink
add support for byte[] and File (async only) payload in OAuth Request…
Browse files Browse the repository at this point in the history
  • Loading branch information
kullfar committed Dec 7, 2016
1 parent 36c33b6 commit b7d0c6d
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 34 deletions.
1 change: 1 addition & 0 deletions changelog
@@ -1,6 +1,7 @@
[SNAPSHOT]
* uncouple OAuthRequest and Service. OAuthRequest shouldn't know anything about OAuthservice.
You don't need OAuthService to create OAuthRequest anymore. Async request should be sent via OAuthService method.
* add support for byte[] and File (async only) payload in OAuth Requests (thanks to https://github.com/keijohyttinen)

[3.3.0]
* update Facebook v2.6 -> v2.8
Expand Down
Expand Up @@ -8,6 +8,7 @@
import java.util.Map;
import com.github.scribejava.core.exceptions.OAuthException;
import com.github.scribejava.core.oauth.OAuthService;
import java.io.File;

/**
* The representation of an OAuth HttpRequest.
Expand All @@ -26,9 +27,12 @@ public abstract class AbstractRequest {
private final Map<String, String> headers = new HashMap<>();
private boolean followRedirects = true;

private String payload;
private String charset;
private byte[] bytePayload;

private String stringPayload;
private byte[] byteArrayPayload;
private File filePayload;

private final Map<String, String> oauthParameters = new HashMap<>();

private String realm;
Expand Down Expand Up @@ -142,22 +146,58 @@ protected boolean hasBodyContent() {
}

/**
* Add body payload. This method is used when the HTTP body is not a form-url-encoded string, but another thing.
* @param payload payload
* @deprecated use {@link #setPayload(java.lang.String) }
*/
@Deprecated
public void addPayload(String payload) {
setPayload(payload);
}

/**
* @param payload payload
* @deprecated use {@link #setPayload(byte[]) }
*/
@Deprecated
public void addPayload(byte[] payload) {
setPayload(payload);
}

/**
* Set body payload. This method is used when the HTTP body is not a form-url-encoded string, but another thing.
* Like for example XML. Note: The contents are not part of the OAuth signature
*
* @param payload the body of the request
*/
public void addPayload(String payload) {
this.payload = payload;
public void setPayload(String payload) {
resetPayload();
stringPayload = payload;
}

/**
* Overloaded version for byte arrays
*
* @param payload byte[]
*/
public void addPayload(byte[] payload) {
this.bytePayload = payload.clone();
public void setPayload(byte[] payload) {
resetPayload();
byteArrayPayload = payload.clone();
}

/**
* Overloaded version for File
*
* @param payload File
*/
public void setPayload(File payload) {
resetPayload();
filePayload = payload;
}

private void resetPayload() {
stringPayload = null;
byteArrayPayload = null;
filePayload = null;
}

/**
Expand Down Expand Up @@ -212,32 +252,54 @@ public String getSanitizedUrl() {
}

/**
* Returns the body of the request
* @return value set in {@link #setPayload(java.lang.String)}
* @deprecated use {@link #getStringPayload()} or {@link #getByteArrayPayload()}
*/
@Deprecated
public String getBodyContents() {
return getStringPayload();
}

/**
* Returns the body of the request (set in {@link #setPayload(java.lang.String)})
*
* @return form encoded string
*
* @throws OAuthException if the charset chosen is not supported
*/
public String getBodyContents() {
try {
return new String(getByteBodyContents(), getCharset());
} catch (UnsupportedEncodingException uee) {
throw new OAuthException("Unsupported Charset: " + charset, uee);
}
public String getStringPayload() {
return stringPayload;
}

/**
* @return value set in {@link #setPayload(byte[])}
* @deprecated use {@link #getByteArrayPayload() }
*/
@Deprecated
public byte[] getByteBodyContents() {
return getByteArrayPayload();
}

byte[] getByteBodyContents() {
if (bytePayload != null) {
return bytePayload;
/**
* @return the body of the request (set in {@link #setPayload(byte[])} or in
* {@link #addBodyParameter(java.lang.String, java.lang.String)} )
*/
public byte[] getByteArrayPayload() {
if (byteArrayPayload != null) {
return byteArrayPayload;
}
final String body = (payload == null) ? bodyParams.asFormUrlEncodedString() : payload;
final String body = bodyParams.asFormUrlEncodedString();
try {
return body.getBytes(getCharset());
} catch (UnsupportedEncodingException uee) {
throw new OAuthException("Unsupported Charset: " + getCharset(), uee);
}
}

public File getFilePayload() {
return filePayload;
}

@Override
public String toString() {
return String.format("@Request(%s %s)", getVerb(), getUrl());
Expand Down
@@ -1,15 +1,24 @@
package com.github.scribejava.core.model;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Future;

public interface HttpClient {

void close() throws IOException;

<T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter);
byte[] bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter);

<T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter);

<T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
File bodyContents, OAuthAsyncRequestCallback<T> callback, OAuthRequestAsync.ResponseConverter<T> converter);

interface Config {
}
Expand Down
Expand Up @@ -7,6 +7,7 @@
import com.github.scribejava.core.exceptions.OAuthConnectionException;
import com.github.scribejava.core.exceptions.OAuthException;
import com.github.scribejava.core.oauth.OAuthService;
import java.io.File;

public class OAuthRequest extends AbstractRequest {

Expand Down Expand Up @@ -69,7 +70,14 @@ Response doSend() throws IOException {
}
addHeaders();
if (hasBodyContent()) {
addBody(getByteBodyContents());
final File filePayload = getFilePayload();
if (filePayload != null) {
throw new UnsupportedOperationException("Sync Requests do not support File payload for the moment");
} else if (getStringPayload() != null) {
addBody(getStringPayload().getBytes(getCharset()));
} else {
addBody(getByteArrayPayload());
}
}
return new Response(connection);
}
Expand Down
Expand Up @@ -13,6 +13,7 @@
import com.github.scribejava.core.model.ScribeJavaConfig;
import com.github.scribejava.core.model.Token;
import com.github.scribejava.core.model.Verb;
import java.io.File;

import java.io.IOException;
import java.util.Map;
Expand Down Expand Up @@ -127,8 +128,17 @@ public <T> Future<T> execute(OAuthRequestAsync request, OAuthAsyncRequestCallbac
config.log("Cannot use async operations, only sync");
}

return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(),
request.getCompleteUrl(), request.getBodyContents(), callback, converter);
final File filePayload = request.getFilePayload();
if (filePayload != null) {
return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(),
request.getCompleteUrl(), filePayload, callback, converter);
} else if (request.getStringPayload() != null) {
return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(),
request.getCompleteUrl(), request.getStringPayload(), callback, converter);
} else {
return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(),
request.getCompleteUrl(), request.getByteArrayPayload(), callback, converter);
}
}

public Future<Response> execute(OAuthRequestAsync request, OAuthAsyncRequestCallback<Response> callback) {
Expand Down
Expand Up @@ -51,16 +51,17 @@ public void shouldAddRequestHeaders() {

@Test
public void shouldSetBodyParamsAndAddContentLength() {
assertEquals("param=value&param%20with%20spaces=value%20with%20spaces", postRequest.getBodyContents());
assertEquals("param=value&param%20with%20spaces=value%20with%20spaces",
new String(postRequest.getByteArrayPayload()));
postRequest.send();
assertTrue(connection.getHeaders().containsKey("Content-Length"));
}

@Test
public void shouldSetPayloadAndHeaders() {
postRequest.addPayload("PAYLOAD");
postRequest.setPayload("PAYLOAD");
postRequest.send();
assertEquals("PAYLOAD", postRequest.getBodyContents());
assertEquals("PAYLOAD", postRequest.getStringPayload());
assertTrue(connection.getHeaders().containsKey("Content-Length"));
}

Expand All @@ -87,14 +88,14 @@ public void shouldHandleQueryStringSpaceEncodingProperly() {

@Test
public void shouldAutomaticallyAddContentTypeForPostRequestsWithBytePayload() {
postRequest.addPayload("PAYLOAD".getBytes());
postRequest.setPayload("PAYLOAD".getBytes());
postRequest.send();
assertEquals(OAuthRequest.DEFAULT_CONTENT_TYPE, connection.getHeaders().get("Content-Type"));
}

@Test
public void shouldAutomaticallyAddContentTypeForPostRequestsWithStringPayload() {
postRequest.addPayload("PAYLOAD");
postRequest.setPayload("PAYLOAD");
postRequest.send();
assertEquals(OAuthRequest.DEFAULT_CONTENT_TYPE, connection.getHeaders().get("Content-Type"));
}
Expand Down
Expand Up @@ -14,6 +14,7 @@
import java.util.concurrent.Future;

import static com.github.scribejava.core.model.AbstractRequest.DEFAULT_CONTENT_TYPE;
import java.io.File;
import org.asynchttpclient.BoundRequestBuilder;

public class AhcHttpClient implements HttpClient {
Expand All @@ -35,8 +36,31 @@ public void close() throws IOException {

@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter) {
byte[] bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new ByteArrayBodySetter(bodyContents),
callback, converter);
}

@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new StringBodySetter(bodyContents), callback,
converter);
}

@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
File bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new FileBodySetter(bodyContents), callback,
converter);
}

private <T> Future<T> doExecuteAsync(String userAgent, Map<String, String> headers, Verb httpVerb,
String completeUrl, BodySetter bodySetter, OAuthAsyncRequestCallback<T> callback,
OAuthRequestAsync.ResponseConverter<T> converter) {
final BoundRequestBuilder boundRequestBuilder;
switch (httpVerb) {
case GET:
Expand All @@ -47,7 +71,7 @@ public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers,
if (!headers.containsKey(AbstractRequest.CONTENT_TYPE)) {
requestBuilder = requestBuilder.addHeader(AbstractRequest.CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
}
boundRequestBuilder = requestBuilder.setBody(bodyContents);
boundRequestBuilder = bodySetter.setBody(requestBuilder);
break;
default:
throw new IllegalArgumentException("message build error: unknown verb type");
Expand All @@ -64,4 +88,51 @@ public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers,
.execute(new OAuthAsyncCompletionHandler<>(
callback, converter));
}

private interface BodySetter {

BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder);
}

private static class ByteArrayBodySetter implements BodySetter {

private final byte[] bodyContents;

private ByteArrayBodySetter(byte[] bodyContents) {
this.bodyContents = bodyContents;
}

@Override
public BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder) {
return requestBuilder.setBody(bodyContents);
}
}

private static class StringBodySetter implements BodySetter {

private final String bodyContents;

private StringBodySetter(String bodyContents) {
this.bodyContents = bodyContents;
}

@Override
public BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder) {
return requestBuilder.setBody(bodyContents);
}
}

private static class FileBodySetter implements BodySetter {

private final File bodyContents;

private FileBodySetter(File bodyContents) {
this.bodyContents = bodyContents;
}

@Override
public BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder) {
return requestBuilder.setBody(bodyContents);
}
}
}

0 comments on commit b7d0c6d

Please sign in to comment.