I'm connecting to URLs with Java (HttpURLConnection).
I have noticed that in some cases, the response code is 3xx but the 'Location' header is empty.
How does a client browser know where to redirect after receiving this kind of HTTP response?
Thanks
Not all 3xx replies can redirect automatically.
300 provides multiple URLs in the response body, not in the Location header. The client/user has to decide which one to retrieve next.
301, 302, 303, and 307 provide a Location only if the next URL is known. Otherwise, the client/user has to decide what to do next.
304 is not a redirect. It is a response to a conditional GET, where the requested content has not changed since the requested criteria was last satisfied.
305 always provides a Location to the required proxy to connect to.
306 is not used anymore.
If you look at the HTTP spec on some of the 3xx status codes, some of them only SHOULD provide a Location header.
How does a client browser know where to redirect after receiving this
kind of HTTP response?
It doesn't. It's up to the client to handle what to do in that case.
Location headers redirect the user agent to retrieve another URI reference when used with 3xx redirection status codes, except for 304 Not Modified. Both absolute URIs and relative references can be provided, including empty references which refer to the current resource (see the URI specification for further information).
Still, only Firefox and old Edge accept empty Location headers; the new Edge and Chrome don't. Although HTTP redirects are only meant to redirect to different resources or URIs (see RFC 7231 section 6.4), all browsers implement non-empty Location headers that explicitly refer to the same page.
Whenever the user agent receives a redirection status code but no Location header (or an invalid Location header or in the case of Chrome, an empty Location header) it will not redirect but show the response body. This also applies when the user disables automatic redirection. Therefore the response body should also include the respective link.
Empty Location headers may obviously introduce redirect loops. Nevertheless, the status code 303 See Other could be used in conjunction with an empty Location header to implement the Post/Redirect/Get idiom using the very same URI. This idiom prevents users from resubmitting the same form using POST when reloading the page because 303 See Other requires the user agent to use the GET request method when following the new Location. 301 Moved Permanently and 302 Found may also change the request method to GET (but they also may not); 307 Temporary Redirect and 308 Permanent Redirect never change the request method.
While this use case seems kind of elegant I would not recommend to implement it because browser support diverges.
Related
I need to log URLs that are linking to my site in a Java Servlet.
It's available in the HTTP referer header. You can get it in a servlet as follows:
String referrer = request.getHeader("referer"); // Yes, with the legendary misspelling.
You, however, need to realize that this is a client-controlled value and can thus be spoofed to something entirely different or even removed. Thus, whatever value it returns, you should not use it for any critical business processes in the backend, but only for presentation control (e.g. hiding/showing/changing certain pure layout parts) and/or statistics.
For the interested, background about the misspelling can be found in Wikipedia.
Actually it's:
request.getHeader("Referer"),
or even better, and to be 100% sure,
request.getHeader(HttpHeaders.REFERER),
where HttpHeaders is com.google.common.net.HttpHeaders
The URLs are passed in the request: request.getRequestURL().
If you mean other sites that are linking to you? You want to capture the HTTP Referrer, which you can do by calling:
request.getHeader("referer");
As all have mentioned it is
request.getHeader("referer");
I would like to add some more details about security aspect of referer header in contrast with accepted answer. In Open Web Application Security Project(OWASP) cheat sheets, under Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet it mentions about importance of referer header.
More importantly for this recommended Same Origin check, a number of HTTP request headers can't be set by JavaScript because they are on the 'forbidden' headers list. Only the browsers themselves can set values for these headers, making them more trustworthy because not even an XSS vulnerability can be used to modify them.
The Source Origin check recommended here relies on three of these
protected headers: Origin, Referer, and Host, making it a pretty
strong CSRF defense all on its own.
You can refer Forbidden header list here. User agent(ie:browser) has the full control over these headers not the user.
Title already says it, when i do an web-api call on my jetty servlet endpoint.
Is it enough to catch the
request.getMethod().equals("OPTIONS")
flag?
Or are there other possibilities to determine if the current request is of type preflight?
Preflight requests are sent as OPTIONS so you can check for that, yes.
But depending on what you are doing (I'm assuming some CORS filter?) you might also want to check if the following headers are present on the preflight: Access-Control-Request-Method (what HTTP method will be used when the actual request is made) and Access-Control-Request-Headers (what HTTP headers will be used when the actual request is made).
even though i have appended my service response with following provided CORS Headers :
resp.setContentType("application/json");
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Credentials", "true");
resp.addHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
resp.addHeader("Access-Control-Allow-Headers", "Origin,accept,content-type");
resp.flushBuffer();
i am still getting below error in the console while trying to access some of the POST web methods in the service through my AngularJS frontend.
XMLHttpRequest cannot load http://192.***.*.***:8080/abc/def/search/vehicleManufacturer. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.***.*.***:8085' is therefore not allowed access.
However within the same class, some POST methods without any payloads are responding perfectly. Any suggestions ?
EDIT--------->
Below is my AngularJS client screen code for calling the web method:-
getVehicleModel : function(searchData,$scope){
$http({
method:'POST',
url:'http://192.169.*.***:8085/abc/def/search/vehicleModel',
dataType:'jsonp',
data:searchData
}).
success(function(data){
console.log("vehicle model")
$scope.vehicleModel = data.Response;
});
},
I think the problem here is Preflighted Requests in CORS.
From the Mozilla docs,
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than
application/x-www-form-urlencoded,
multipart/form-data
text/plain
e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
As explained above, even though you're making a simple POST request, the Content-Type in your request is application/json which is different from the 3 types mentioned above, so it's considered as a Preflight request and an OPTIONS request is fired before your actual POST request.
You can solve this by implementing doOptions in your servlet, just add the headers there and it will work :)
The preflight (OPTIONS) is occurring due to the fact that you are sending a cross-origin ajax request AND specifying an Authorization header with this GET request.
Also (this is not causing an issue) I would suggest removing the contentType option. This doesn't make sense in the context of a GET request. A GET request should not have any content. All data should be included in the query string or, possibly, headers.
The Authorization header will not be sent with the OPTIONS. You must acknowledge it server-side, and then the browser will send the underlying GET. Read more about CORS at https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS .
According to W3 Meta refresh is discouraged and they recommend Server side redirection. So my question is how am I supposed to do that in HTTPServer?
The API does not provide any redirection method here:
http://docs.oracle.com/javase/7/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html
If this "redirection" is not that abstract thing, then what would be the idea behind that?
What the W3 means when is says this, is that instead of your HTTP server returning a status-code of 200 (OK) with a response body that has HTML with a
<META HTTP-EQUIV=REFRESH CONTENT="1; URL=http://www.example.org/bar">
you have your HTTP server return a suitable 3xx (redirect) status code, with a Location header that gives the location to be redirected to.
The most suitable status code would be 302 (Found) if you still expect people to use the original URL as an alias for the URL being redirected too (the HTTP standard says "the client SHOULD continue to use the Request-URI for future requests").
If you want users to stop using the original URL the most suitable status code would be 301 (Moved Permanently).
I want to check if a URL's mimetype is not a webpage. Can I do this in Java? I want to check if the file is a rar or mp3 or mp4 or mpeg or whatever, just not a webpage.
You can issue an HTTP HEAD request and check for Content-Type response headers. You can use the HttpURLConnection.setRequestMethod("HEAD") before you issue the request. Then issue the request with URLConnection.connect() and then use URLConnection.getContentType() which reads the HTTP headers.
The bonus of using a HEAD request is that the actual resource is never transmitted/generated. You can also use a GET request and inspect the resulting stream using URLConnection.guessContentTypeFromStream() which will inspect the actual bytes and try to guess what the stream represents. I think that it looks for magic numbers or other patterns in the stream.
There's nothing inherent in a URL which will tell you what you will receive when you request it. You have to actually request the resource, and then inspect the content-type header. At that point, it's still not clear what you should do - some content types will (almost) always be handled by the browser, e.g. text/html. Some types should be handled by a browser, e.g. application/xhtml+xml. Some types may be handled by the browser, e.g. application/pdf.
Which, if any, of these you consider to be "webpage" is still not clear - you'll need to decide for yourself.
You can inspect the content-type header once you're requested the resource, using, for example, the HttpURLConnection class.
content-type:text/html represents webpage.