I am thinking of how to make AJAX take use of servlet 3 async response. in the request-response synchronize processing model, when the response comes back, the callback of XmlHttpRequest can get the response text; but how about the response is processed in another thread, and returns some message, what will the XHR get when the request ends? can it still get the response body? I tried a simple codes to test it, it seems failed to get the response;
I can understand this, when the AJAX request return, there is nothing in the response, it will be delayed in another server thread, so the callback get nothing.
But I wonder is there any way to let AJAX get the correct response?
I am afraid I made a mistake before, that I forget to call asycContext.complete() after the async processing is done. after complete() is called, ajax get the response. However, if the processing lasts longer than the timeout setting, just like following, a exception saying illegal state of the asynccontext will arise, and client get nothing:
final AsyncContext ac = request.startAsync();
ac.setTimeout(1000);
Executors.newSingleThreadExecutor().execute(new Runnable(){
#Override
public void run() {
PrintWriter pw;
try {
Thread.sleep(2000);
pw = ac.getResponse().getWriter();
pw.write("Hello, World!");
ac.complete();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
So I guess the keys here are: 1. call complete after processing is done; 2. set a appropriate timeout;
Related
I am writing a personal web server.
When I send the GET request to the system, the system responds well.
But when I send the HEAD request, I get the following problems:
sun.net.httpserver.ExchangeImpl sendResponseHeaders
WARNING: sendResponseHeaders: being invoked with a content length for a HEAD
request java.io.IOException: response headers not sent yet at
jdk.httpserver/sun.net.httpserver.PlaceholderOutputStream.checkWrap(ExchangeImpl.java:448)
at
jdk.httpserver/sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:458)
at
ir.utux.service.HandleHttpResponse.writeResponse(HandleHttpResponse.java:32)
This is the code I wrote to manage the response, to simplify HEAD and GET together.
public void writeResponse(SettingModal settingModal) {
try {
switch (requestHeader.getMethod()) {
case HEAD,GET -> {
response.getResponseHeaders().set("Server", "utux HttpServer");
response.getResponseHeaders().set("Connection", "close");
response.getResponseHeaders().set("Transfer-encoding", "chunked");
response.getResponseHeaders().set("Content-Type", ContentType.HTML);
response.sendResponseHeaders(HttpStatus.SC_OK, "".length());
response.getResponseBody().write("".getBytes(StandardCharsets.UTF_8));
response.getResponseBody().flush();
response.getResponseBody().close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
I found the problem
BODY should not be sent in response to HEAD requests.
I want to use Java Http Client: java.net.http.HttpClient in asynchronous way.
When I receive http response (any http code) or timeout I want to execute some custom Java code. I struggle to complete the body of error handling.
CompletableFuture<HttpResponse<String>> response =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApplyAsync(body -> {
System.out.println("Request finished");
return body;
})
.exceptionallyAsync(exception -> {
// WHAT TO RETURN HERE ?
});
The method: exceptionallyAsync returns: CompletableFuture<T>, but I do not know how to complete this method.
Can you please help me to finish this method? I cannot find example in GitHub or Google.
You can use
.exceptionally(this::handlError)
If this is your response handler with a method like:
private Void handlError(Throwable ex) {
System.out.println(ex.toString());
return null;
}
I'm running a local instance of vertx. Router redirects my requests to a worker verticle which has the following handler:
protected Handler<Message<JsonObject>> handler1() {
return msg -> {
final RequestBody req = mapper.readValue(msg.body().encode(), RequestBody.class);
processRequest(req, msg);
}
}
The processRequest function accepts the request body, makes calls to two external services, aggregates the responses, and returns back to the client.
private processRequest(RequestBody req, Message<JsonObject> msg) {
CompletableFuture<String> service1Response = getService1ResponseAsync(req); // Make async call to service 1
String service2Response = getService2ResponseSync(req); // Make sync call to service 2
ResponseBody response = aggregateResult(service1Response.join(), service2Response); // Tag1
msg.reply(mapper.writeValueAsString(response));
}
private CompletableFuture<String> getService1ResponseAsync(RequestBody req) {
CompletableFuture<String> result = new CompletableFuture();
// Below handler call makes GET call to service 1 using apache HTTP client and returns the response
vertx.eventBus().request("serviceVerticleAddr1", mapper.writeValueAsString(req), new DeliveryOptions(), reply -> { // Tag2
if (reply.succeeded())
result.complete(reply.result().body().toString());
else
result.completeExceptionally(result.cause());
}
}
When I hit the above API, my request times out. The thread from worker pool assigned for the execution of my request gets blocked at Tag1 forever. On debugging a little further, I found that the reply handler for the call in Tag2 is not getting invoked.
The handlers in service verticle (serviceVerticleAddr1) [i.e. Tag2] returns proper response for other APIs making use of it, but for me it's getting blocked. Can someone please help me identify the cause? Is there some kind of deadlock being formed when the thread calling vertx.eventBus().request [Tag2] starts to wait for future completion at service1Response.join() [Tag1]?
I think is blocked because of that sender is not being notified by the consumer that message has been processed.
I would recommend you to check inside the handler block registered for the consumer of serviceVerticleAddr1 address and ensure that is replying (notifying) to the sender that the requested message has successfully handled (or not). Consumer might look like
vertx.eventBus().consumer("serviceVerticleAddr1", message -> {
try{
doSomething();
message.reply("done");
} catch(){
message.fail(0, "fails");
}
});
This way, sender's async handler would be notified that consumer could process requested message
I am trying to determine why a webserver response that initially throws an exception in processing; then returns a 200 OK client side. The details are as follows:
a request is sent to the webserver from the web application and if an error occurs an exception is caught and the relevant code &/or message is returned as follows:
public void dispatchRequest(HttpServletRequest req, HttpServletResponse
res)
{
if (method.equalsIgnoreCase("get")) {
doGet(req, res);
} else {
res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
return;
}
}
void doGet(HttpServletRequest request, HttpServletResponse response)
throws
IOException,
HTTPServerException {
handleGetClient(request, response);
}
#SuppressWarnings("unchecked")
private void handleGetClient(HttpServletRequest request,
HttpServletResponse
response)
throws IOException, HTTPServerException {
...
} catch (IOException e) {
logger("I/O Error during playback with parameters (additional
parameters logged) {0}: {1}",traceParams,e.toString());
logger(Level.FINER, "I/O Error during playback with parameters {0}:
{1}", parameters, e.getMessage());
logger(Level.FINER, "I/O Error during playback with parameters {0}:
{1}", parameters, e);
sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
...
}
protected void sendError(HttpServletResponse response, int errCode) {
response.setContentType("text/plain");
try {
response.sendError(errCode,"ERROR");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
The handleGetClient method handles the process and in the event of an error throws exceptions which are caught. The method then uses the sendError method to set the error code returned and when log debugging I could see that this was set to in this specific error (500). But once the call returns to the dispatchRequest method the httpservletResponse status is actually (200). I cannot see where this happening and why. Initially I thought I could just change the method to int to return the error code but I am limited to the changes I can make on this code.
Any ideas?
You could try one of the following:
response.resetBuffer ();
response.setStatus (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.flushBuffer ();
or if you have an error page matching the 500 code in your web.xml:
response.reset ();
response.setError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
You could also consider using an exception mapper, for any generic error, so instead of playing yourself with the error code, you could just throw an exception which would take care of the status code return.
i.e: throw new InternalServerException ("Your message");
Possibly a return statement might be in the catch block that eventually ends up in normal functioning of sending response. I am only stating a possibility. More you can do is check what is being returned after the request is processed successfully from server (either after exception handling or normal functioning).
Either your pasted code is missing a piece or the original code does not set the actual error on the response object. Somewhere is the sendError method there should be a line like:
response.setStatus(errCode);
Once you have started streaming response to the client, you can no longer change the response code, since it is returned in the first line of response and can never be changed afterwards. Same thing with response headers - once you've started streaming body content, you can't change them.
Now, servlet containers use buffering. This means that you can write some data to response and then change your mind (as #MehdiB. has indicated). But once you have overflown that buffer, the data is written to client (first status code, then headers, then body) and you can no longer change status at this point.
The probable solution here is to avoid writing body until you are sure there are no errors. If your body is long, but you can figure out its full lenght, you can add Content-Length header - in this case client will know if you don't deliver the response in whole without relying on status codes.
I remember in my practice adding servlet filters which will intercept HttpServletResponse to make sure that servlets behave nicely with regards to this constraint.
Currently in my pipeline I have a simple handler to reject connections when my server gets overloaded:
public class RequestFilter extends SimpleChannelHandler {
#Override
public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception {
requestLimiter(ctx, e);
super.channelConnected(ctx, e);
}
}
private void requestLimiter(final ChannelHandlerContext ctx, final ChannelStateEvent e) {
if(threshold < counter) {
ctx.getChannel().close();
}
}
When the counter exceeds the threshold, the channel is closed, that all seems to work fine.
Now I'd like to enhance this by first sending an HTTP 503 response prior to closing the channel. What i've tried so far is this method below, instead of closing the channel immediatly I try to write a response to the channel and then handle closing it with a channelfuture listener so it's closed when the write is complete. However whats happening is I get a ton of exceptions about "already sent a response, can't send more than 1" followed by stack overflow.
protected void sendResponse(Channel channel, HttpResponse response) {
if (channel.isConnected()) {
channel.write(response).addListener(ChannelFutureListener.CLOSE);
log.trace("response sent");
} else if (!channel.isConnected()) {
log.trace("attempted to send response, but the channel was closed");
} else {
log.trace("Not sure why this would happen");
}
}
Any thoughts or examples I could look at? thanks
Edit: stacktrace
java.lang.IllegalStateException: cannot send more responses than requests
at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
at org.jboss.netty.handler.timeout.WriteTimeoutHandler.writeRequested(WriteTimeoutHandler.java:152)
at org.jboss.netty.handler.stream.ChunkedWriteHandler.flush(ChunkedWriteHandler.java:262)
at org.jboss.netty.handler.stream.ChunkedWriteHandler.handleDownstream(ChunkedWriteHandler.java:119)
at org.jboss.netty.handler.execution.ExecutionHandler.handleDownstream(ExecutionHandler.java:165)
at org.jboss.netty.channel.Channels.write(Channels.java:605)
at org.jboss.netty.channel.Channels.write(Channels.java:572)
at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
at com.test.RequestFilter.sendResponse(RequestFilter.java:98)
I don't think it is sending multiple responses. I think that it's [trying] to send one, which is one more than the number of requests which is zero because the the event is being triggered by the connect channel event and the pipeline has yet to see any http requests.
I would change your code to not do this on connect, but rather, trigger the 503 response on the first request. If the channel is then closed, adios client, but if the client's first request sneaks in under the threshold, then remove the bouncer from the pipeline (assuming that once a client is in, they're in for good).
Make sense ?