Tomcat ignores Accept-Language header - java

In my Java application, I set the default system locale (Locale.getDefault()) as Accept-Language header for the HTTP request to my tomcat web application.
In my case, this is de_DE.
On server side, I try to get the locale by using request.getLocale(). But I only get an empty string.
If I set the Accept-Language to de, everything works fine.
Why does de_DE not work as Accept-Language header?
EDIT:
This is my client side code:
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
And this my server side code:
request.getLocale().toString(); //empty string
request.getHeader("Accept-Language"); //"de_DE"

The correct format for language tags is de-DE. With a dash, not an underscore.
I guess it wouldn't be surprising if other webservers were more lenient and would accept de_DE to be equivalent, but Tomcat does not. For reference, Tomcat delegates this parsing to Locale.forLanguageTag(), which makes it clear that it expects format de-DE.

As kumesana pointed out, Tomcat will use Locale.forLanguageTag() to convert the Accept-Language header value into a Locale. There is an opposite method to convert a Locale instance into the expected header String: toLanguageTag()
I would suggest using this rather than manually replacing underscore with hyphen, so your code would be like the following:
connection.setRequestProperty("Accept-Language", Locale.getDefault().toLanguageTag());

Related

Spring REST Docs: special characters are not shown correctly

In my Spring MVC Test (UTF-8 encoded) we find:
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity())
.apply(documentationConfiguration(restDocumentation)
.snippets().withEncoding("UTF-8")) // default
.build();
...
myRequestDTO.setValue("Größe");
ResultActions action = this.mockMvc
.perform(post("/my-service")
.content(jacksonObjectMapper.writeValueAsString(myRequestDTO))
...
action.andDo(document("docs"));
The asciidoctor file contains
HTTP Request
include::{snippets}/docs/http-request.adoc[]
After I have rendered it and open the generated HTML file (which is UTF-8 encoded, too) in my firefox browser I find
HTTP Request
POST /my-service HTTP/1.1
...
Größe
How can the special chars be displayed correctly?
The underlying problem here was with the conversion of a request's content as a byte[] into a String. Spring REST Docs uses the charset attribute of the Content-Type header to determine the Charset that should be used when creating the String. If there's no Content-Type header or its value doesn't have a charset attribute, the JVM's default Charset is used (as a result of calling new String(bytes)).
There are two ways to avoid corruption of special characters:
Specify a charset attribute in the request's Content-Type header. Use text/plain;charset=UTF-8 rather than text/plain, for example.
Configure the JVM's default Charset by setting the file.encoding system property. -Dfile.encoding=UTF8, for example.
After I have called prettyPrint() it works:
action.andDo(document("docs",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())));

Java HttpURLConnection | POST method | Sequence of HTTP header fields

I am trying to "spoof" a Firefox HTTP POST request in Java using java.net.HttpURLConnection.
I use Wireshark to check the HTTP headers being sent, so I have (hopefully) reliable source of information, why the Java result doesn't match the ideal situation (using Firefox).
I have set all header fields exactly to the values that Firefox sends via HTTP and noticed, that the sequence of the header fields is not the same.
The output for Firefox is like:
POST ...
**Host**
User-Agent
Accept
Accept-Language
Accept-Encoding
Referer
Connection
Content-Type
Content-Length
When I let wireshark tap off my implementation in Java, it gives me a slightly different sequence of fields:
POST...
**User-Agent**
Accept
Accept-Language
Accept-Encoding
Referer
Content-Type
Host
Connection
Content-Length
So basically, I have all the fields, just in a different order.
I have also noticed that the Host field is sent with a different value:
www.thewebsite.com (Firefox) <---> thewebsite.com (Java HttpURLConnection), although I pass on the String to httpUrlConnection.setRequestProperty with the "www."
I have not yet analyzed the byte output of Wireshark, but I know that the server is not returning the same Location in the header fields of my response.
My questions are:
(1) Is is possible to control the sequence the header fields in the request, and if yes is it possible to do using HttpURLConnection? If not, is it possible to directly control the bytes in the HTTP header using Java? [I don't own the server, so my only hope to get the POST method working is through my application pretending to be Firefox, the server is not really verbose, my only info are: Apache with PHP]
(2) Is there a way to fix the setRequestProperty() problem ("www") as described above?
(3) What else could matter? (Do I need to concern the underlying layers, TCP....?)
Thanks for any comments.
PS. I am trying to model a situation without cookies being sent, so that I can ignore the effect.
First, the order of the headers is irrelevant.
Second, in order to manually override the host header you need to set sun.net.http.allowRestrictedHeaders=true either in code
System.setProperty("sun.net.http.allowRestrictedHeaders", "true")
or at JVM start
-Dsun.net.http.allowRestrictedHeaders=true
This is a security precaution introduced by Oracle a while ago. That's because according to RFC
The Host request-header field specifies the Internet host and port
number of the resource being requested, as obtained from the original
URI given by the user or referring resource (generally an HTTP URL).
the headers order is not important. the headers got by server are also out-of-order. And you can not control httpUrlConnection header order. But if you write your own TCP client, you can control your header order. like:
clientSocket = new Socket(serverHost, serverPort);
OutputStream os = clientSocket.getOutputStream();
String send = "GET /?id=y2y HTTP/1.1\r\nConnection: keep-alive\r\nKeep-Alive: timeout=15, max=200\r\nHost: chillyc.info\r\n\r\nGET /?id=y2y HTTP/1.1\r\nConnection: keep-alive\r\nKeep-Alive: timeout=15, max=200\r\nHost: chillyc.info\r\n\r\n";
os.write(send.getBytes());
The Second question is answered by Marcel Stör in the first answer.
a
I got lucky with Apache Http Components, my guess is that the "Host" header's missing "www." made the difference, which can be set exactly as intended using Apache's HttpPost:
httpPost.setHeader("Host", "www.thewebsite.com");
The Wireshark output confirmed my suspicion. Also this time the TCP communication prior to my HTTP post looks different (client ---> server, server ---> client, client ---> server) instead of (client ---> server, server ---> client, client ---> server, client---> server).
Now I get the desired Location header value and the server is also setting the cookies. :)
For the most part, this question is resolved.
Actually I wanted to use the lightweihgt HttpUrlConnection because that's what the Android Developers blog suggesting. The System.setProperty("sun.net.http.allowRestrictedHeaders", "true") might work as well, if it allows to "www." in the Host value.

how to remove a header from URLConnection

I am talking to a file upload service that accepts post data, not form data. By default, java's HttpURLConnection sets the Content-Type header to application/x-www-form-urlencoded. this is obviously wrong if i'm posting pure data.
I (the client) don't know the content type. I don't want the Content-Type header set at all. the service has a feature where it will guess at the content type (based on the file name, reading some data from the file, etc).
How do I unset a header? There's no remove header, and setting it to null doesn't change the value and setting it to the empty string results in the header being set with no value.
I haven't tested this approach but you can try this:
Extend HttpURLConnection and try by overriding its getContentHandler() and setContentHandler(...) methods. Most probably this should work as, you will take a look at code of getContentHandler().
Use Apache HttpClient instead of URLConnection
Use fluent Request to generate your request
use removeHeader()
What do you mean "i don't want the Content-Type header to set at all"?
The browser (or other http client) sends your post request to the server, so it has to inform the server which way it encoded the parameters.
If the Content-Type header is not set, on the server side you (= your server) won't be able to understand how to parse the received data.
If you didn't set Content-Type, the default value will be used.
You browser (or other http client) MUST do two things:
Send key/value pairs.
Inform the server how the key/value pairs were encoded.
So, it is impossible to completely get rid of this header.
I just accomplished this by setting the header to null.
connection.setRequestProperty(MY_HEADER, null);

HttpURLConnection and encoded characters in the URL

I have a code like this:
URL url = new URL("http://foo.com/?param=paj%E9");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
...
However, it seems like the openConnection is supressing the "%E9" part of the url, and the server ends up receiving a request http://foo.com?param=paj
Am I forgetting to apply any different setting for this to work properly?
Thanks!
EDIT: The url "http://foo.com/?param=paj%E9" is already encoded (from http://foo.com/?param=pajé), and this should be the request the server should receive. If I try to access http://foo.com/?param=paj%E9 straight from the browser, it works as expected. If I URLEncode "paj%E9", I'll be double-encoding the parameter, and the server would see "paj%E9" instead "pajé" upon decoding the value.
I'm actually trying to build a proxy, and therefore I receive the urls already encoded. The problem is that whenever I pass such an encoded parameter to be requested using HttpURLConnection, it simply ignores the encoded part (like %E9).
You need to use java.net.URI class to encode your URL instead of handle it on your own. Chek this:
HTTP URL Address Encoding in Java
You can use the following code
URLEncoder.encode("中文", "utf-8")

Safe Data serialization for Plain HTTP GET & POST communication

I'm using the client's browser to submit HTTP request.
For report generation the securityToken is submitted as POST, for report download the same token needs to be submitted by the user browser, this time using GET.
What encoding would you recommend for the securityToken which actually represents encrypted data.
I've tried BASE64 but this fails because the standard can include the "+" character which gets translated in HTTP GET to ' ' (blank space).
Then I tried URL Encoding, but this fails because for HTTP POST stuff such as %3d are transmitted without translation but when browser does HTTP GET with the data %3d is converted to '='.
What encoding would you recommend, to allow safe transmission over HTTP POST & GET without data being misinterpreted.
The environment is Java, Tomcat.
Thank you,
Maxim.
Hex string.
Apache commons-codec has a Hex class that provides this functionality.
It will look like this:
http://youraddress.com/context/servlet?param=ac7432be432b21
Well, you can keep the Base64 and use this solution:
Code for decoding/encoding a modified base64 URL

Categories

Resources