HTTP statuscode to retry same request - http

Is there an HTTP status code to instruct a client to perform the same request again?
I am facing a situation where the server has to "wait" for a lock to disappear when processing a request. But by the time the lock disappears, the requests might be close to its timeout limit. So instead, once the lock clears, I would like to instruct the client to just perform the same request again.
The best I an come up with is a HTTP 307 to the same location, but I'm worried that some browsers might not buy into this (redirect loop detection).

The correct response, when a server is unable to handle a request, is 503 Service Unavailable. When the condition is temporary, as it is in your case, you can set the Retry-After header to let the client know how long it should wait before trying again.
However, this will not force the browser to perform the request again - this is something you would need to handle yourself in javascript. For example, here is how you might perform a retrying ajax POST request in jquery:
function postData() {
$.ajax({
type: 'POST',
url: '503.php',
success: function() {
/*
Do whatever you need to do here when successful.
*/
},
statusCode: {
503: function(jqXHR) {
var retryAfter = jqXHR.getResponseHeader('Retry-After');
retryAfter = parseInt(retryAfter, 10);
if (!retryAfter) retryAfter = 5;
setTimeout(postData, retryAfter * 1000);
}
}
});
}
Note that the above code only supports a Retry-After header where the retry delay is specified in seconds. If you want to support dates that would require a little more work. In production code I would also recommend a counter of some sort to make sure you don't keep retrying forever.
As for using a 307 status code to repeat the request automatically, I don't think that is a good idea. Even if you add a retry parameter to get around the browser loop detection (which feels like a horrible hack), it's still not going to work on a POST request. From RFC2616:
If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user.
While some browsers are known to ignore this requirement, it's definitely not correct, and isn't something you would want to rely on.
And if you're not using a POST request, you almost certainly should be. Remember that a GET request should not have any side effects, and by default the response will be cached. From the description of your problem, it sounds very much like your request is likely to be doing something that has side-effects.

Use the 307 redirect, but add a retry counter:
http://path/to/server?retry=3
This will make the URL different on each retry, preventing loop detection. And the server could check for retry hitting a limit, and abort with an error when that happens, so the user doesn't wait forever.

Related

Fetch API, custom request headers, CORS, and cross-origin redirects

I need to make an HTTP GET request with custom request headers in-browser and process the result as it streams in. The Fetch API is ideal for this:
fetch('https://example.com/resource', {
method: 'GET',
headers: {
'X-Brad-Test': 'true'
},
cache: 'no-store',
mode: 'cors'
}).then((res) => {
const reader = res.body.getReader();
// etc.
});
This works quite well. Since there are custom headers, the browser pre-flights the request with an OPTIONS request to /resource. I have configured my server to respond with a 204 No Content and the following headers:
Access-Control-Allow-Headers: X-Requested-With, Range, If-Range, X-Brad-Test
Access-Control-Allow-Origin: *
The browser is happy with this, then makes a GET request, the server returns a 200 OK with the data, and the browser allows me to access the response headers and body.
The problem comes in when there is a redirect. The OPTIONS request succeeds with the 204 No Content and the same headers as before. The browser makes the correct GET request, and on the server I send a 302 with a Location: header. Chrome throws the following error:
Fetch API cannot load https://example.com/resource. Redirect from 'https://example.com/resource' to 'http://some-other-origin/resource' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.
This was unexpected, and seems nonsensical to me. I expected the browser to follow the redirect, and do another pre-flight request for this new location, but it didn't do that.
Stranger still is that I can sort of hack around this client-side. I can make an HTTP request without my custom header, figure out where I ended up after redirects by looking at the Response object, then make a second request at the new target with my custom headers. This doesn't work in all cases of course, and I'd rather not rely on this hack. I'd rather find a proper way.
Two Questions:
What is the proper way to allow the client to follow redirects? Is there some sort of Access-Control-* header I can use?
Why does this restriction exist? What security issue is prevented by not following and running pre-flight on the followed URL?
Supporting redirects to requests that require a preflight is very recent change to Fetch (which defines CORS).
https://github.com/whatwg/fetch/commit/0d9a4db8bc02251cc9e391543bb3c1322fb882f2
I believe some implementations have started adjusting their implementations, but this will take some time to reach everyone.

What happens when there is network request time out during AJAX request to ASP.NET MVC Action

As stated in the title, i would like to know what happens when an AJAX request is sent to a controller action and, during this time, a network timeout happens for a few ms before the request is completed.
Reply from <Server IP>: bytes=32 time=31ms TTL=122
Request timed out
Reply from <Server IP>: bytes=32 time=28ms TTL=122
Considering the timeout happens only for a couple of ms, what effects would this have on my AJAX request?
This is in continuation to a problem we are facing in our application as explained in this SO question and i would like to know if they are somehow related.
I have googled for similar issues but couldn't find anything useful.
Edit: Apart from the impact on AJAX, would it affect the action method's behavior (server)?
Regardless of whether the timeout happens only for a couple of ms or more, the request will fail. The success callback function of your AJAX request will not be executed and the request will end with the execution of complete callback function. By default, all AJAX requests will have a timeout of 0ms (unlimited), but it will hit the default timeout of the browser.
When an AJAX request times out, the error callback function will be invoked. The second argument of this function is a string describing the type of error and in this case, it will have the value timeout. You can handle request timeouts by handling this callback function and by optionally specifying a timeout value (if not specified, works on the default value) in the AJAX request body:
$.ajax({
...
timeout: 5000, //specify the timeout value in milliseconds
error: function(jqXHR, textStatus, errorThrown) {
if(textStatus==="timeout") {
//code to execute when timeout occurs
}
}
});​
Additionally, you can also check if the request has timed out in the complete callback function (in a similar way as shown above) by checking the second argument which is a string and it will have the value timeout if the request was timed out.
Also, note this:
The timeout period starts at the point the $.ajax call is made; if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent.
Request timeouts are usually either left at their default or set as a global default using $.ajaxSetup() rather than being overridden for specific requests with the timeout option.
I would suggest you to use an alternative HTTP/s traffic monitoring tool like fiddler to find the mystery behind the second request.
More info: jQuery ajax documentation
The request will "fail", meaning it will enter the onError state of your AJAX request. The status code will then be 0, since there is no response from the server to determine the real status code (eg. 200 OK or 500 Internal Server Error).
In case of time-out your success callback wont execute so you have to write an error callback at the client side to handle such issues.
You have to raise an exception from server side in case of time-out so that it will get back to the client as an error that is the one way you can handle the time-out.

Is it possible to send partial reply to the client and later send complete response?

I use a payment gateway which uses the relay response url of my web application to return the transaction response or receipt information. The problem is that it uses a timeout i.e if it is 10 seconds since it made the request to the relay response url and if the relay response url didn't respond within that time then it will timeout. The problem i am trying to avoid or minimize is for the url to respond within the timeout period. One thing i have noticed is that this method that relay response url points to over the time has gotten bulkier and this may amount to the occasional timeouts that is happening. One solution i think could be to render partial response quickly like "Please wait...". If the payment gateway receives something from the relay response url then it shouldn't timeout. After that when the heavy processing is complete then the relay method sending full response which will be receipt in most cases. Is there a way to achieve this? I appreciate any help! The framework i am using for my application is grails 2.0.
I thought something like this would work but i was wrong.
def receiptFinal(){
...
}
def receipt(){
render "Please wait..."
redirect(controller: 'payment', action: 'receiptFinal')
}
Yes,it is quite possible I guess. Your payment gateway has to make two requests. One is to confirm whether validation/payment is all right or not. And, Second request to the client would be final response (like receipt etc)
It would totally depend on payment gateway.
--
Jitendra

Correct API approach for long processing time

I am making an HTTP web API that's mainly fed by a database. Simplified, the db contains userobjects.
These objects have a last_online (when the user was online) and last_checked (the last time I checked the userobject).
Checking the userobject can take from 3 to 30 seconds. When the last_checked time is less than 10 minutes then everything's okay; API call returns 200 and the userobject.
But I want to reprocess the userobject when the data is staler than 10 minutes. Obviously I can not have my API return sit there and wait.
What is the right approach to HTTP APIs that (sometimes) need to return data from long running processes?
My first proposal would be to have the server update the user object every X minutes as a background process. I don't see any reason to place the burden of keeping server data up-to-date on the client. Responses to the GET call would include an Expires header. The client could then cache the response for a fixed amount of time, saving you server hits until the data gets refreshed.
If you must make the refresh be client-driven, you want your GET to return a 202 Accepted, which indicates a valid request that the API is working on but has not completed. The entity that gets returned from your GET request should provide a timestamp for when the API should check back to get the updated data. Once the data has been refreshed, the GET will return a 200 Ok with the refreshed data. This is the approach I recommend.
GET /userObject
<- 202 Accepted
{ "checkAt": <timestamp> }
GET /userObject
<- 200 OK
{ "userName": "Bob", ... }
You could also consider using the Retry-After header in your response, but that's only appropriate for 503 Service Unavailable or any of the various 3xx (Redirection) responses. You definitely aren't describing a 503, and it doesn't sound like redirection is correct either.
If you do want to go the redirection route, you'd return a 302 Found, specifying the temporary URI in the Location header and the delay time in the Retry-After header.
A fourth approach would be to use a POST and the Post-Redirect-Get pattern. You could POST to your userObject URI and have it return the 302 Found with the Retry-After header.
I really don't think that options three or four buy you anything that the second option doesn't, and I think it's the most clear. Three implies that your resource currently lives in a different location when it doesn't. Four transforms what is fundamentally a GET request (give me the user object) into a POST (refresh the user object, but only if you need to).
If you do decide to follow #JonSkeet's suggestion, you probably want a separate resource, something like /userObjects and /userObjectRequests. The client would always POST to /userObjectRequests. If the userObject was valid on the back end, that POST would return a 302 to /userObjects. If it wasn't valid, the POST would return an entity with an id and an estimated completion time. The client could call GET on /userObjectRequests/{id}, and they'd either get a 302 to the userObject (if it's ready) or a 200 with the id and a new estimated completion time.
One fairly "old-school" way of handling this would be to return a continuation token - basically a job ID saying, "Check this periodically; sooner or later it'll come back with a result." Given that even 30 seconds is quite a long time, you might want to give back a continuation token even in the normal "checking" situation.
More modern alternatives would be web sockets or a hanging get... it really depends on what your client use cases are.

HTTP status code for "success with errors"?

I've poked around a bit, but I don't see an HTTP status code for when a request's succeeds, but there is an error after the "point of no return".
e.g., Say you process a request, its committed to the database, but while returning the result you run of memory, or encounter a NPE, or what have you. It would have been a 200 response, but now, internally, you aren't able to return the proper, well-formed response.
202 Accepted doesn't seem to fit since we've already processed the request.
What status code means "Success, but errors"? Does one even exist?
HTTP doesn't have such a status code, but there is a best practice that allows you to handle such situations - redirect the user after a POST operation.
Here is a break down -
A POST request tries to modify data on the server
If the server fails, it sends a 500 error to indicate failure
If the server succeeds, it sends a 302 redirect response
The browser then sends a fresh GET request to the server
If this fails, you get a 500 error, otherwise you get a 200
So, your use case of 'Saved data but can't retrieve it immediately' translates to a 302 redirect for the initial POST, followed by a 500 for the subsequent GET.
This approach has other advantages - you get rid of the annoying 'Are you sure you want to resubmit the data?' message. Also keeps your back/forward/refresh buttons usable.
If the server is aware that it has encountered a problem, it should normally return a 5xx error. The most generic one is the 500 Server Error, which the RFC 2616 defines as follows:
500 Internal Server Error
The server encountered an unexpected condition which prevented it
from fulfilling the request.
Then it's the client's responsibility to reattempt the request. If the previous request was partially committed, it's the server's (or the database's) responsibility to roll that back, or to handle the duplicate transaction appropriately.
I agree with #Daniel that the proper response is an HTTP 500 (server error). The web application has to be written to roll back the transaction when there is an error, not leave things half-finished.
One thing you can leverage in your web application is "idempotency". This is the property of a function (or operation) that you can repeat it as many times as you like with the same result. For instance if a read fails, the client can simply retry it until it succeeds. If a deletion appears to fail, the client can again retry and the server will treat the request as valid whether or not the resource being deleted is already gone. And if an update appears to fail, the client can retry that until it gets a successful return from the server. The REST approach to architecting web services makes heavy use of idempotency to make operations robust in the face of error.

Resources