okhttp3 websocket dynamic header - java

I'm trying to create a websocket and dynamically recalculate its header in every message sent. Is it possible?
I was trying to use an interceptor but is only called once.
public void run() {
// only open a websocket if there aren't websockets already open
if (this.webSocket == null || !this.openingWS) {
this.openingWS = true;
wsBuilder = new OkHttpClient.Builder();
OkHttpClient client = wsBuilder.addInterceptor(this)
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("wss://...")
.build();
client.newWebSocket(request, this);
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
client.dispatcher().executorService().shutdown();
}
}
#Override public void onOpen(WebSocket webSocket, Response response) {
this.openingWS = false; // already open
this.webSocket = webSocket; // storing websocket for future usages
if (listener != null) listener.onWSOpen();
}
public void sendCommand(String cmd) {
System.out.println("SEND " + cmd);
if (webSocket != null) webSocket.send(cmd);
}
This same class is implementing the interceptor
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!isSpecial()) return chain.proceed(originalRequest);
okhttp3.Request.Builder builder = originalRequest.newBuilder()
.addHeader("text", "...")
.addHeader("dfds", "...");
Request compressedRequest = builder.build();
return chain.proceed(compressedRequest);
}
The authentication code sent in the header will change every X seconds/minutes.
If it's not possible to change dynamically the header, what is the best way to approach this kind of connection?
Thank you for your help.

I think the headers are send only first time when you request the connection, later is depends on frames between the client and the server.
So if you want to inform the server that you had changed the header then send message with your new header. Or you can close the connection and start a new one with the new header.

Related

How can remove or reset OkHttp3 connection from pool

My project use spring-cloud-starter-openfeign.(version 2.1.0)
open feign is using feign-okhttp as http client and I know it uses ok-http3 internally.
I want the OkHttp3 connection to be reset or delete from connection-pool when http request gets a 500 error response.
To do that I need to detect a 500 error, so I figured I could use a network interceptor.
However, the RealConnection object I get with chain.getConnection() doesn't seem to have any methods to reset or close the connection.
How can I implement what I have in mind?
#Bean
public OkHttpClient client() {
return new OkHttpClient(
new okhttp3.OkHttpClient.Builder()
.addNetworkInterceptor(new ResetConnection())
.build());
}
public static class ResetConnection implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
okhttp3.Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() >= 500 && response.code() < 600) {
// ???????????
}
return response;
}
}

OKHTTP GET and POST request return empty body message

I want to a upload file on my server and I've decided to try OKHTTP instead of my current method which is based on android own HTTP implementation and AsyncTask.
Anyway, I used OKHTTP and its asynchronous implementation (from its own recipes) but it returns an empty message (the request code is ok, the message is empty) in both GET and POST methods.
Did I implement it wrong or is there anything else remained that I did not considered? In the meantime, I couldn't find a similar case except this which says used AsyncTask.
Here's the code:
Request request;
Response response;
private final OkHttpClient client = new OkHttpClient();
private static final String postman_url = "https://postman-echo.com/get?foo1=bar1&foo2=bar2";
String message_body;
public void Get_Synchronous() throws IOException
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
response = call.execute();
message_body = response.toString();
//assertThat(response.code(), equalTo(200));
}
public void Get_Asynchronous()
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException
{
message_body = response.toString();
}
public void onFailure(Call call, IOException e)
{
}
});
}
Edit:
I catch the log on response:
onResponse: Response{protocol=h2, code=200, message=, url=https://postman-echo.com/get?foo1=bar1&foo2=bar2}
OK, for anyone who wants to receive an string from a call, response and response.messgage() don't provide that. To catch the response from your provider, you just need to call response.body().string() inside onResponse which returns the message inside your request.
But after all, Retrofit is a better choice if you want to receive a JSON file using
.addConverterFactory(GsonConverterFactory.create(gson)).
If you still want to receive an string just use .addConverterFactory(ScalarsConverterFactory.create()) as explained here.

How to handle errors when executing an async request with HTTP Java Client?

I'm using the new Java 11 HTTP Client. I have a request like:
httpClient.sendAsync(request, discarding())
How to add a handler for HTTP errors? I need to log errors but I want to keep the request async.
Currently, HTTP errors like 400 or 500, are silent.
I'd like to log the status code and response body on those occasions.
I suppose the CompletableFuture is just like a promise, so the reply is not available there yet.
You just need to register a dependent action with the completable future returned by HttpClient::sendAsync
Here is an example:
public static void main(String[] argv) {
var client = HttpClient.newBuilder().build();
var request = HttpRequest.newBuilder()
.uri(URI.create("http://www.example.com/"))
.build();
var cf = client.sendAsync(request, HttpResponse.BodyHandlers.discarding())
.thenApplyAsync((resp) -> {
int status = resp.statusCode();
if (status != 200) {
System.err.println("Error: " + resp.statusCode());
} else {
System.out.println("Success: " + resp.statusCode());
}
return resp;
});
cf.join(); // prevents main() from exiting too early
}

How can I send more then one response to client from a Httpserver base on Netty

How can I send more then one response to client from a Httpserver base on Netty?
I am trying to make a httpserver by netty and it works.
Now I have a question,can I send more then one response to client from a Httpserver ?
For example, the client requests the server from a web browser, and server responses "hello" then responses "bye" several seconds later.
I add three handles:
sc.pipeline().addLast(new HttpResponseEncoder());
sc.pipeline().addLast(new HttpRequestDecoder());
sc.pipeline().addLast(new HttpChannelHandler());
In the HttpChannelHandler ,I tried to response twice,but failed
public class HttpChannelHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
//the content response to the client
String resp_content = "hello";
request = (HttpRequest) msg;
boolean keepaLive = HttpHeaders.isKeepAlive(request);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
OK, Unpooled.copiedBuffer(resp_content.getBytes("UTF-8")));
response.headers().set(CONTENT_TYPE, "text/html;charset=UTF-8");
response.headers().set(CONTENT_LENGTH,
response.content().readableBytes());
if (keepaLive) {
response.headers().set(CONNECTION, KEEP_ALIVE);
//first response
ctx.writeAndFlush(response);
content = "test";
response.headers().set(CONTENT_TYPE, "text/html;charset=UTF-8");
response.headers().set(CONTENT_LENGTH,
response.content().readableBytes());
//second response,but failed
// exception io.netty.util.IllegalReferenceCountException: refCnt: 0
response.content().writeBytes(resp_content.getBytes());
ctx.writeAndFlush(response);
}
}
}
}
No thats not possible... HTTP is a "request/response" style protocol. Once you sent a response to the client you can only send another one when you receive another request.

Retrofit get a parameter from a redirect URL

I am using Retrofit.
I have an endpoint that redirects to another endpoint. The latter (the endpoint that I end up at) has a parameter in its URL that I need. What is the best way to get the value of this parameter?
I cannot even figure out how to get the URL that I am redirected to, using Retrofit.
OkHttp's Response will give you the wire-level request (https://square.github.io/okhttp/3.x/okhttp/okhttp3/Response.html#request--). This will be the Request that initiated the Response from the redirect. The Request will give you its HttpUrl, and HttpUrl can give you its parameters' keys and values, paths, etc.
With Retrofit 2, simply use retrofit2.Response.raw() to get the okhttp3.Response and follow the above.
I am using retrofit. And I can get the redirect url following this way :
private boolean handleRedirectUrl(RetrofitError cause) {
if (cause != null && cause.getResponse() != null) {
List<Header> headers = cause.getResponse().getHeaders();
for (Header header : headers) {
//KEY_HEADER_REDIRECT_LOCATION = "Location"
if (KEY_HEADER_REDIRECT_LOCATION.equals(header.getName())) {
String redirectUrl = header.getValue();
return true;
}
}
}
return false;
}
Hope it could help someone.
Solution for this would be to use an interceptor e.g.
private Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response response = chain.proceed(chain.request());
locationHistory.add(response.header("Location"));
return response;
}
};
Add the interceptor to your HttpClient and add that to Retrofit(using 2.0 for this example)
public void request(String url) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.followRedirects(true);
client.addNetworkInterceptor(interceptor);
OkHttpClient httpClient = client.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
Now you have full access the the entire redirect history.

Categories