OKHTTP newCall() hangs when calling restAPI - java

Trying to post to a restAPI using JAVA OKHttp.
My code looks as follows:
try {
loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build();
MediaType mediaType = MediaType.parse("app.lication/json");
RequestBody body = RequestBody.create(mediaType, FileUtils.readFileToString(file));
Request request = new Request.Builder()
.url(restAPIUrl)
.post(body)
.addHeader("content-type", "application/json")
.addHeader("cache-control", "no-cache")
.build();
Call call = client.newCall(request);
call.execute();
handleResponse(file, response.code());
} catch (Exception e) {
logger.severe("Http Request failed: " + e.getMessage());
}
On my local development box (centos) that doesn't have firewall open to the restAPI, every thing behaves as expected. call.execute() throws an exception: Could not resolve host: Name or service not available.
On the production box (rhel), where firewall is open. The method call to client.newCall(request); just hangs indefinitely. (Before calling execute()) strace just shows the thread waiting.
I've verified i can reach the restAPI from command line with:
curl -X get https://restAPIUrl/index.html
The same jar is behaving differently on the two servers. I can't figure out why differences in server configuration would affect the newCall() (I would more expect it on execute)
What could be the cause for newCall() hanging due to environment? Any help would be much appreciated.
thank you!
Update:
I found that it gets past the newCall() method if I override the defaultSocketFactory.
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.setSocketFactory(new SocketFactory () {
#Override
public Socket createSocket(String s int i) thorws IOException, UnknownHostException {
return null;
}
.
.
})
.build();
Why wouldn't the system properly allocate the socket for the client using defaultSocketFactory on the rhel env? (SELINUX disabled.)
Update 2: Resolution
In the end this issue was caused by no read permissions on 'local_policy.jar' in $JAVA_HOME/lib/security. (File didn't exist in my dev environment)
The nasty part was the permissions exception was being swallowed by the OKHttp API.

Try to patch the security provider as described in "Update your security provider to protect against SSL exploits".
Just implement ProviderInstaller.installIfNeeded (or installIfNeededAsync)

Related

How to release the http connection

I call below code within my application. The first request is always working fine. My issue is that every following request is not sent, it runs into timeout, when I specify a timeout value. Otherwise it seems to wait endlessly. It seems the first request blocks the connection for every following attempt. How can I ensure the connection is properly released again? Maybe some headers? Maybe some properties (defaults are used for http https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/doc-files/net-properties.html)?
HttpRequest request = HttpRequest.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.GET()
.uri(URI.create(url))
.build();
try {
HttpResponse<Path> response = client.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get(outfile)));
} catch (IOException | InterruptedException e) {
// ...
}
used: java.net.http.HttpClient (AdoptOpenJDK 11.0.7_10)
The send method is a Sync method, so the request get block until you get a response, in this case maybe you are not getting the response.
send(HttpRequest, BodyHandler) blocks until the request has been sent and the response has been received.
Try using the Async method
sendAsync(HttpRequest, BodyHandler) sends the request and receives the response asynchronously.
The sendAsync method returns immediately with a CompletableFuture. The CompletableFuture completes when the response becomes available. The returned CompletableFuture can be combined in different ways to declare dependencies among several asynchronous tasks.
Example of an async request (taken from the apidoc):
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json")))
.build();
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);

Malformed reply from SOCKS server when using OkHttpClient

I'm sending REST requests in Java using a feign client which works perfectly fine, however when additionally using an OkHttpClient I get an error message
Caused by: feign.RetryableException: Malformed reply from SOCKS server executing GET
I identified the line of code causing this error which is
OkHttpClient.Builder builder = new OkHttpClient.Builder();
...
builder.proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksProxyHost, socksProxyPort)));
However since I both need the OkHttpClient and to send requests using the feign client I can't just remove this but instead have to find a workaround. Is there a way to reset the proxy settings for as long as I'm sending the requests via feign and set them back afterwards? I tried setting the default proxy server to null using
proxySelector.setDefault(null)
but that unfortunately didn't resolve my issue.
Thanks for your help!
Create two instances of OkHttpClient, one with the proxy configured and one with none.
builder.proxy(Proxy.NO_PROXY);
If you use OkHttpClient.newBuilder() to create one client from the other they'll share an ExecutorService and other resources.
I fixed the Problem by assigning a new ProxySelector instead of the actual proxy. When overriding the select method I made sure the proxy list is empty for requests sent using the feign client:
ProxySelector proxySelector = new ProxySelector() {
#Override
public List<Proxy> select(URI uri) {
if (uri.toString().contains("HOST_NAME")) {
return List.of();
}
return List.of(
new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksProxyHost, socksProxyPort)));
}
#Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
}
};
builder.proxySelector(proxySelector);

java.io.IOException: unexpected end of stream on Connection? [duplicate]

This question already has answers here:
java.io.IOException: unexpected end of stream on Connection in android
(12 answers)
Closed 3 years ago.
Calling one of our in-house web services seems to be giving the following error:
java.io.IOException: unexpected end of stream on Connection{webservicessandbox.xxx.com:443, proxy=DIRECT# hostAddress=174.143.185.13 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1} (recycle count=0)
From what I've seen elsewhere, this is being pointed to as a server issue, but we're not seeing this problem when the WS is being called in browser or in IOS. I've used both OkHTTP and a manual HTTPUrlConnection implementation and it hasn't seemed to make any difference. Does anyone have any ideas?
OKHttp:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + apiToken)
.addHeader("content-type", "application/json")
.url(uri)
.build();
HTTPURLConnection:
URL myURL = new URL(uri);
HttpsURLConnection myConnection = (HttpsURLConnection) myURL.openConnection();
myConnection.setConnectTimeout(TIMEOUT_MILLISEC);
myConnection.setDoOutput(false);
myConnection.setRequestMethod("GET");
myConnection.setRequestProperty("Authorization", "Bearer " + apiToken);
myConnection.setRequestProperty("content-type", "application/json");
int respCode = myConnection.getResponseCode();
Please try the potential solutions (in my opinion) below:
1- Try to use retryOnConnectionFailure(true) as below:
OkHttpClient client = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.build();
2- Try to add: request.addHeader("Connection","close") (most cases this is the solution, respecting to Kartik's answer.)
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + apiToken)
.addHeader("content-type", "application/json")
.addHeader("Connection","close")
.url(uri)
.build();
3- Please increase the response time of the server (set it as high as possible) and try again.
Also see: OkHTTP Websocket: Unexpected end of steam on Connection
Both the client and server needs to close the connection once they are finished.
In your OkHttp request builder, add this line: .header("Connection", "close").
Also check the network tab of your browser inspector to see which headers the browser is sending. Match those headers in your code and it should work.
This error TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 is related to server. A possible reason could be, the server is shut down.

failure : retrofit.RetrofitError: 307 Temporary Redirect?

I am using the library com.squareup.retrofit:retrofit:1.9.0, in order to send data to my server from my android Application.
In fact,when I want to send a request to the server, I found this error:
failure : retrofit.RetrofitError: 307 Temporary Redirect
I try many idea but the same problem persist.
Please Expert help me to resolve this issue.
Regards
Edit: if you must not use Retrofit 1.9, switching to a newer version (ie 2.0+) should handle the solution covered below automatically.
Looks like your HTTP client (on Android side) should handle this redirection by reading the Location value in the response header you are receiving when this happens, which supposed to contain a target redirection URL you ought to hit from the client again.
See their comment here.
So for now you need to implement this at the application level (which
is hard with Retrofit 1, easier with the forthcoming Retrofit 2) or
wait until OkHttp 3.1(ish) when we implement the newest spec.
See also what 307 means.
Fixing 307 errors - general The 307 response from the Web server
should always include an alternative URL to which redirection should
occur. If it does, a Web browser will immediately retry the
alternative URL. So you never actually see a 307 error in a Web
browser, unless perhaps you have a corrupt redirection chain e.g. URL
A redirects to URL B which in turn redirects back to URL A. If your
client is not a Web browser, it should behave in the same way as a Web
browser i.e. immediately retry the alternative URL.
If the Web server does not return an alternative URL with the 307
response, then either the Web server sofware itself is defective or
the Webmaster has not set up the URL redirection correctly.
See javadoc for Retrofit 1.9.0 to grab the Location header value(URL) from response;
http://static.javadoc.io/com.squareup.retrofit/retrofit/1.9.0/retrofit/client/Response.html#getHeaders--
// omitting null check for brevity
for (Header header : response.getHeaders())
{
if (header.getName().equals("Location")) {
redirectURL = header.getValue();
}
}
// do a redirect to redirectURL
I got the solution after a lot of efforts. May be this will help you. If you are getting this error "203 HTTP 307 Temporary Redirect" then this trick will help you.
Append '/' to your web service at the end and then check this error goes away. I don't know the reason behind this but it works for me.
For me, my old request web service: https://mydomain/rest/Search.
Using this URL I was getting "203 HTTP 307 Temporary Redirect" where I was able to get a response in POSTMAN and web browser.
My new request web service with the trick: https://mydomain/rest/Search/.
This '/' resolve the issue.
You are Using Old dependency
Change your dependency from
"com.squareup.retrofit:retrofit:1.9.0"
to
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
also add this dependency to get full log
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'.
Add this method in your Retrofit Client Class
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.connectTimeout("Your_Time", TimeUnit.SECONDS);
httpClient.readTimeout("Your_Time", TimeUnit.SECONDS);
httpClient.addInterceptor(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("BASE URL")
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
Below code is to Handle the Http protocol Exception
public Response post(String url, String content) throws IOException {
RequestBody body = RequestBody.create(PROTOCOL, content);
Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
Request request = requestBuilder.build();
Response response = this.client.newCall(request).execute();
if(response.code() == 307) {
String location = response.header("Location");
return post(location, content);
}
return response;
}
If you want to handle all 307 responses, you can create and add an Interceptor to do the job; This interceptor simply creates a new request with the new path which is indicated in the response header "location".
val client = OkHttpClient.Builder()
.addInterceptor {
val request = it.request()
val response = it.proceed(request)
if (response.code() == 307) {
val url = HttpUrl.Builder()
.scheme(request.url().scheme())
.host(request.url().host())
.addPathSegment(response.header("location", request.url().url().path)?: "")
.build()
val newRequest = Request.Builder()
.method(request.method(), request.body())
.url(url)
.headers(request.headers())
.build()
return#addInterceptor it.proceed(newRequest)
}
response
}
.build()
Retrofit.Builder().client(client).build()
Solution 1
You can create an extra interceptor to handle 307
private static class RedirectInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(chain.request());
if (response.code() == 307) {
request = request.newBuilder()
.url(response.header("Location"))
.build();
response = chain.proceed(request);
}
return response;
}
}
and add the RedirectInterceptor to your okHttpClient(okhttp2 for example)
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new RedirectInterceptor());
okHttpClient.setFollowRedirects(false);
Solution 2
You can create an ApacheClient with LaxRedirectStrategy
private ApacheClient createRedirectClient(){
return new ApacheClient(HttpClients.custom()
.setRedirectStrategy(new LaxRedirectStrategy()).build());
}
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint("http://yourendpoint")
.setClient(createRedirectClient());
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
Try to put HttpLoggingInterceptor and check the logs. Also cross check with Rest Client if your server is returning back with proper response or not for provided input along with parameters.
For HttpLoggingInterceptor, this is the following way to configure
public RestClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("BASEURL")
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
//APIInterface initiation
service = retrofit.create(APIInterface.class);
}
Below are the dependencies for the same
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
Go through this Sample tutorial of Retrofit, if you are following some other possibilities of initiating the rest client

How to close HTTP connection with okhttp?

How one is supposed to tell OkHttp that a connection to an HTTP server should be closed after a request has finished?
Suppose I use the following code to make a request to google.com
OkHttpClient cl = new OkHttpClient();
Request req = new Request.Builder()
.url("http://www.google.com")
.header("Connection", "close")
.get()
.build();
Response res = cl.newCall(req).execute();
System.out.println("Response message: " + res.message());
System.out.println("Reponse body: " + res.body().string());
by using "Connection: close" I'm able to get the remote server to send a FIN (if I don't, the remote server just hangs awaiting for new requests). Still, on the client side no FIN is produced, and the connection stays half opened apparently for an indefinitive amount of time.
Ugh, it's a regression in OkHttp 2.0.0-RC1. Fix is out for review.

Categories