Java web server compare if-modified-since with last modified - java

I am trying to make a Java web server that will compare the if-modified-since with the file's last modified.
However, I don't know how to get the if-modified-since header from the client and do the comparison.

I wouldn't jump right into trying to handle a particular header. If you're writing a web server from scratch then you should write a generic HTTP parser that can handle every part of an HTTP request:
Request line
Request method (GET, POST, etc.)
URL
HTTP version
Zero or more headers of the form Name: Value
A blank line
Message body
You could, for instance, build up a class like:
class HttpRequest {
String method;
URL url;
String httpVersion;
Map<String, String> headers;
byte[] body;
}
Since header names are case insensitive I'd suggest using map with String.CASE_INSENSITIVE_ORDER.
Once you can parse all headers than looking for a particular header will be a simple task. If you had the class above it'd be as easy as looking up headers.get("If-Modified-Since").

Related

Java Servlets: Differentiate between GET and POST

Is there a reliable way to separately extract GET and POST parameters using a HttpServletRequest?
That is, differentiate parameters that were sent in the query string (GET) to parameters that were sent in the request body (POST), assuming Content-Type: application/x-www-form-urlencoded.
Example
POST /path HTTP/1.1
Host: test.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 42
first_name=posted_foo&last_name=posted_bar
I would like to end up with two variables, one containing the values from the URL and one containing the values from the request body:
get = {"first_name": "foo", "last_name": "bar"}
post = {"first_name": "posted_foo", "last_name": "posted_bar"}
The only methods I seem to be able to extract these parameters are the getParameter* methods.
HttpServletRequest.getParameter: Returns a single string and tends to be the value provided in the URL (GET).
HttpServletRequest.getParameterValues: Returns an array of strings containing all of the values provided in the query string and request body. Those passed via the query string tend to appear first. However, if only one value is present in the returns array of strings, it cannot be reliably determined whether the value came from the query string or the request body.
To illustrate, using PHP these values are provided through the $_GET and $_POST superglobals.
The query string is trivial to parse, thus gives you the URI query param names, while the getParameterNames() gives you the whole set.
Split the query string by '&', then subsplit each token by '='.
For each key and value, perform the URLDecoder.decode(). That's all.
Toss all such keys in a set. If the param is in the uri query set it a good chance it's only there. If you must find if it is also in the post, actually, post form-encoded data is also coded like that, but that post is consumed so it's too late. Besides, the post could also be a multipart encoding which is non-trivial decode.
In the end, it's odd that you need this distinction. Can you explain for what purpose you seek this distinction?

HTTP: what is the correct way to send "retry/redirect" response

I need to force the client to retry its request (meaning to send the same request one more time). What I'm thinking of is a response with status-code 307 and header Location: <original-url> (that's good enough for now, unless there's a better way).
My question is, from HTTP specification point of view, what is the correct value for Location in this specific context. Or more specifically in Java having request of type HttpServletRequest, which one should I use: getRequestURI (Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request) or getRequestURL (Reconstructs the URL the client used to make the request containing protocol, server name, port number, and server path, but it does not include query string parameters).
Any other suggestion/comment is appreciated.
getRequestURL() returns complete URL used by the client where as getRequestURI() returns just the basic path resides in server.
i am using this technique to redirect with a response status this is my code this is useful:-
httpServletResponse.reset();
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.setHeader("SERVER-RESPONSE", "bad request");
return;
and also you can set headers in response.
I believe a redirect is the wrong status code in the first place.
Isn't this what 503 is for? (https://www.greenbytes.de/tech/webdav/rfc7231.html#status.503)

Watson Content Analytics: How to make web crawler plug-in to get data, sending POST request?

I have WCA 3.5.0 server and I need to get documents from the site, using web crawler.
The problem is in the fact that I have to send a POST request to the site to get some data (initialy my site consists only of a form with some fields and submit button to send the request to the server). So, my POST request body should be something like that:
{"DateFrom":"2000-01-01T00:00:00","DateTo":"2030-01-01T23:59:59","Bundles":[{"Name":"the test name that i passed","Type":-1}],"Company":[],"Transaction":[],"Text":""}
I was thinking about making a a prefetch plugin for a web crawler.
But from the documentation I've found it looks like it is hardly possible:
"The first element ([0]) in the argument array that is passed to your
plug-in is an object of type PrefetchPluginArg1, which is an interface
that extends the interface PrefetchPluginArg. This is the only
argument and the only argument type that is passed to the prefetch
plug-in."
PrefetchPluginArg1 class has only getHTTPHeader(), setHTTPHeader(), getURL(), setURL(), doFetch(), setFetch(),
where:
The getHTTPHeader method returns a String that contains the all of
the content of the HTTP request header that the crawler sends so that
the crawler can download the document.
The getURL method returns the URL (in String form) of a document that
the crawler downloads. You can use this URL to decide if the document
requires additional information in the request header, such as a
cookie.
And it looks like there is no way to change request body.
So, is it realy possible to control POST request body, but not only header, and if it is so, can you please, share some information about the ways of solving this task?

Spring 4 #RequestMapping -- consumes vs headers?

I'm learning how to build RESTful web services using Spring 4, and one thing I'm not clear on is in #RequestMapping. I've seen examples where one uses headers = "Accept=application/xml" and other examples using consumes (or produces) = "application/xml".
For instance, in my own #RestController class, I have this function...
// POST
#RequestMapping(method = RequestMethod.POST, headers = "Accept=application/xml")
public User create(#RequestBody User user) {
LOG.info("User = " + user.toString());
return userService.create(user);
}
What is the difference between using headers = "Accept=application/xml" vs. using consumes = "application/xml"? Or even using headers = "content-type=application/xml"?
Could someone explain the differences between headers and consumes/produces, and when each is used?
SHORT ANSWER
In the example you have above, using headers = "Accept=application/xml" or produces = "application/xml" will both respond to the client the same way i.e. send a response to the client with XML representation.
LONGER ANSWER
i. Headers
For RESTful web services, the client (e.g. your browser) sends a request (e.g. GET, POST, etc.) to a server, and the server will send a response back. This is an HTTP Transaction. Both the request and response have HTTP header fields ("headers"), which define the operating parameters of an HTTP transaction (I will refer to the headers for client request as "request headers", and these differ from headers from server response "response headers").
As part of the request your browser sends to server, there are different request headers and some examples include Accept, Connection, Content-Length etc. and each of these headers have their own function (see a full list of headers here: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields).
Using your code example, if a client does a POST request, Spring will check the request header(s) and if it finds a header Accept with a value of application/xml, it will map the request to the create method you have above (and in your case the server will return an XML response representation to the client).
Let me modify the headers element in the code you provided:
#RequestMapping(method = RequestMethod.POST, headers = "Connection=keep-alive")
public User create(#RequestBody User user) {
...
}
Notice the headers element now has a value of Connection=keep-alive. If a client does a POST request, Spring will check the request header(s) and if it finds a header Connection with a value of keep-alive, it will map that client request to the create method above.
ii. Produces and Consumes
If you used produces="application/xml" for the create method, this means a client request is only mapped to the create method if the client's Accept header matches application/xml. This essentially is the client saying, "Hey server, I prefer to accept your response in XML representation, so send your response to me in XML". Effectively, the produces="application/xml" is also the server saying, "Hey client, I can only produce responses for you in XML representation, so I will send you that format".
Link to Spring documentation reference.
If you used consumes="application/xml" for the create method, this means a client request is only mapped to the create method if the client's Content-Type header matches application/xml (the Content-Type request header describes the representation the client request is coming in). This essentially is the server saying, "Hey client, I can only consume requests in XML representation, so send that format to me".
SUMMARY
The headers element within the #RequestMapping annotation can take different request headers (Accept, Connection, Cache-Control etc.), but the produces element is only concerned with the Accept request header and the consumes element is only concerned with the Content-Type request header.
As the javadoc of HeadersRequestCondition (which handles the value provided in the headers attribute of a #RequestMapping annotation) states
Expressions passed to the constructor with header names 'Accept' or
'Content-Type' are ignored. See ConsumesRequestCondition and
ProducesRequestCondition for those.
So don't use those headers in headers. Use the produces and consumes attributes for Accept and Content-Type.
As to how to use them, the documentation gives examples: for consumes and for produces.

Return file or plain text

I have to create a rest web service where in user access data using url. Url has a query parameter named format which can be either text or file. If user chooses text as format then I have to return text data to browser or if user chooses file as format then return a file for user to download. How can I achieve this?
What I have tried so far (not working) :
#GET
#Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM})
#Path("/some_path")
public Response some_path (#Context HttpServletRequest request) {
String format = null;
if(request.getParameterValues("format") != null && request.getParameterValues("format")[0] != null) {
format = request.getParameterValues("format")[0].toString();
}
else {
format = "text";
}
File file = new File("/some/file/path.txt");
if(format.equals("text")) {
return Response.status(200).entity("sending some text").build();
} else {
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition","attachment; filename = result.txt").build();
}
}
With above code format=text works properly but format=file throws HTTP Status 406 null error.
Thanks In Advance
The HTTP status you get is
406 Not Acceptable
To quote Wikipedia:
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
So you client doesn't send a HTTP header of
Accept: text/plain
or
Accept: application/octet-stream
or any combination. Check your client and the header it sends.
You are mixing to approaches. The first, quite popular but not standardized, by providing the hint of the expected result in request's path by using the format parameter (BTW you may use the #QueryParam("format") String format rather than extracting it from ServletRequest). The second approach is the HTTP content negotiation mechanism, in this case using Accept/Content-Type headers. This mechanism is handled by the JAX-RS implementation based on #Produces annotation and providers' classes.
So now, your user is not only required to set the format but also Accept header. It seems that you are using the client which sets the Accept and one of its value is text/plain. That is why it the first case is working, but there is no application/octet-stream or */* (all), so you the JAX-RS expects that the client is not able to process such a content and instead sends him an error 406 Not Acceptable.
The solution here is to remove the #Produces annotation (you are the one who is taking care of the format of the response), or drop the format parameter and let the JAX-RS do his work (probably you will need to register your own provider). If you however stay with your solution then make sure that the correct or no Accept header is send in request (no header means: "I would accept whatever you send").

Categories