Skip to content

Commit eccd3dd

Browse files
committed
Add Armeria HTTP client (thanks to https://github.com/max904-github)
1 parent 4fa982b commit eccd3dd

File tree

16 files changed

+683
-687
lines changed

16 files changed

+683
-687
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ ScribeJava support out-of-box several HTTP clients:
3636
* Async Http Client asynchttpclient 2.x (maven module scribejava-httpclient-ahc) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java)
3737
* OkHttp (maven module scribejava-httpclient-okhttp) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java)
3838
* Apache HttpComponents HttpClient (maven module scribejava-httpclient-apache) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java)
39+
* Armeria HTTP client (required >= java 8) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20ArmeriaExample.java)
3940
* any externally created HTTP client [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java)
4041

4142
just add corresponding maven modules to your pom

changelog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
* Add Polar API (https://www.polar.com/) (thanks to https://github.com/vidi42)
33
* make Response accept resources to autoclose and autoclose it (thanks to https://github.com/drei01)
44
* fix url encoding in POST payload (it's needed for 'application/x-www-form-urlencoded' Content-Type)
5-
+ unit tests (thanks to https://github.com/max904-github)
5+
+ unit tests (thanks to https://github.com/max904-github)
6+
* Add Armeria HTTP client (thanks to https://github.com/max904-github)
67

78
[6.9.0]
89
* Add Xero API (https://www.xero.com/) (thanks to https://github.com/SidneyAllen)

pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@
146146
<encoding>UTF-8</encoding>
147147
<release>${java.release}</release>
148148
<showDeprecation>true</showDeprecation>
149+
<compilerArgs>
150+
<arg>-Xlint:-options</arg>
151+
</compilerArgs>
149152
</configuration>
150153
</plugin>
151154
<plugin>

scribejava-apis/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@
4444
<version>${project.version}</version>
4545
<scope>test</scope>
4646
</dependency>
47+
<dependency>
48+
<groupId>com.github.scribejava</groupId>
49+
<artifactId>scribejava-httpclient-armeria</artifactId>
50+
<version>${project.version}</version>
51+
<scope>test</scope>
52+
</dependency>
4753
</dependencies>
4854

4955
<build>
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.github.scribejava.apis.examples;
2+
3+
import java.util.Random;
4+
import java.util.Scanner;
5+
import com.github.scribejava.apis.GoogleApi20;
6+
import com.github.scribejava.core.builder.ServiceBuilder;
7+
import com.github.scribejava.core.model.OAuth2AccessToken;
8+
import com.github.scribejava.core.model.OAuthRequest;
9+
import com.github.scribejava.core.model.Response;
10+
import com.github.scribejava.core.model.Verb;
11+
import com.github.scribejava.core.oauth.OAuth20Service;
12+
import com.github.scribejava.httpclient.armeria.ArmeriaHttpClientConfig;
13+
import java.io.IOException;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
import java.util.concurrent.ExecutionException;
17+
18+
public class Google20ArmeriaExample {
19+
20+
private static final String NETWORK_NAME = "Google Armeria";
21+
private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo";
22+
23+
private Google20ArmeriaExample() {
24+
}
25+
26+
@SuppressWarnings("PMD.SystemPrintln")
27+
public static void main(String... args) throws InterruptedException, ExecutionException, IOException {
28+
// Replace these with your client id and secret
29+
final String clientId = "your client id";
30+
final String clientSecret = "your client secret";
31+
final String secretState = "secret" + new Random().nextInt(999_999);
32+
33+
try (OAuth20Service service = new ServiceBuilder(clientId)
34+
.apiSecret(clientSecret)
35+
.defaultScope("profile") // replace with desired scope
36+
.callback("http://example.com/callback")
37+
.httpClientConfig(ArmeriaHttpClientConfig.defaultConfig())
38+
.build(GoogleApi20.instance())) {
39+
final Scanner in = new Scanner(System.in, "UTF-8");
40+
41+
System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ===");
42+
System.out.println();
43+
44+
// Obtain the Authorization URL
45+
System.out.println("Fetching the Authorization URL...");
46+
//pass access_type=offline to get refresh token
47+
//https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow
48+
final Map<String, String> additionalParams = new HashMap<>();
49+
additionalParams.put("access_type", "offline");
50+
//force to reget refresh token (if user are asked not the first time)
51+
additionalParams.put("prompt", "consent");
52+
final String authorizationUrl = service.createAuthorizationUrlBuilder()
53+
.state(secretState)
54+
.additionalParams(additionalParams)
55+
.build();
56+
System.out.println("Got the Authorization URL!");
57+
System.out.println("Now go and authorize ScribeJava here:");
58+
System.out.println(authorizationUrl);
59+
System.out.println("And paste the authorization code here");
60+
System.out.print(">>");
61+
final String code = in.nextLine();
62+
System.out.println();
63+
64+
System.out.println("And paste the state from server here. We have set 'secretState'='"
65+
+ secretState + "'.");
66+
System.out.print(">>");
67+
final String value = in.nextLine();
68+
if (secretState.equals(value)) {
69+
System.out.println("State value does match!");
70+
} else {
71+
System.out.println("Ooops, state value does not match!");
72+
System.out.println("Expected = " + secretState);
73+
System.out.println("Got = " + value);
74+
System.out.println();
75+
}
76+
77+
System.out.println("Trading the Authorization Code for an Access Token...");
78+
OAuth2AccessToken accessToken = service.getAccessToken(code);
79+
System.out.println("Got the Access Token!");
80+
System.out.println("(The raw response looks like this: " + accessToken.getRawResponse()
81+
+ "')");
82+
83+
System.out.println("Refreshing the Access Token...");
84+
accessToken = service.refreshAccessToken(accessToken.getRefreshToken());
85+
System.out.println("Refreshed the Access Token!");
86+
System.out.println("(The raw response looks like this: " + accessToken.getRawResponse()
87+
+ "')");
88+
System.out.println();
89+
90+
// Now let's go and ask for a protected resource!
91+
System.out.println("Now we're going to access a protected resource...");
92+
while (true) {
93+
System.out.println("Paste fieldnames to fetch (leave empty to get profile, 'exit' to stop example)");
94+
System.out.print(">>");
95+
final String query = in.nextLine();
96+
System.out.println();
97+
98+
final String requestUrl;
99+
if ("exit".equals(query)) {
100+
break;
101+
} else if (query == null || query.isEmpty()) {
102+
requestUrl = PROTECTED_RESOURCE_URL;
103+
} else {
104+
requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query;
105+
}
106+
107+
final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl);
108+
service.signRequest(accessToken, request);
109+
System.out.println();
110+
try (Response response = service.execute(request)) {
111+
System.out.println(response.getCode());
112+
System.out.println(response.getBody());
113+
}
114+
System.out.println();
115+
}
116+
}
117+
}
118+
}

scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package com.github.scribejava.core.httpclient.jdk;
22

33
import com.github.scribejava.core.exceptions.OAuthException;
4-
import com.github.scribejava.core.httpclient.multipart.BodyPartPayload;
54
import com.github.scribejava.core.httpclient.HttpClient;
6-
import com.github.scribejava.core.httpclient.multipart.ByteArrayBodyPartPayload;
75
import com.github.scribejava.core.httpclient.multipart.MultipartPayload;
6+
import com.github.scribejava.core.httpclient.multipart.MultipartUtils;
87
import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
98
import com.github.scribejava.core.model.OAuthConstants;
109
import com.github.scribejava.core.model.OAuthRequest;
@@ -207,7 +206,7 @@ private static void addBody(HttpURLConnection connection, MultipartPayload multi
207206
}
208207

209208
if (requiresBody) {
210-
final ByteArrayOutputStream os = getPayload(multipartPayload);
209+
final ByteArrayOutputStream os = MultipartUtils.getPayload(multipartPayload);
211210
final int contentLength = os.size();
212211
final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength);
213212
if (contentLength > 0) {
@@ -216,46 +215,16 @@ private static void addBody(HttpURLConnection connection, MultipartPayload multi
216215
}
217216
}
218217

218+
/**
219+
* @param multipartPayload multipartPayload
220+
* @return ByteArrayOutputStream
221+
* @throws IOException
222+
* @deprecated use {@link com.github.scribejava.core.httpclient.multipart.MultipartUtils#getPayload(
223+
* com.github.scribejava.core.httpclient.multipart.MultipartPayload) }
224+
*/
225+
@Deprecated
219226
static ByteArrayOutputStream getPayload(MultipartPayload multipartPayload) throws IOException {
220-
final ByteArrayOutputStream os = new ByteArrayOutputStream();
221-
222-
final String preamble = multipartPayload.getPreamble();
223-
if (preamble != null) {
224-
os.write(preamble.getBytes());
225-
}
226-
final List<BodyPartPayload> bodyParts = multipartPayload.getBodyParts();
227-
if (!bodyParts.isEmpty()) {
228-
final String boundary = multipartPayload.getBoundary();
229-
final byte[] startBoundary = ("\r\n--" + boundary + "\r\n").getBytes();
230-
231-
for (BodyPartPayload bodyPart : bodyParts) {
232-
os.write(startBoundary);
233-
234-
final Map<String, String> bodyPartHeaders = bodyPart.getHeaders();
235-
if (bodyPartHeaders != null) {
236-
for (Map.Entry<String, String> header : bodyPartHeaders.entrySet()) {
237-
os.write((header.getKey() + ": " + header.getValue() + "\r\n").getBytes());
238-
}
239-
}
240-
241-
if (bodyPart instanceof MultipartPayload) {
242-
getPayload((MultipartPayload) bodyPart).writeTo(os);
243-
} else if (bodyPart instanceof ByteArrayBodyPartPayload) {
244-
os.write("\r\n".getBytes());
245-
os.write(((ByteArrayBodyPartPayload) bodyPart).getPayload());
246-
} else {
247-
throw new AssertionError(bodyPart.getClass());
248-
}
249-
}
250-
251-
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
252-
final String epilogue = multipartPayload.getEpilogue();
253-
if (epilogue != null) {
254-
os.write((epilogue + "\r\n").getBytes());
255-
}
256-
257-
}
258-
return os;
227+
return MultipartUtils.getPayload(multipartPayload);
259228
}
260229

261230
private static OutputStream prepareConnectionForBodyAndGetOutputStream(HttpURLConnection connection,

scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,8 @@
55
import java.util.Collections;
66
import java.util.List;
77
import java.util.Map;
8-
import java.util.regex.Matcher;
9-
import java.util.regex.Pattern;
108

119
public class MultipartPayload extends BodyPartPayload {
12-
private static final String B_CHARS_NO_SPACE_PATTERN = "0-9a-zA-Z'()+_,-./:=?";
13-
private static final String B_CHARS_PATTERN = B_CHARS_NO_SPACE_PATTERN + " ";
14-
private static final String BOUNDARY_PATTERN = '[' + B_CHARS_PATTERN + "]{0,69}[" + B_CHARS_NO_SPACE_PATTERN + ']';
15-
private static final Pattern BOUNDARY_REGEXP = Pattern.compile(BOUNDARY_PATTERN);
16-
private static final Pattern BOUNDARY_FROM_HEADER_REGEXP
17-
= Pattern.compile("; boundary=\"?(" + BOUNDARY_PATTERN + ")\"?");
1810

1911
private static final String DEFAULT_SUBTYPE = "form-data";
2012

@@ -24,7 +16,7 @@ public class MultipartPayload extends BodyPartPayload {
2416
private String epilogue;
2517

2618
public MultipartPayload() {
27-
this(null, generateDefaultBoundary(), null);
19+
this(null, MultipartUtils.generateDefaultBoundary(), null);
2820
}
2921

3022
public MultipartPayload(String boundary) {
@@ -50,7 +42,7 @@ public MultipartPayload(String subtype, String boundary, Map<String, String> hea
5042

5143
private static Map<String, String> composeHeaders(String subtype, String boundary, Map<String, String> headersIn)
5244
throws IllegalArgumentException {
53-
checkBoundarySyntax(boundary);
45+
MultipartUtils.checkBoundarySyntax(boundary);
5446
final Map<String, String> headersOut;
5547
String contentTypeHeader = headersIn == null ? null : headersIn.get(HttpClient.CONTENT_TYPE);
5648
if (contentTypeHeader == null) {
@@ -64,7 +56,7 @@ private static Map<String, String> composeHeaders(String subtype, String boundar
6456
}
6557
} else {
6658
headersOut = headersIn;
67-
final String parsedBoundary = parseBoundaryFromHeader(contentTypeHeader);
59+
final String parsedBoundary = MultipartUtils.parseBoundaryFromHeader(contentTypeHeader);
6860
if (parsedBoundary == null) {
6961
headersOut.put(HttpClient.CONTENT_TYPE, contentTypeHeader + "; boundary=\"" + boundary + '"');
7062
} else if (!parsedBoundary.equals(boundary)) {
@@ -75,28 +67,32 @@ private static Map<String, String> composeHeaders(String subtype, String boundar
7567
return headersOut;
7668
}
7769

70+
/**
71+
*
72+
* @param boundary boundary
73+
* @deprecated use
74+
* {@link com.github.scribejava.core.httpclient.multipart.MultipartUtils#checkBoundarySyntax(java.lang.String)}
75+
*/
76+
@Deprecated
7877
static void checkBoundarySyntax(String boundary) {
79-
if (boundary == null || !BOUNDARY_REGEXP.matcher(boundary).matches()) {
80-
throw new IllegalArgumentException("{'boundary'='" + boundary + "'} has invalid syntax. Should be '"
81-
+ BOUNDARY_PATTERN + "'.");
82-
}
78+
MultipartUtils.checkBoundarySyntax(boundary);
8379
}
8480

8581
private static String parseOrGenerateBoundary(Map<String, String> headers) {
86-
final String parsedBoundary = parseBoundaryFromHeader(headers.get(HttpClient.CONTENT_TYPE));
87-
return parsedBoundary == null ? generateDefaultBoundary() : parsedBoundary;
88-
}
89-
90-
private static String generateDefaultBoundary() {
91-
return "----ScribeJava----" + System.currentTimeMillis();
92-
}
93-
82+
final String parsedBoundary = MultipartUtils.parseBoundaryFromHeader(headers.get(HttpClient.CONTENT_TYPE));
83+
return parsedBoundary == null ? MultipartUtils.generateDefaultBoundary() : parsedBoundary;
84+
}
85+
86+
/**
87+
*
88+
* @param contentTypeHeader contentTypeHeader
89+
* @return String
90+
* @deprecated use
91+
* {@link com.github.scribejava.core.httpclient.multipart.MultipartUtils#parseBoundaryFromHeader(java.lang.String)}
92+
*/
93+
@Deprecated
9494
static String parseBoundaryFromHeader(String contentTypeHeader) {
95-
if (contentTypeHeader == null) {
96-
return null;
97-
}
98-
final Matcher matcher = BOUNDARY_FROM_HEADER_REGEXP.matcher(contentTypeHeader);
99-
return matcher.find() ? matcher.group(1) : null;
95+
return MultipartUtils.parseBoundaryFromHeader(contentTypeHeader);
10096
}
10197

10298
public void addFileBodyPart(byte[] fileContent) {

scribejava-httpclient-armeria/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartUtils.java renamed to scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartUtils.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,40 @@
44
import java.io.IOException;
55
import java.util.List;
66
import java.util.Map;
7+
import java.util.regex.Matcher;
8+
import java.util.regex.Pattern;
79

8-
public abstract class MultipartUtils {
10+
public class MultipartUtils {
11+
12+
private static final String B_CHARS_NO_SPACE_PATTERN = "0-9a-zA-Z'()+_,-./:=?";
13+
private static final String B_CHARS_PATTERN = B_CHARS_NO_SPACE_PATTERN + " ";
14+
private static final String BOUNDARY_PATTERN = '[' + B_CHARS_PATTERN + "]{0,69}[" + B_CHARS_NO_SPACE_PATTERN + ']';
15+
private static final Pattern BOUNDARY_REGEXP = Pattern.compile(BOUNDARY_PATTERN);
16+
private static final Pattern BOUNDARY_FROM_HEADER_REGEXP
17+
= Pattern.compile("; boundary=\"?(" + BOUNDARY_PATTERN + ")\"?");
18+
19+
private MultipartUtils() {
20+
}
21+
22+
public static void checkBoundarySyntax(String boundary) {
23+
if (boundary == null || !BOUNDARY_REGEXP.matcher(boundary).matches()) {
24+
throw new IllegalArgumentException("{'boundary'='" + boundary + "'} has invalid syntax. Should be '"
25+
+ BOUNDARY_PATTERN + "'.");
26+
}
27+
}
28+
29+
public static String parseBoundaryFromHeader(String contentTypeHeader) {
30+
if (contentTypeHeader == null) {
31+
return null;
32+
}
33+
final Matcher matcher = BOUNDARY_FROM_HEADER_REGEXP.matcher(contentTypeHeader);
34+
return matcher.find() ? matcher.group(1) : null;
35+
}
36+
37+
public static String generateDefaultBoundary() {
38+
return "----ScribeJava----" + System.currentTimeMillis();
39+
}
940

10-
// copied from com.github.scribejava.core.httpclient.jdk.JDKHttpClient#getPayload
1141
public static ByteArrayOutputStream getPayload(MultipartPayload multipartPayload) throws IOException {
1242
final ByteArrayOutputStream os = new ByteArrayOutputStream();
1343

@@ -49,5 +79,4 @@ public static ByteArrayOutputStream getPayload(MultipartPayload multipartPayload
4979
}
5080
return os;
5181
}
52-
5382
}

0 commit comments

Comments
 (0)