Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IllegalArgumentException: Unexpected char ... in header value: ... at com.squareup.okhttp.Headers$Builder.checkNameAndValue (Headers.java:295) #2016

Closed
thest1 opened this issue Nov 20, 2015 · 40 comments
Labels
bug Bug in existing code stackoverflow Referred to stackoverflow
Milestone

Comments

@thest1
Copy link

thest1 commented Nov 20, 2015

I get crash reports like that:

java.lang.IllegalArgumentException: Unexpected char 0x43a at 101 in header value: Mozilla/5.0 (Linux; U; Android 4.1.2; ru-ru; PMP7170B3G_DUO Build/JZO54K) AppleWebKit/534.30 (KHTML, как Gecko) Version/4.0 МобильныйSafari/534.30
    at com.squareup.okhttp.Headers$Builder.checkNameAndValue (Headers.java:295)
    at com.squareup.okhttp.Headers$Builder.set (Headers.java:275)
    at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.setRequestProperty (HttpURLConnectionImpl.java:526)
    <some 3rd library code I don't have access to>

Seems to happen due to non-ASCII chars in header value.
Similar to #1998. But #1998 is about response header and my smacktrace is about request header.

Thank you!

@swankjesse swankjesse added the bug Bug in existing code label Nov 22, 2015
@swankjesse swankjesse added this to the 2.7 milestone Nov 22, 2015
@swankjesse
Copy link
Collaborator

Any chance you can get the 3rd party library to fix? It's difficult to fix this in OkHttp because the request header contains invalid data.

@thest1
Copy link
Author

thest1 commented Dec 3, 2015

No problem, I can ask library developers try/catch that.

Just wanted you to be aware about the issue.

Imagine some other developer. His project works fine. He adds

 URL.setURLStreamHandlerFactory(new OkUrlFactory(okHttpClient));

After that his project starts crashing. He can't add try/catch becasue it happens in some 3rd party library. He can't fix the wrong header becasue it's in some 3rd party library. He's not hapy with "your header is incorrect" answer because the same header worked just fine for a long time with HttpUrlConnection.

Probably not a big deal, I'm not sure. Just wanted issue to be created, may be helpful in further development :-) Thank you!

@swankjesse
Copy link
Collaborator

Yeah, agreed. We decided to make it more strict than it was before on the expectation that most people weren't doing this deliberately.

@cakoose
Copy link

cakoose commented Dec 11, 2015

When someone passes you a Java String, rejecting non-ASCII (as you do now) is definitely a good idea.

However, the HTTP specification allows more than just ASCII (roughly, any octet except control characters). What happens when the response contains a header with non-ASCII? Do you just get rid of the non-ASCII parts? It might be worth having a byte[] interface to let clients receive non-ASCII header values uncorrupted.

@pbertsch
Copy link

I had that issue a few minutes ago.
What I did was adding a Base64 encoded string as Header.

I used Android and the Base64 class with Base64.DEFAULT.

When I change it to Base64.URL_SAFE, its working. Problem is, the server
does not understand it anymore.

Patrick

https://www.streetlife.com.mx
powered by Alpha Wave Systems
On Dec 11, 2015 14:46, "Kannan Goundan" [email protected] wrote:

When someone passes you a Java String, rejecting non-ASCII (as you do
now) is definitely a good idea.

However, the HTTP specification allows more than just ASCII (roughly, any
octet except control characters). What happens when the response contains a
header with non-ASCII? Do you just get rid of the non-ASCII parts? It might
be worth having a byte[] interface to let clients receive non-ASCII
header values uncorrupted.


Reply to this email directly or view it on GitHub
#2016 (comment).

@cakoose
Copy link

cakoose commented Dec 11, 2015

@pbertsch: What encoding does the server say you should use? Is there public documentation for the server you're sending requests to? (Aside: OkHttp should be fine with both DEFAULT and URL_SAFE -- both use only printable ASCII characters.)

@pbertsch
Copy link

I used only NO_WRAP and it seems to work at the moment. I will try to write
a test case with the Base64.DEFAULT flag only.

Patrick

https://www.streetlife.com.mx
powered by Alpha Wave Systems
On Dec 11, 2015 15:16, "Kannan Goundan" [email protected] wrote:

@pbertsch https://github.com/pbertsch: What encoding does the server
say you should use? Is there public documentation for the server you're
sending requests to? (Aside: OkHttp should be fine with both DEFAULT and
URL_SAFE -- both use only printable ASCII characters.)


Reply to this email directly or view it on GitHub
#2016 (comment).

@cakoose
Copy link

cakoose commented Dec 11, 2015

Ah, that's probably it. Newlines aren't allowed as header values.

@carlosEleao
Copy link

Hi, this restriction about just ASCII chars is very strange, we support chinese users etc... So almost all of our request fail when we try to send some chinese value using the header. This works fine in the HttpRequest apache. Why this restriction?

@DASAR-zz
Copy link

@gudomau This restriction comes from official HTTP spec.

@cakoose
Copy link

cakoose commented Jan 25, 2016

Non-ASCII characters are allowed according to both the original and updated HTTP 1.1 specs.

The updated spec recommends against using non-ASCII characters for newly-defined headers, but clearly they're allowed:

Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets. A recipient SHOULD treat other octets in field content (obs-text) as opaque data.

Because of the way UTF-8 is encoded, all codepoints above 127 are composed of bytes whose values are 128 or greater, which means they should pass through untouched. The only characters you have to be careful with are ASCII control characters and, in certain situations, spaces, quotes, and backslashes.

@carlosEleao
Copy link

How can I do that since all headers are validated by the checkNameAndValue ?

@peterArconsis
Copy link

The HTTP Spec actually uses the word SHOULD, so isn't this restriction in the http client too harsh?

amartinz pushed a commit to NamelessRom/android_external_okhttp that referenced this issue Jun 30, 2016
This relaxes some validation logic newly introduced in the version
of OkHttp used in N.

This change leaves the character code validation stricter
than it was in M by still preventing control codes like \n, \r,
backspace and delete. It does allow developers to pass
Java characters > 7F to addRequestProperty() and also receive
headers from servers which contain characters > 7F.

Android's HttpURLConnection does not follow the HTTP spec as
it encodes request header values and interprets response
headers as UTF-8 and not ISO-8859-1.

If a server is expecting or sending ISO-8859-1 encoded characters
>7F in headers then these will still be corrupted or misinterpreted
by Android. However, this has been the behavior since L and is not
changed here.

The OkHttp change which caused characters >7F to generate an
IllegalArgumentException and partially reverted here:
square/okhttp#1785

See also: square/okhttp#1998
square/okhttp#2016
for recent upstream bugs.

Bug: 28867041
Bug: https://code.google.com/p/android/issues/detail?id=210205
(cherry picked from commit 75687ca5ae54f417afb4c02ba04767da6786d829)

Change-Id: Ib640b58addff4c0c4eac589c77eb74a6bd6b3ec2
amartinz pushed a commit to NamelessRom/android_external_okhttp that referenced this issue Jun 30, 2016
This reapplies AOSP commit 3c28a13
There is a chance that we may want to revert this change in future.

Previous commit message:

This relaxes some validation logic newly introduced in the version
of OkHttp used in N.

This change leaves the character code validation stricter
than it was in M by still preventing control codes like \n, \r,
backspace and delete. It does allow developers to pass
Java characters > 7F to addRequestProperty() and also receive
headers from servers which contain characters > 7F.

Android's HttpURLConnection does not follow the HTTP spec as
it encodes request header values and interprets response
headers as UTF-8 and not ISO-8859-1.

If a server is expecting or sending ISO-8859-1 encoded characters
>7F in headers then these will still be corrupted or misinterpreted
by Android. However, this has been the behavior since L and is not
changed here.

The OkHttp change which caused characters >7F to generate an
IllegalArgumentException and partially reverted here:
square/okhttp#1785

See also: square/okhttp#1998
square/okhttp#2016
for recent upstream bugs.

Bug: 28867041
Bug: https://code.google.com/p/android/issues/detail?id=210205
(cherry picked from commit 75687ca5ae54f417afb4c02ba04767da6786d829)

Change-Id: Id683ec13142f1d7d8792143066f1dd2e5f62cf86
cm-gerrit pushed a commit to CyanogenMod/android_external_okhttp that referenced this issue Aug 24, 2016
This relaxes some validation logic newly introduced in the version
of OkHttp used in N.

This change leaves the character code validation stricter
than it was in M by still preventing control codes like \n, \r,
backspace and delete. It does allow developers to pass
Java characters > 7F to addRequestProperty() and also receive
headers from servers which contain characters > 7F.

Android's HttpURLConnection does not follow the HTTP spec as
it encodes request header values and interprets response
headers as UTF-8 and not ISO-8859-1.

If a server is expecting or sending ISO-8859-1 encoded characters
>7F in headers then these will still be corrupted or misinterpreted
by Android. However, this has been the behavior since L and is not
changed here.

The OkHttp change which caused characters >7F to generate an
IllegalArgumentException and partially reverted here:
square/okhttp#1785

See also: square/okhttp#1998
square/okhttp#2016
for recent upstream bugs.

Bug: 28867041
Bug: https://code.google.com/p/android/issues/detail?id=210205
Change-Id: Ibdf14d819411a12fcc78d012bfca97db048b7e6e
@Panthro
Copy link

Panthro commented Oct 18, 2016

so you're saying that "ç" and "é á ó" are "invalid data in headers"?

Sorry but I respectfully disagree, the RFC does not say that, is the client being too strict.

In my scenario, what's happening is that I use pagination with the Link header, but when my users are querying my API with special chars eg "ç ã é" this error is being thrown =|

@Panthro
Copy link

Panthro commented Oct 18, 2016

Alright, I can see the issue is fixed in new releases as mentioned #1998 sorry for my comment in an outdated thread.

@henryleu
Copy link

Whatever I use whether android.util.Base64.DEFAULT or URL_SAFE, as flag for Base64.encodeToString(...), it throws IllegalArgumentException.

        byte[] sigBytes = hmac.doFinal(reqStr.getBytes());
        String sig = Base64.encodeToString(sigBytes, Base64.URL_SAFE);
...

        Request.Builder builder = request.newBuilder()
                .header("x-guoer-ts", signature.ts)
                .header("x-guoer-nonce", signature.nonce)
                .header("x-guoer-sig", signature.sig);


java.lang.IllegalArgumentException: Unexpected char 0x0a at 44 in x-guoer-sig value: IkObQzVpEFRv06bEwc5-CnSUO4rqYX_9Gdqs4qEJ_vI=


	at okhttp3.Headers$Builder.checkNameAndValue(Headers.java:320)
	at okhttp3.Headers$Builder.set(Headers.java:300)
	at okhttp3.Request$Builder.header(Request.java:164)

@henryleu
Copy link

I regret that I used base64 which is always a trouble. I should use hex for it is stable and deterministic ascii. the code is running in production in iOS, it is hard to change now.

@cakoose
Copy link

cakoose commented Dec 27, 2016

Character 0x0a is newline. I'm guessing sig contains a newline at the end. The docs say you can pass in Base64.NO_WRAP to omit newlines.

For OkHttp: it might be good to quote strings in error messages so the mistake is more clear. Java string literal quoting or JSON quoting seem reasonable.

@swankjesse
Copy link
Collaborator

swankjesse commented Dec 27, 2016

Also consider using ByteString.base64() which might be a bit easier.

@castaldichristian
Copy link

Hi, just switched from Volley and this is the only issue we found, we understand what the standard suggests, however we have cookies we need to send in the header that contain Russian characters.

Is there a way we can bypass the checkNameAndValue method?

@cakoose
Copy link

cakoose commented Jun 8, 2017

@kriskast: What value are you passing to OkHttp and what error are you seeing, exactly?

@castaldichristian
Copy link

Caused by: java.lang.IllegalArgumentException: Unexpected char 0x416 at 9 in User-Agent value: M.R.User=Ж

builder.addHeader(entry.getKey(), "M.R.User=Ж");

This is only a small extract of it as the cookie string is longer and has more Russian characters.

@swankjesse
Copy link
Collaborator

Thats not valid HTTP. You might need to base64 encode it client-side and decode it server-side. ByteString from Okio can help with that.

@castaldichristian
Copy link

Thanks for your response, to Clarify

builder.addHeader("Cookie", "M.R.User=Ж;"); where the value is part of a cookie that is generated by .NET and we have no access on how it is produced. Also we don't have access to the server to be able to decode.

What do you mean by not valid HTTP?

@swankjesse
Copy link
Collaborator

What happens when you use this cookie in a browser like Chrome or Safari?

@swankjesse
Copy link
Collaborator

Also – make sure you're using the latest OkHttp. We recently fixed things to be consistent with major web browsers.

@cakoose
Copy link

cakoose commented Jun 8, 2017

Just to be clear, HTTP is a byte-oriented protocol so I'm assuming you want "Ж" to be encoded as UTF-8, which would be a two-byte sequence: 0xd0, 0x96.

Loophole: use Headers.of(...), which doesn't seem to do any validation, though this is probably dangerous to rely on. (This only works because OkHttp's HTTP 1 and 2 codecs encode/decode header values as UTF-8, which seems problematic in general but oh well.)

@cakoose
Copy link

cakoose commented Jun 8, 2017

@swankjesse: Excluding control characters, UTF-8-encoded text is valid (in some sense) as an HTTP header value. See: #2016 (comment)

@castaldichristian
Copy link

castaldichristian commented Jun 8, 2017

Just checked on Chrome, looks like UserTitle=женский from the server is being encoded into UserTitle=%D0%B6%D0%B5%D0%BD%D1%81%D0%BA%D0%B8%D0%B9 on chrome, and if I send both of them back to the server both are accepted, would you know how I can convert it to the one in chrome so I send that?

@castaldichristian
Copy link

Correction, if I send UserTitle=%D0%B6%D0%B5%D0%BD%D1%81%D0%BA%D0%B8%D0%B9 to the server it seems it is not being decoded back, looks like chrome only displays it like that but actually sends UserTitle=женский in the background.

@cakoose 's method is the best I found so far of using Headers.of(...) which skips validation

Thanks everyone

@smart1024
Copy link

@pbertsch good job

@vishna
Copy link

vishna commented Sep 21, 2018

Headers.of() is your friend if you want non-ASCII characters in your headers... at least for know, unless someone decides to add ASCII validation there.

@swankjesse
Copy link
Collaborator

@vishna yikes. #4283

@rohitkum28
Copy link

Hi,

I am trying to send "tête-à-tête.pdf" in my header and I am getting the same exception. But, I am able to send the same header from either Postman or using HttpsURLConnection. So, I don't understand why this restriction in okhttp when HTTP protocol supports this.

@swankjesse
Copy link
Collaborator

Try this: https://square.github.io/okhttp/4.x/okhttp/okhttp3/-headers/-builder/add-unsafe-non-ascii/

@rohitkum28
Copy link

Try this: https://square.github.io/okhttp/4.x/okhttp/okhttp3/-headers/-builder/add-unsafe-non-ascii/

Thanks! This works. I am suprised why was this not mentioned in this thread before.

@yschimke yschimke added the stackoverflow Referred to stackoverflow label Mar 31, 2020
@yschimke
Copy link
Collaborator

@rohitkum28 If you ask and self answer on stackoverflow, I'll bump up both. 25 points right there :)

@rohitkum28
Copy link

@rohitkum28 If you ask and self answer on stackoverflow, I'll bump up both. 25 points right there :)

I did ask the question on StackOverflow and now that I know the answer I added it on Stackoverflow.

@castaldichristian
Copy link

Try this: https://square.github.io/okhttp/4.x/okhttp/okhttp3/-headers/-builder/add-unsafe-non-ascii/

Thanks! This works. I am suprised why was this not mentioned in this thread before.

thats because it wasn't possible at the time with the current version

@vvelmurugan
Copy link

Try this: https://square.github.io/okhttp/4.x/okhttp/okhttp3/-headers/-builder/add-unsafe-non-ascii/

Thanks! This works. I am suprised why was this not mentioned in this thread before.

thats because it wasn't possible at the time with the current version

is this method is renamed as addLenient() now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bug in existing code stackoverflow Referred to stackoverflow
Projects
None yet
Development

No branches or pull requests