This question was asked for Apache Commons HttpClient, however I'm using the async client HttpAsyncClient.
Content decompression (gzip) does not work out of the box.
I tried to configure it with:
httpClientAsync = HttpAsyncClients.custom()
.setMaxConnPerRoute(100)
.setMaxConnTotal(200)
// Enable response content encoding (gzip)
//
// NOTE: Does not work for some reason
.addInterceptorLast(ResponseContentEncoding())
Which I copied from HttpClientBuilder, but it doesn't work.
Any ideas?
The use of addInterceptorLast and addInterceptorFirst has no effect.
asyncHttpClient.execute() will create a BasicAsyncResponseConsumer by default.
This BasicAsyncResponseConsumer will copy the original ContentDecoder into the buffer, resulting in DecompressingEntity.getContent() is never called.
org.apache.http.impl.nio.client.CloseableHttpAsyncClient#execute()
public Future<HttpResponse> execute(
final HttpHost target, final HttpRequest request, final HttpContext context,
final FutureCallback<HttpResponse> callback) {
return execute(
HttpAsyncMethods.create(target, request),
HttpAsyncMethods.createConsumer(), // BasicAsyncResponseConsumer
context, callback);
}
org.apache.http.nio.protocol.BasicAsyncResponseConsumer#onContentReceived
protected void onContentReceived(
final ContentDecoder decoder, final IOControl ioControl) throws IOException {
Asserts.notNull(this.buf, "Content buffer");
this.buf.consumeContent(decoder);
}
My solution is to manually call ResponseContentEncoding.process(resp, context) in the callback to reset the HttpEntity.
private static final ResponseContentEncoding responseContentEncoding = new ResponseContentEncoding();
HttpClientContext hcc = HttpClientContext.create();
asyncHttpClient.execute(bidreq, hcc, new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse result) {
HttpEntity entity = null;
String content = null;
try {
responseContentEncoding.process(result, hcc);
entity = result.getEntity();
if (entity != null) {
content = EntityUtils.toString(entity, UTF_8);
log.info(content);
}
} catch (Exception e) {
log.error("error", e);
} finally {
EntityUtils.consumeQuietly(entity);
}
}
#Override
public void failed(Exception ex) {
log.error("failed", ex);
}
#Override
public void cancelled() { }
});
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 hours ago.
Improve this question
Using a pure Java,I try to send Get request and get response null.
Is there any bug in my code?
public static void TradeExchange() throws IOException, InterruptedException {
String urlString = "https://api.spimex.com/otc/lookup-tables/1";
HttpClient httpClient = new HttpClient() {
#Override
public Optional<CookieHandler> cookieHandler() {
return Optional.empty();
}
#Override
public Optional<Duration> connectTimeout() {
return Optional.empty();
}
#Override
public Redirect followRedirects() {
return null;
}
#Override
public Optional<ProxySelector> proxy() {
return Optional.empty();
}
#Override
public SSLContext sslContext() {
return null;
}
#Override
public SSLParameters sslParameters() {
return null;
}
#Override
public Optional<Authenticator> authenticator() {
return Optional.empty();
}
#Override
public Version version() {
return null;
}
#Override
public Optional<Executor> executor() {
return Optional.empty();
}
#Override
public <T> HttpResponse<T> send(HttpRequest httpRequest, HttpResponse.BodyHandler<T> bodyHandler) throws IOException, InterruptedException {
return null;
}
#Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest httpRequest, HttpResponse.BodyHandler<T> bodyHandler) {
return null;
}
#Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest httpRequest, HttpResponse.BodyHandler<T> bodyHandler, HttpResponse.PushPromiseHandler<T> pushPromiseHandler) {
return null;
}
};
HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(urlString))
.GET()
.build();
try {
HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
String body = response.body();
} catch (IOException | InterruptedException e) {
System.out.println(e.getMessage());
}
}
I have no idea why you're implementing your own HttpClient with methods which return nulls and empty optionals and expect it to work properly.
Java already has an implementation of Http client, you just need to use it.
HttpClient client = HttpClient.newHttpClient();
or using builder:
HttpClient.newBuilder()
...
Examples from official docs
An article which covers how to use Java HttpClient
You can do something similar. Your code returns null because you didn't provide any implementation for your HTTP client.
String urlString = "https://api.spimex.com/otc/lookup-tables/1";
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(urlString))
.GET()
.build();
try {
HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
String responseBody = response.body();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
you should not implement you own HttpClient that returns nulls you can try this instead :
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
I want to use OkHttp library for networking in Android.
I started with the simple post example as written in their website:
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
With this call:
String response = post("http://www.roundsapp.com/post", json);
This call ends with NetworkOnMainThreadException.
I could wrap the call with an AsyncTask, but as far as I understand from the examples, the OkHttp library should have already taken care of that..
Am I doing something wrong?
You should use OkHttp's async method.
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
Call post(String url, String json, Callback callback) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
And then your response would be handled in the callback (OkHttp 2.x):
post("http://www.roundsapp.com/post", json, new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
// Something went wrong
}
#Override public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Or OkHttp 3.x/4.x:
post("http://www.roundsapp.com/post", "", new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Something went wrong
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Take a look at their recipes for more examples: http://square.github.io/okhttp/recipes/
According to the OkHttp docs:
It supports both synchronous blocking calls and async calls with callbacks.
Your example is on main thread and Android since version 3.0 throws that exception if you try to do network calls on main thread
Better option is to use it together with retrofit and Gson:
http://square.github.io/retrofit/
https://code.google.com/p/google-gson/
Here are the examples:
http://engineering.meetme.com/2014/03/best-practices-for-consuming-apis-on-android/
http://heriman.net/?p=5
If you follows these steps to implement OKHTTP, then definitely you'll call multiple API on multiple screen by applying only two lines of code
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.getJsonData();
Step 1:
baseHTTPRequest = new BaseHTTPRequest();
// baseHTTPRequest.setURL("https://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demohttps://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo");
baseHTTPRequest.setURL("http://jsonparsing.parseapp.com/jsonData/moviesDemoItem.txt");
baseHTTPRequest.setRequestCode(reqType);
baseHTTPRequest.setCachedRequired(true);
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.executeRequest();
Step 2 : Create a request class
/**
* Created by Deepak Sharma on 4/7/16.
* This is a HTTP request class which has the basic parameters.
* If you wants to add some more parameters, please make a subclass of that class
* and add with your subclass. Don't modify this class.
*/
public class BaseHTTPRequest<T> {
private Context context;
private String URL;
private int requestCode;
private List<T> listParameters;
private String header;
private boolean isCachedRequired;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getURL() {
return URL;
}
public int getRequestCode() {
return requestCode;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public List<T> getListParameters() {
return listParameters;
}
public void setListParameters(List<T> listParameters) {
this.listParameters = listParameters;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public boolean isCachedRequired() {
return isCachedRequired;
}
public void setCachedRequired(boolean cachedRequired) {
isCachedRequired = cachedRequired;
}
}
step 4 : Create a listener class
import android.util.Log;
import com.google.gson.Gson;
import java.io.IOException;
import dxswifi_direct.com.wifidirectcommunication.base.model.request.BaseHTTPRequest;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Created by Deepak Sharma on 4/7/16.
* #email : dpsharma.sharma1#gmail.com
* This is a Simple java class which will help you for HTTP request/response and it will
* throw the response to your correspondance activity.
*/
public class UpdateListener {
private OnUpdateViewListener onUpdateViewListener;
OkHttpClient okHttpClient = new OkHttpClient();
BaseHTTPRequest mRequestModel;
private String mURL = null;
private Request mRequest = null;
public interface OnUpdateViewListener {
void updateView(String responseString, boolean isSuccess,int reqType);
}
public UpdateListener(OnUpdateViewListener onUpdateView, final BaseHTTPRequest requestModel) {
this.mRequestModel = requestModel;
this.onUpdateViewListener = onUpdateView;
if (requestModel.isCachedRequired())
{
/*File httpCacheDirectory = new File(requestModel.getContext().getCacheDir(), "responses");
Cache cache = null;
cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
if (cache != null) {
okHttpClient.setCache(cache);
}*/
}
/*mURL = null;
if (requestModel.getListParameters()!=null && requestModel.getListParameters().size()>0)
{
HttpUrl.Builder urlBuilder = HttpUrl.parse(requestModel.getURL()).newBuilder();
List<RequestParameter> requestParameters = requestModel.getListParameters();
for (int i=0; i<requestParameters.size();i++)
{
urlBuilder.addQueryParameter(requestParameters.get(i).getKey(),requestParameters.get(i).getValue());
}
mURL = urlBuilder.build().toString();
}
else
{
mURL = requestModel.getURL();
}*/
mURL = requestModel.getURL();
if (mRequestModel.getListParameters()!=null && mRequestModel.getListParameters().size()>1)
{
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
mRequest = new Request.Builder()
.url(mURL)
.post(RequestBody.create(JSON, new Gson().toJson(BaseHTTPRequest.class)))
.build();
}
else
{
mRequest = new Request.Builder()
.url(mURL)
.build();
}
}
public void executeRequest()
{
Call call = okHttpClient.newCall(mRequest);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
onUpdateViewListener.updateView(NetworkException.getErrorMessage(e), false, mRequestModel.getRequestCode());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
// You can also throw your own custom exception
throw new IOException("Unexpected code " + response);
} else {
Log.i("Response:",response.toString());
Log.i("Response body:",response.body().toString());
Log.i("Response message:",response.message());
onUpdateViewListener.updateView(response.body().string(),true, mRequestModel.getRequestCode());
}
// do something wih the result
}
});
}
}
step 5 : From the activity you requesting, implement listener
public class HitAPIActivity extends AppCompatActivity implements View.OnClickListener, UpdateListener.OnUpdateViewListener{
#Override
public void updateView(final String responseString, boolean isSuccess, int reqType) {
if (isSuccess)
{
if (!responseString.contains("failure")
&& !responseString.contains("Error")) {
// Handle request on the basis of Request Type.
switch (reqType) {
case ApiConstants.GET_CONTACTS:
break;
default:
break;
}
}
}
}
I have a servlet filter that intercepts requests and checks for a custom "encrypted" header:
public class EncryptionFilter extends GenericFilterBean{
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getHeader("EncryptedCommunication") != null){
decryptedRequest = /*decrypt request body and forward to next filter*/
encryptedResponse = /*encrypt request body and forward to next filter*/
filterChain.doFilter(decryptedRequest, encryptedResponse);
}
else { /* communication not encrypted */
filterChain.doFilter(request, response);
}
}
}
When the header exist I should decrypt the request body and also encrypt the response body.
Otherwise, should leave the request/response body unchanged.
How can I change the response only when needed?
You need to use a HttpServletResponseWrapper example is :
filterChain.doFilter(servletRequest,
new HttpServletResponseWrapper((HttpServletResponse) servletResponse) {
#Override
public void setHeader(String name, String value) {
if (!HTTPCacheHeader.ETAG.getName().equalsIgnoreCase(name)) {
super.setHeader(name, value);
}
}
});
See http://www.programcreek.com/java-api-examples/javax.servlet.http.HttpServletResponseWrapper
This is an example of how the body can be set :
public class ReadTwiceHttpServletRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
public ReadTwiceHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
try {
IOUtils.copy(request.getInputStream(), outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())));
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
return new ServletInputStream() {
#Override
public int readLine(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
#Override
public boolean isFinished() {
return inputStream.available() > 0;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener arg0) {
// TODO Auto-generated method stub
}
#Override
public int read() throws IOException {
return inputStream.read();
}
};
}
public void setBody(String body) {
outputStream = new ByteArrayOutputStream();
try {
outputStream.write(body.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getBody() {
return new String(outputStream.toByteArray());
}
See How to get the XML from POST request and modify it in Servlet Filter?
When I create custom Call class I can't return Response, because Response class is final. Is there any workaround for this?
public class TestCall implements Call<PlacesResults> {
String fileType;
String getPlacesJson = "getplaces.json";
String getPlacesUpdatedJson = "getplaces_updated.json";
public TestCall(String fileType) {
this.fileType = fileType;
}
#Override
public Response execute() throws IOException {
String responseString;
InputStream is;
if (fileType.equals(getPlacesJson)) {
is = InstrumentationRegistry.getContext().getAssets().open(getPlacesJson);
} else {
is = InstrumentationRegistry.getContext().getAssets().open(getPlacesUpdatedJson);
}
PlacesResults placesResults= new Gson().fromJson(new InputStreamReader(is), PlacesResults.class);
//CAN"T DO IT
return new Response<PlacesResults>(null, placesResults, null);
}
#Override
public void enqueue(Callback callback) {
}
//default methods here
//....
}
In my unit test class I want to use it like this:
Mockito.when(mockApi.getNearbyPlaces(eq("testkey"), Matchers.anyString(), Matchers.anyInt())).thenReturn(new TestCall("getplaces.json"));
GetPlacesAction action = new GetPlacesAction(getContext().getContentResolver(), mockEventBus, mockApi, "testkey");
action.downloadPlaces();
My downloadPlaces() method look like:
public void downloadPlaces() {
Call<PlacesResults> call = api.getNearbyPlaces(webApiKey, LocationLocator.getInstance().getLastLocation(), 500);
PlacesResults jsonResponse = null;
try {
Response<PlacesResults> response = call.execute();
Timber.d("response " + response);
jsonResponse = response.body();
if (jsonResponse == null) {
throw new IllegalStateException("Response is null");
}
} catch (UnknownHostException e) {
events.sendError(EventBus.ERROR_NO_CONNECTION);
} catch (Exception e) {
events.sendError(EventBus.ERROR_NO_PLACES);
return;
}
//TODO: some database operations
}
After looking at retrofit2 Response class more thoroughly I've found out that there is a static method that do what I need. So, I simply changed this line:
return new Response<PlacesResults>(null, placesResults, null);
to:
return Response.success(placesResults);
Everything works now.
I have a bolt that is making an API call (HTTP Get) for every tuple.
to avoid the need to wait for the response, I was looking to use the apache HttpAsyncClient.
after instantiating the client in the bolt's prepare method, the execute method constructs the URL from the tuple and calls sendAsyncGetRequest(url):
private void sendAsyncGetRequest(String url){
httpclient.execute(new HttpGet(url), new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
LOG.info("Response Code : " + response.getStatusLine());
LOG.debug(response.toString());
}
#Override
public void failed(Exception ex) {
LOG.warn("Async http request failed!", ex);
}
#Override
public void cancelled() {
LOG.warn("Async http request canceled!");
}
});
}
the topology deploys but the Storm UI shows an error:
java.lang.RuntimeException: java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED at backtype.storm.utils.DisruptorQueue.consumeBatchToCursor(DisruptorQueue.java:12
I got this to work with no issues.
the key things to note are:
declare the client on the bolt class scope
public class MyRichBolt extends BaseRichBolt {
private CloseableHttpAsyncClient httpclient;
Instantiate and stat the client in the bolt's prepare method
#Override
public final void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
try {
// start the http client
httpclient = HttpAsyncClients.createDefault();
httpclient.start();
// other initialization code ...
} catch (Throwable exception) {
// handle errors
}
}
make the calls in the bolt's execute method
#Override
public final void execute(Tuple tuple) {
// format the request url
String url = ...
sendAsyncGetRequest(url);
}
private void sendAsyncGetRequest(String url){
logger.debug("Async call to URL...");
HttpGet request = new HttpGet(url);
HttpAsyncRequestProducer producer = HttpAsyncMethods.create(request);
AsyncCharConsumer<HttpResponse> consumer = new AsyncCharConsumer<HttpResponse>() {
HttpResponse response;
#Override
protected void onResponseReceived(final HttpResponse response) {
this.response = response;
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
// Do something useful
}
#Override
protected void releaseResources() {
}
#Override
protected HttpResponse buildResult(final HttpContext context) {
return this.response;
}
};
httpclient.execute(producer, consumer, new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
// do something useful with the response
logger.debug(response.toString());
}
#Override
public void failed(Exception ex) {
logger.warn("!!! Async http request failed!", ex);
}
#Override
public void cancelled() {
logger.warn("Async http request canceled!");
}
});
}
Are you shutting down the client (client.close();) in your main flow before the callback can execute?
The error is saying that the IO path has already been closed. In general, instances of async clients should be re-used for repeated requests and destroyed only when "ALL" requests have been made, e.g. at application shutdown.