Skip to content

Commit 06dfc2c

Browse files
committed
Merge pull request #1 from mb3364/features
Features
2 parents 8cfe4a3 + 6c2564e commit 06dfc2c

8 files changed

Lines changed: 180 additions & 129 deletions

File tree

README.md

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Java Async HTTP Client
2-
A simple asynchronous HTTP client built on top of Java's `HttpURLConnection`.
2+
A simple, lightweight, asynchronous HTTP client built on top of Java's `HttpURLConnection`.
33

44
This project is actively being improved and major changes may break backwards compatibility.
55

66
Please feel free to report any issues.
77

88
## Basics
99

10-
Simply create either an `AsyncHttpClient` (asynchronous) or `HttpClient` (synchronous) instance and make requests through it with the `get()`, `post()`, `put()`, `delete()`, or `head()` methods.
10+
Simply create either an `AsyncHttpClient` (asynchronous) or `SyncHttpClient` (synchronous) instance and make requests through it with the `get()`, `post()`, `put()`, `delete()`, or `head()` methods.
1111
Responses are handled by callbacks through `HttpResponseHandler` usually created as an anonymous inner class of the function call.
1212

1313

@@ -18,34 +18,50 @@ String url = "https://api.twitch.tv/kraken/games/top";
1818

1919
// Set the GET parameters
2020
RequestParams params = new RequestParams();
21-
params.put("limit", "1");
22-
params.put("offset", "0");
21+
params.put("limit", 1);
22+
params.put("offset", 0);
2323

24-
AsyncHttpClient client = new AsyncHttpClient();
25-
client.setHeader("Accept", "application/vnd.twitchtv.v3+json"); // Optional: send custom headers
24+
HttpClient client = new AsyncHttpClient();
25+
client.setHeader("Accept", "application/vnd.twitchtv.v3+json"); // Optional: send custom headers, send with all future requests
2626
client.setUserAgent("my-java-application"); // Optional: set a custom user-agent
2727

28+
client.get(url, params, new StringHttpResponseHandler() {
29+
@Override
30+
public void onSuccess(int statusCode, Map<String, List<String>> headers, String content) {
31+
/* Request was successful */
32+
}
33+
34+
@Override
35+
public void onFailure(int statusCode, Map<String, List<String>> headers, String content) {
36+
/* Server responded with a status code 4xx or 5xx error */
37+
}
38+
39+
@Override
40+
public void onFailure(Throwable throwable) {
41+
/* An exception occurred during the request. Usually unable to connect or there was an error reading the response */
42+
}
43+
});
44+
```
45+
46+
The above example reads String responses using `StringHttpResponseHandler`. It will automatically read the response encoding and encode the String automatically for you.
47+
48+
For raw data in an array of bytes, you may use `HttpResponseHandler`,
49+
50+
```java
2851
client.get(url, params, new HttpResponseHandler() {
2952
@Override
30-
public void onSuccess(HttpResponse response) {
31-
// Successful response from the server
32-
System.out.println(response.getStatusCode());
33-
System.out.println(response.getStatusMessage());
34-
System.out.println(response.getContent());
53+
public void onSuccess(int statusCode, Map<String, List<String>> headers, byte[] content) {
54+
/* Request was successful */
3555
}
3656

3757
@Override
38-
public void onFailure(HttpResponse response) {
39-
// The server returned an error (4xx/5xx status code)
40-
System.out.println(response.getUrl());
41-
System.out.println(response.getStatusCode());
42-
System.out.println(response.getStatusMessage());
58+
public void onFailure(int statusCode, Map<String, List<String>> headers, byte[] content) {
59+
/* Server responded with a status code 4xx or 5xx error */
4360
}
4461

4562
@Override
4663
public void onFailure(Throwable throwable) {
47-
// Something went wrong with the request and an exception was thrown
48-
throwable.printStackTrace();
64+
/* An exception occurred during the request. Usually unable to connect or there was an error reading the response */
4965
}
5066
});
5167
```
@@ -54,10 +70,18 @@ client.get(url, params, new HttpResponseHandler() {
5470

5571
The `RequestParams` object is used to specify the HTTP request parameters such as for GET or POST. GET parameters are automatically appended to the URL and POST parameters will be x-www-form-urlencoded and sent in the content body.
5672

73+
## Limitations
74+
75+
* Currently only able to send basic form data in UTF-8. Files are currently not supported.
76+
* All response data is buffered before the callback is fired. This means downloading large files can cause an out of memory error.
77+
5778
## Download
58-
* [v1.0.0 jar](https://github.com/mb3364/java-async-http/releases/tag/v1.0.0)
79+
80+
* [v1.2.0 jar](https://github.com/mb3364/java-async-http/releases/tag/v1.2.0)
5981

6082
## Roadmap
6183

62-
* Handle chunked streaming of data for efficient file uploads/downloads.
63-
* More control over setting Content-Type.
84+
* Allow file uploads with form data.
85+
* Allow streaming downloads to file.
86+
* More control over setting Content-Type.
87+
* Handle cookies.

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
22
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
33
<modelVersion>4.0.0</modelVersion>
44

55
<groupId>com.mb3364.http</groupId>
66
<artifactId>async-http-client</artifactId>
7-
<version>1.0.0</version>
7+
<version>1.2.0</version>
88
<packaging>jar</packaging>
99

1010
<name>async-http-client</name>

src/main/java/com/mb3364/http/HttpClient.java

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,38 @@
88
import java.util.List;
99
import java.util.Map;
1010

11-
public class HttpClient {
11+
public abstract class HttpClient {
1212

1313
public static final String DEFAULT_USER_AGENT = "Java-Async-Http";
14-
public static final String DEFAULT_CHARSET = "UTF-8";
1514

1615
private final Map<String, String> headers;
1716

18-
private String charset;
1917
private int connectionTimeout = 20000;
2018
private int dataRetrievalTimeout = 20000;
2119

2220
public HttpClient() {
2321
headers = Collections.synchronizedMap(new LinkedHashMap<String, String>());
2422
setUserAgent(DEFAULT_USER_AGENT);
25-
setCharset(DEFAULT_CHARSET);
2623
}
2724

2825
/**
29-
* Read the input stream and convert to a string
26+
* Reads an InputStream into a byte array.
3027
*
3128
* @param inputStream InputStream to read
32-
* @return String representing entire input stream contents
29+
* @return byte array representing entire InputStream contents
30+
* @throws IOException if unable to read stream
3331
*/
34-
private static String readStream(InputStream inputStream) {
35-
if (inputStream == null) return "";
36-
StringBuilder text = new StringBuilder();
37-
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
38-
String line;
39-
while ((line = reader.readLine()) != null) {
40-
text.append(line);
41-
}
42-
} catch (IOException e) {
43-
e.printStackTrace();
32+
private static byte[] readStreamAsBytes(InputStream inputStream) throws IOException {
33+
if (inputStream == null) return new byte[0];
34+
35+
ByteArrayOutputStream os = new ByteArrayOutputStream();
36+
byte[] buffer = new byte[1024 * 32];
37+
int bytesRead;
38+
while ((bytesRead = inputStream.read(buffer, 0, buffer.length)) != -1) {
39+
os.write(buffer, 0, bytesRead);
4440
}
45-
return text.toString();
41+
os.flush();
42+
return os.toByteArray();
4643
}
4744

4845
protected void request(String url, HttpRequestMethod method, RequestParams params, HttpResponseHandler handler) {
@@ -79,38 +76,26 @@ protected void request(String url, HttpRequestMethod method, RequestParams param
7976
// POST and PUT expect an output body.
8077
if (method == HttpRequestMethod.POST || method == HttpRequestMethod.PUT) {
8178
urlConnection.setDoOutput(true);
82-
// TODO: Allow user to set content-type
8379
byte[] content = params.toEncodedString().getBytes();
84-
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
80+
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + "utf-8"); // TODO: Allow user to set content-type
8581
urlConnection.setRequestProperty("Content-Length", Long.toString(content.length));
82+
urlConnection.setFixedLengthStreamingMode(content.length); // Stream the data so we don't run out of memory
8683
try (OutputStream os = urlConnection.getOutputStream()) {
8784
os.write(content);
8885
}
8986
}
9087

9188
// Response
9289
int responseCode = urlConnection.getResponseCode();
93-
String responseMessage = urlConnection.getResponseMessage();
94-
95-
// Response Headers
9690
Map<String, List<String>> responseHeaders = urlConnection.getHeaderFields();
9791

98-
// Build response object
99-
HttpResponse response = new HttpResponse();
100-
response.setUrl(urlConnection.getURL().toString());
101-
response.setStatusCode(responseCode);
102-
response.setStatusMessage(responseMessage);
103-
response.setHeaders(responseHeaders);
104-
10592
// 'Successful' response codes will be in interval [200,300)
10693
if (responseCode >= 200 && responseCode < 300) {
107-
String responseContent = readStream(urlConnection.getInputStream());
108-
response.setContent(responseContent);
109-
handler.onSuccess(response);
94+
byte[] responseContent = readStreamAsBytes(urlConnection.getInputStream());
95+
handler.onSuccess(responseCode, responseHeaders, responseContent);
11096
} else {
111-
String responseContent = readStream(urlConnection.getErrorStream());
112-
response.setContent(responseContent);
113-
handler.onFailure(response);
97+
byte[] responseContent = readStreamAsBytes(urlConnection.getErrorStream());
98+
handler.onFailure(responseCode, responseHeaders, responseContent);
11499
}
115100

116101
} catch (IOException e) {
@@ -178,10 +163,6 @@ public void setUserAgent(String userAgent) {
178163
headers.put("User-Agent", userAgent);
179164
}
180165

181-
public void setCharset(String charset) {
182-
this.charset = charset;
183-
}
184-
185166
public int getConnectionTimeout() {
186167
return connectionTimeout;
187168
}

src/main/java/com/mb3364/http/HttpResponse.java

Lines changed: 0 additions & 65 deletions
This file was deleted.
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.mb3364.http;
22

3+
import java.util.List;
4+
import java.util.Map;
5+
36
public abstract class HttpResponseHandler {
4-
public abstract void onSuccess(HttpResponse response);
57

6-
public abstract void onFailure(HttpResponse response);
8+
public abstract void onSuccess(int statusCode, Map<String, List<String>> headers, byte[] content);
9+
10+
public abstract void onFailure(int statusCode, Map<String, List<String>> headers, byte[] content);
711

812
public abstract void onFailure(Throwable throwable);
913
}

src/main/java/com/mb3364/http/RequestParams.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ public class RequestParams {
1111
public static final String DEFAULT_ENCODING = "UTF-8";
1212

1313
private ConcurrentHashMap<String, String> params = new ConcurrentHashMap<>();
14+
1415
private String encoding;
1516

1617
public RequestParams() {
1718
encoding = DEFAULT_ENCODING;
1819
}
1920

21+
public RequestParams(String key, String value) {
22+
this();
23+
params.put(key, value);
24+
}
25+
2026
public boolean containsKey(String key) {
2127
return params.containsKey(key);
2228
}
@@ -25,6 +31,38 @@ public void put(String key, String value) {
2531
params.put(key, value);
2632
}
2733

34+
public void put(String key, short value) {
35+
params.put(key, Short.toString(value));
36+
}
37+
38+
public void put(String key, int value) {
39+
params.put(key, Integer.toString(value));
40+
}
41+
42+
public void put(String key, double value) {
43+
params.put(key, Double.toString(value));
44+
}
45+
46+
public void put(String key, float value) {
47+
params.put(key, Float.toString(value));
48+
}
49+
50+
public void put(String key, long value) {
51+
params.put(key, Long.toString(value));
52+
}
53+
54+
public void put(String key, boolean value) {
55+
params.put(key, Boolean.toString(value));
56+
}
57+
58+
public void put(String key, char value) {
59+
params.put(key, Character.toString(value));
60+
}
61+
62+
public void put(Map<String, String> otherMap) {
63+
params.putAll(otherMap);
64+
}
65+
2866
public String get(String key) {
2967
return params.get(key);
3068
}

0 commit comments

Comments
 (0)