How to get httpclient response as stream - java

I am using httpclient 4.5.5
i want to get large files upto 1 gb in response. But it seems like
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
This returns whole response so its not good to have whole response in memory. Is there any way to get response as stream?

Apache HttpClient as of version 4.0 (as well as Apache HttpAsyncClient) supports full content streaming for both incoming and outgoing HTTP messages. Use HttpEntity to get access to the underling content input stream
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://myhost/tons-of-stuff");
try (CloseableHttpResponse response1 = client.execute(httpGet)) {
final HttpEntity entity = response1.getEntity();
if (entity != null) {
try (InputStream inputStream = entity.getContent()) {
// do something useful with the stream
}
}
}

You need Apache Async Client.
HttpAsyncClient is the ASYNC version of Apache HttpClient. Apache HttpClient construct the whole response in memory, while with HttpAsyncClient, you can define a Handler (Consumer) to process the response while receiving data.
https://hc.apache.org/httpcomponents-asyncclient-4.1.x/index.html
Here is an example from their official example code
package org.apache.http.examples.nio.client;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.concurrent.Future;
import org.apache.http.HttpResponse;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.client.methods.AsyncCharConsumer;
import org.apache.http.nio.client.methods.HttpAsyncMethods;
import org.apache.http.protocol.HttpContext;
/**
* This example demonstrates an asynchronous HTTP request / response exchange with
* a full content streaming.
*/
public class AsyncClientHttpExchangeStreaming {
public static void main(final String[] args) throws Exception {
CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
try {
httpclient.start();
Future<Boolean> future = httpclient.execute(
HttpAsyncMethods.createGet("http://httpbin.org/"),
new MyResponseConsumer(), null);
Boolean result = future.get();
if (result != null && result.booleanValue()) {
System.out.println("Request successfully executed");
} else {
System.out.println("Request failed");
}
System.out.println("Shutting down");
} finally {
httpclient.close();
}
System.out.println("Done");
}
static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {
#Override
protected void onResponseReceived(final HttpResponse response) {
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
while (buf.hasRemaining()) {
System.out.print(buf.get());
}
}
#Override
protected void releaseResources() {
}
#Override
protected Boolean buildResult(final HttpContext context) {
return Boolean.TRUE;
}
}
}

Use HttpURLConnection instead of httpClient.
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
final int bufferSize = 1024 * 1024;
conn.setChunkedStreamingMode(bufferSize);
final OutputStream out = conn.getOutputStream();

Related

Java HttpClient with NTLM - how to use default network credentials

In .NET we can create HttpClient that would use credentials of the current process/user:
var uri = new Uri("http://service-endpoint");
var credentialsCache = new CredentialCache { { uri, "NTLM", CredentialCache.DefaultNetworkCredentials } };
var handler = new HttpClientHandler { Credentials = credentialsCache };
var httpClient = new HttpClient(handler) { BaseAddress = uri, Timeout = new TimeSpan(0, 0, 10) };
Is there an equivalent in Java? I want to be able to send the credentials transparently so the user won't be bothered.
Answering my own question.
It is possible using WinHttpClients from Apache HttpClient 5 https://repo1.maven.org/maven2/org/apache/httpcomponents/client5/httpclient5-win/
import statements:
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.win.WinHttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
sample request:
public static void Get(String uri) {
var request = new HttpGet(uri);
try
{
CloseableHttpClient httpClient = WinHttpClients.createDefault();
CloseableHttpResponse httpResponse = httpClient.execute(request);
System.out.println(httpResponse.getCode()); //200
HttpEntity entity = httpResponse.getEntity();
if (entity != null)
{
System.out.println(EntityUtils.toString(entity)); //Success
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}

Why is my Android app connecting to the Reddit API, but getting a blank response?

I am trying to learn how to connect to an API and receieve and parse JSON data so I am currently following an example on this webpage: http://www.whycouch.com/2012/12/how-to-create-android-client-for-reddit.html, but I am getting an error that says:
E/fetchPosts(): org.json.JSONException: End of input at character 0 of
My app is connecting because it says that a new host connection has been established so I'm not quite sure as to why it's getting a blank response. Below is my class that gets the connection and reads the contents. If I had to guess where I went wrong, I would say it has to do with the request properties, but I went to reddit's website and formatted it like they want and it's still not returning anything. Thank you.
public class RemoteData {
/*
This method returns a connection to the specified URL,
with necessary properties like timeout and user-agent
set to your requirements.
*/
public static HttpURLConnection getConnection(String url){
System.out.println("URL: " + url);
HttpURLConnection hcon = null;
try{
hcon = (HttpURLConnection)new URL(url).openConnection();
hcon.setReadTimeout(30000); //Timeout set at 30 seconds
hcon.setRequestProperty("User-Agent", "android:com.example.reddittestappbydrew:v0.0.1");
}catch(MalformedURLException e){
Log.e("getConnection()", "Invalid URL: " +e.toString());
}catch (IOException e){
Log.e("getConnection()", "Could not connect: " + e.toString());
}
return hcon;
}
/*
A utility method that reads the contents of a url and returns them as a string
*/
public static String readContents(String url){
HttpURLConnection hcon = getConnection(url);
if(hcon == null) return null;
try{
StringBuffer sb = new StringBuffer(8192);
String tmp = "";
BufferedReader br = new BufferedReader(new InputStreamReader(hcon.getInputStream()));
while((tmp = br.readLine()) != null){
sb.append(tmp).append("\n");
}
br.close();
return sb.toString();
}catch(IOException e){
Log.d("READ FAILED", e.toString());
return null;
}
}
}
The code you have written looks pretty naive for getting html/json response data from my URL as redirects are not being handled there. Either you handle redirects in your code which you can do by checking hcon.getResponseCode() whose value should be 200 for you to read the data successfully. In case it is not 200 and something else like 301 (redirects) or 403 (authorization required), you need to handle these responses accordingly.
Here I am giving you a simple code which uses HttpClient (I am using httpclient-4.2.1) library from apache, and gets the response back as String.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.fileupload.util.Streams;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpUtils {
private static Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
public static String getResponse(String url) throws IOException {
return getResponse(url, "UTF-8");
}
public static String getResponse(String url, String characterEncoding) throws IOException {
return getByteArrayOutputStream(url).toString(characterEncoding);
}
public static byte[] getBytes(String url) throws IOException {
return getByteArrayOutputStream(url).toByteArray();
}
public static ByteArrayOutputStream getByteArrayOutputStream(String url) throws IOException {
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpclient.execute(httpGet);
LOGGER.debug("Status Line: " + response.getStatusLine());
HttpEntity resEntity = response.getEntity();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Streams.copy(resEntity.getContent(), byteArrayOutputStream, true);
return byteArrayOutputStream;
}
public static void main(String[] args) throws IOException {
System.out.println(getResponse("https://www.reddit.com/r/AskReddit/.json"));
}
}
Use this code to achieve what you want and in case you don't want to use HTTPClient API, then modify your existing code to handle http status codes but it would be simple for you to use above code.

org.apache.http is deprecated, what to use?

I have an app that in order to download Json data uses org.apache.http
this is the class I use in order to make the request:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
#SuppressWarnings("deprecation")
public class ServiceHandler {
static String response = null;
public final static int GET = 1;
public final static int POST = 2;
public ServiceHandler() {
}
/**
* Making service call
* #url - url to make request
* #method - http request method
* */
public String makeServiceCall(String url, int method) {
return this.makeServiceCall(url, method, null);
}
/**
* Making service call
* #url - url to make request
* #method - http request method
* #params - http request params
* */
public String makeServiceCall(String url, int method,
List<NameValuePair> params) {
try {
// http client
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpEntity httpEntity = null;
HttpResponse httpResponse = null;
// Checking http request method type
if (method == POST) {
HttpPost httpPost = new HttpPost(url);
// adding post params
if (params != null) {
httpPost.setEntity(new UrlEncodedFormEntity(params));
}
httpResponse = httpClient.execute(httpPost);
} else if (method == GET) {
// appending params to url
if (params != null) {
String paramString = URLEncodedUtils
.format(params, "utf-8");
url += "?" + paramString;
}
HttpGet httpGet = new HttpGet(url);
httpResponse = httpClient.execute(httpGet);
}
httpEntity = httpResponse.getEntity();
response = EntityUtils.toString(httpEntity,HTTP.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}
The problem is that every import from org.apache.http is deprecated and I don't know if i'll have problem using this class. Can someone point me to the right direction in order to "update" my class using non-deprecated methods?
EDIT:
From the Android M documentation:
This preview removes support for the Apache HTTP client. If your app is using this client and targets Android 2.3 (API level 9) or higher, use the HttpURLConnection class instead. This API is more efficient because it reduces network use through transparent compression and response caching, and minimizes power consumption. To continue using the Apache HTTP APIs, you must first declare the following compile-time dependency in your build.gradle file:
android {
useLibrary 'org.apache.http.legacy'
}
So my app will crash using that class right?
Instead of DefaultHttpClient use
HttpClient httpClient = HttpClientBuilder.create().build();
And Instead of HTTP.UTF_8 use
StandardCharsets.UTF_8
For HTTP.UTF_8 the alternative is Consts.UTF_8. It's weird they don't mention that in the docs. Consts.UTF_8 is a Charset whereas HTTP.UTF_8 is a String. HttpProtocolParams.setContentCharset(HttpParams httpParams, String charset) is expecting a String, not the Consts Charset.
For string we can use by String.valueOf(Consts.UTF_8)
Reference link

Mocking Apache HTTPClient using Mockito

I'm trying to mock Apache HttpClient Interface in order to mock one of its methods mentioned below to return a stubbed JSON object in response.
HttpResponse response = defaultHttpClient.execute(postRequest);
Could somebody be able to suggest how to achieve this with some sample code? Your help would be greatly appreciated.
Thanks
Here is what I did to test my code using Mockito and Apache HttpBuilder:
Class under test:
import java.io.BufferedReader;
import java.io.IOException;
import javax.ws.rs.core.Response.Status;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StatusApiClient {
private static final Logger LOG = LoggerFactory.getLogger(StatusApiClient.class);
private String targetUrl = "";
private HttpClient client = null;
HttpGet httpGet = null;
public StatusApiClient(HttpClient client, HttpGet httpGet) {
this.client = client;
this.httpGet = httpGet;
}
public StatusApiClient(String targetUrl) {
this.targetUrl = targetUrl;
this.client = HttpClientBuilder.create().build();
this.httpGet = new HttpGet(targetUrl);
}
public boolean getStatus() {
BufferedReader rd = null;
boolean status = false;
try{
LOG.debug("Requesting status: " + targetUrl);
HttpResponse response = client.execute(httpGet);
if(response.getStatusLine().getStatusCode() == Status.OK.getStatusCode()) {
LOG.debug("Is online.");
status = true;
}
} catch(Exception e) {
LOG.error("Error getting the status", e);
} finally {
if (rd != null) {
try{
rd.close();
} catch (IOException ioe) {
LOG.error("Error while closing the Buffered Reader used for reading the status", ioe);
}
}
}
return status;
}
}
Test:
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.HttpHostConnectException;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class StatusApiClientTest extends Mockito {
#Test
public void should_return_true_if_the_status_api_works_properly() throws ClientProtocolException, IOException {
//given:
HttpClient httpClient = mock(HttpClient.class);
HttpGet httpGet = mock(HttpGet.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
//and:
when(statusLine.getStatusCode()).thenReturn(200);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(httpClient.execute(httpGet)).thenReturn(httpResponse);
//and:
StatusApiClient client = new StatusApiClient(httpClient, httpGet);
//when:
boolean status = client.getStatus();
//then:
Assert.assertTrue(status);
}
#Test
public void should_return_false_if_status_api_do_not_respond() throws ClientProtocolException, IOException {
//given:
HttpClient httpClient = mock(HttpClient.class);
HttpGet httpGet = mock(HttpGet.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
//and:
when(httpClient.execute(httpGet)).thenThrow(HttpHostConnectException.class);
//and:
StatusApiClient client = new StatusApiClient(httpClient, httpGet);
//when:
boolean status = client.getStatus();
//then:
Assert.assertFalse(status);
}
}
What do you think folks, do I need to improve something? (Yeah, I know, the comments. That is something I brought from my Spock background :D)
In your unit test class you need to mock defaultHttpClient:
#Mock
private HttpClient defaultHttpClient;
Then you tell mockito (for example in #Before method) to actually create your mocks by:
MockitoAnnotations.initMocks(YourTestClass);
Then in your test method you define what execute() method should return:
when(defaultHttpClient.execute(any()/* or wahtever you want here */)).thenReturn(stubbed JSON object);
There is a nicer way to do this without having to add PowerMock as yet another dependency. Here you only need an extra constructor taking HTTPClient as an argument and Mockito. In this example I'm creating a custom health check (Spring Actuator) and I need to mock the HTTPClient for unit testing.
Libs: JUnit 5, Spring Boot 2.1.2 and Mockito 2.
Component:
#Component
public class MyHealthCheck extends AbstractHealthIndicator {
HttpClient httpClient;
public MyHealthCheck() {
httpClient = HttpClientBuilder.create().build();
}
/**
Added another constructor to the class with an HttpClient argument.
This one can be used for testing
*/
public MyHealthCheck(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
Method to test
*/
#Override
protected void doHealthCheck(Builder builder) throws Exception {
//
// Execute request and get status code
HttpGet request = new HttpGet("http://www.SomeAuthEndpoint.com");
HttpResponse response = httpClient.execute(request);
//
// Update builder according to status code
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode == 200 || statusCode == 401) {
builder.up().withDetail("Code from service", statusCode);
} else {
builder.unknown().withDetail("Code from service", statusCode);
}
}
}
Test method:
Note that here we use Mockito.any(HttpGet.class)
private static HttpClient httpClient;
private static HttpResponse httpResponse;
private static StatusLine statusLine;
#BeforeAll
public static void init() {
//
// Given
httpClient = Mockito.mock(HttpClient.class);
httpResponse = Mockito.mock(HttpResponse.class);
statusLine = Mockito.mock(StatusLine.class);
}
#Test
public void doHealthCheck_endReturns401_shouldReturnUp() throws Exception {
//
// When
when(statusLine.getStatusCode()).thenReturn(401);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(httpClient.execute(Mockito.any(HttpGet.class))).thenReturn(httpResponse);
//
// Then
MyHealthCheck myHealthCheck = new MyHealthCheck(httpClient);
Health.Builder builder = new Health.Builder();
myHealthCheck.doHealthCheck(builder);
Status status = builder.build().getStatus();
Assertions.assertTrue(Status.UP == status);
}
You can look at HttpClientMock, I wrote it for internal project but later decided to open source. It allows you to define mock behavior with fluent API and later verify a number of made calls. Example:
HttpClientMock httpClientMock = new
HttpClientMock("http://localhost:8080");
httpClientMock.onGet("/login?user=john").doReturnJSON("{permission:1}");
httpClientMock.verify().get("/login?user=john").called();
You can do this easily using PowerMockito which can also mock final/static methods, private methods and anonymous classes easily. Here's the sample code for mocking http request. JSON_STRING_DATA is any string which you want to get from execute method.
PowerMockito.mockStatic(DefaultHttpClient.class);
HttpClient defaultHttpClientMocked = PowerMockito.mock(DefaultHttpClient.class);
PowerMockito.when(defaultHttpClientMocked.execute(Mockito.any(HttpPost.class))).thenReturn(createMockedHTTPResponse(JSON_STRING_DATA));

Bouncing between "Adapter is detached" and "No wrapped connection" with HttpClient

So as i said i'm bouncing back and forth between these two errors when trying to run HttpClient.execute(HttpPost). Getting IllegalStateException
public class NetMethods {
private static HttpClient client = new DefaultHttpClient();
public static void getStuff() {
ArrayList<Alarm> alarms = new ArrayList<Alarm>();
HttpPost post = HttpPostFactory.getHttpPost("GetStuff");
StringBuilder builder = new StringBuilder();
HttpResponse response = client.execute(post); // Exception thrown here
...
Also, my MttpPostFactory just has this
import org.apache.http.client.methods.HttpPost;
public class HttpPostFactory {
private static final String url = "http://example.com/ExampleFolder/";
public static HttpPost getHttpPost(String s) {
return new HttpPost(url + s);
}
}
This may arise from not closing the InputStream's you get from HttpClient, especially if arising from different threads...either not reading the whole content or calling the same HttpClient instance from two different threads.
I found solution from Androider's blog:
I got the log :
Invalid use of SingleClientConnManager: connection still allocated.
or
Caused by: java.lang.IllegalStateException: No wrapped connection.
or
Caused by: java.lang.IllegalStateException: Adapter is detached.
Finally got Solution:
public static DefaultHttpClient getThreadSafeClient() {
DefaultHttpClient client = new DefaultHttpClient();
ClientConnectionManager mgr = client.getConnectionManager();
HttpParams params = client.getParams();
client = new DefaultHttpClient(new ThreadSafeClientConnManager(params,
mgr.getSchemeRegistry()), params);
return client;
}
Try with this..
// Execute the asynctask with your URL
new myAsyncTask().execute("http://example.com/ExampleFolder/");
// Asynctask Callback method
private class myAsyncTask extends AsyncTask<String, Void, Void>
{
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected Void doInBackground(String... arg0)
{
// Creating service handler class instance
ServiceHandler serhand = new ServiceHandler();
// Making a request to url and getting response
serhand.makeServiceCall(arg0[0], ServiceHandler.GET);
return null;
}
protected void onPostExecute(Void result)
{
}
}
// Service Handler class
package yourpagename
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class ServiceHandler {
static String response = null;
public final static int GET = 1;
public final static int POST = 2;
public ServiceHandler() {
}
/**
* Making service call
* #url - url to make request
* #method - http request method
* */
public String makeServiceCall(String url, int method) {
return this.makeServiceCall(url, method, null);
}
/**
* Making service call
* #url - url to make request
* #method - http request method
* #params - http request params
* */
public String makeServiceCall(String url, int method,
List<NameValuePair> params) {
try {
// http client
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpEntity httpEntity = null;
HttpResponse httpResponse = null;
// Checking http request method type
if (method == POST) {
HttpPost httpPost = new HttpPost(url);
// adding post params
if (params != null) {
httpPost.setEntity(new UrlEncodedFormEntity(params));
}
httpResponse = httpClient.execute(httpPost);
} else if (method == GET) {
// appending params to url
if (params != null) {
String paramString = URLEncodedUtils
.format(params, "utf-8");
url += "?" + paramString;
}
HttpGet httpGet = new HttpGet(url);
httpResponse = httpClient.execute(httpGet);
}
httpEntity = httpResponse.getEntity();
response = EntityUtils.toString(httpEntity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}

Categories

Resources