dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.04k stars 1.55k forks source link

Throws exception 'Failed to parse header value' #46442

Open sleith opened 5 years ago

sleith commented 5 years ago

Hi,

Http lib version 0.12.0+1 Flutter 1.0.0 • channel beta • https://github.com/flutter/flutter.git Framework • revision 5391447fae (6 weeks ago) • 2018-11-29 19:41:26 -0800 Engine • revision 7375a0f414 Tools • Dart 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

I tried http.get(). It works fine on response code 200. But when my server returns 401 Unauthorized, it throws exception "Failed to parse header value". Please find the header below i got from Postman:

Date →Fri, 11 Jan 2019 10:04:09 GMT Server →kong/0.11.2 Content-Type →application/json; charset=utf-8 WWW-Authenticate →Bearer realm="service" error="invalid_token" error_description="The access token is invalid or has expired" Vary →Accept-Encoding Content-Encoding →gzip Content-Length →97 Keep-Alive →timeout=15, max=100 Connection →Keep-Alive

Any idea why it failed parse header value? Thank you

zoechi commented 5 years ago

Can you please post the full exception output?

sleith commented 5 years ago

Hi, i print(exception) only show that text "Failed to parse header value". Here's the code and url you can try to see this issue:

Future<http.Response> testHit() async {
    Map<String, String> headers = new Map<String, String>();
    headers['Content-Type'] = "application/json";
    headers['Accept'] = "application/json";
    headers['nik'] = "1805304321";
    headers['deviceNumber'] = "869058030276537";
    headers['role'] = "SALES_AGENT";
    headers['Authorization'] = "Bearer dXKCloWJ0KQp05nQSbfmj6EvO6sILGB5}";

    http.Response response;
    try {
      response = await http.get(
          "https://api-uat.homecredit.co.id/smart-headline/v1/headline/",
          headers: headers);
      print("--> Requeststatus: " + response.statusCode.toString());

      if (response.statusCode == 200) {
        print("->" + response.body);
        return response;
      } else {
        print("->Request failed with status:" + response.statusCode.toString());
      }
    } catch (exception) {
      print(exception);
      if (response != null) {
        print("Request failed with status: " + response.statusCode.toString());
      } else {
        print("response null");
      }
    }
    return null;
  }

If you run on postman it will give output. Thank you :)

zoechi commented 5 years ago

Please remove try/catch completely and run again and then post the error output.

sleith commented 5 years ago

Hi, Please find the log:

E/flutter (15005): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception: E/flutter (15005): Failed to parse header value E/flutter (15005): #0 IOClient.send (package:http/src/io_client.dart:65:7) E/flutter (15005): E/flutter (15005): dart-lang/http#1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38) E/flutter (15005): E/flutter (15005): dart-lang/http#2 BaseClient.get (package:http/src/base_client.dart:32:7) E/flutter (15005): dart-lang/http#3 get. (package:http/http.dart:46:36) E/flutter (15005): dart-lang/http#4 _withClient (package:http/http.dart:166:20) E/flutter (15005): E/flutter (15005): dart-lang/http#5 get (package:http/http.dart:46:5) E/flutter (15005): dart-lang/http#6 LoginState.testHit (package:smart/screens/login/login_screen.dart:707:22)

Thank you

zoechi commented 5 years ago

Looks like this is an error your server causes

WWW-Authenticate →Bearer realm="service" error="invalid_token" error_description="The access token is invalid or has expired"
annalaufey commented 5 years ago

I have the same problem, I would love to know if you've found a solution. I know the reason is that my auth token is invalid, but I need to be able to catch the 401 unauthorized error when that happens. Instead it throws a parse header value error

redvg commented 5 years ago

Im having the same issue. Im trying to access the file which i've uploaded to firebase storage. So i've tried both .getDownloadURL() on the ref AND generating signed url on the gcs blob myself. It fails with 'invalid header field name' when using CachedNetworkImage() or Image.network(). When I try to use http.get() on it fails with the following

E/flutter (23764): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhand led exception: E/flutter (23764): Invalid header field name E/flutter (23764): #0 IOClient.send (package:http/src/io_client.dart:65:7) E/flutter (23764): E/flutter (23764): dart-lang/http#1 BaseClient._sendUnstreamed (package:http/src/base_cli ent.dart:169:38) E/flutter (23764): E/flutter (23764): dart-lang/http#2 BaseClient.get (package:http/src/base_client.dart:32: 7) E/flutter (23764): dart-lang/http#3 get. (package:http/http.dart:46:36 ) E/flutter (23764): dart-lang/http#4 _withClient (package:http/http.dart:166:20) E/flutter (23764): E/flutter (23764): dart-lang/http#5 get (package:http/http.dart:46:5) E/flutter (23764): dart-lang/http#6 RecordCard.build (FOO/screens/feed/compone nts/record_card/record_card.dart:53:7) E/flutter (23764): dart-lang/http#7 StatelessElement.build (package:flutter/src/widgets/f ramework.dart:3774:28) E/flutter (23764): dart-lang/http#8 ComponentElement.performRebuild (package:flutter/src/ widgets/framework.dart:3721:15) E/flutter (23764): dart-lang/http#9 Element.rebuild (package:flutter/src/widgets/framewor k.dart:3547:5) E/flutter (23764): dart-lang/http#10 ComponentElement._firstBuild (package:flutter/src/wid gets/framework.dart:3701:5) E/flutter (23764): dart-lang/http#11 ComponentElement.mount (package:flutter/src/widgets/f ramework.dart:3696:5) E/flutter (23764): dart-lang/http#12 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#13 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#14 SingleChildRenderObjectElement.mount (package:flutter /src/widgets/framework.dart:4860:14) E/flutter (23764): dart-lang/http#15 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#16 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#17 SingleChildRenderObjectElement.mount (package:flutter /src/widgets/framework.dart:4860:14) E/flutter (23764): dart-lang/http#18 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#19 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#20 ComponentElement.performRebuild (package:flutter/src/ widgets/framework.dart:3732:16) E/flutter (23764): dart-lang/http#21 Element.rebuild (package:flutter/src/widgets/framewor k.dart:3547:5) E/flutter (23764): dart-lang/http#22 ComponentElement._firstBuild (package:flutter/src/wid gets/framework.dart:3701:5) E/flutter (23764): dart-lang/http#23 ComponentElement.mount (package:flutter/src/widgets/f ramework.dart:3696:5) E/flutter (23764): dart-lang/http#24 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#25 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#26 ComponentElement.performRebuild (package:flutter/src/ widgets/framework.dart:3732:16) E/flutter (23764): dart-lang/http#27 Element.rebuild (package:flutter/src/widgets/framewor k.dart:3547:5) E/flutter (23764): dart-lang/http#28 ComponentElement._firstBuild (package:flutter/src/wid gets/framework.dart:3701:5) E/flutter (23764): dart-lang/http#29 ComponentElement.mount (package:flutter/src/widgets/f ramework.dart:3696:5) E/flutter (23764): dart-lang/http#30 ParentDataElement.mount (package:flutter/src/widgets/ framework.dart:4047:11) E/flutter (23764): dart-lang/http#31 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#32 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#33 ComponentElement.performRebuild (package:flutter/src/ widgets/framework.dart:3732:16) E/flutter (23764): dart-lang/http#34 Element.rebuild (package:flutter/src/widgets/framewor k.dart:3547:5) E/flutter (23764): dart-lang/http#35 ComponentElement._firstBuild (package:flutter/src/wid gets/framework.dart:3701:5) E/flutter (23764): dart-lang/http#36 StatefulElement._firstBuild (package:flutter/src/widg ets/framework.dart:3848:11) E/flutter (23764): dart-lang/http#37 ComponentElement.mount (package:flutter/src/widgets/f ramework.dart:3696:5) E/flutter (23764): dart-lang/http#38 Element.inflateWidget (package:flutter/src/widgets/fr amework.dart:2950:14) E/flutter (23764): dart-lang/http#39 Element.updateChild (package:flutter/src/widgets/fram ework.dart:2753:12) E/flutter (23764): dart-lang/http#40 SliverMultiBoxAdaptorElement.updateChild (package:flu tter/src/widgets/sliver.dart:1028:36) E/flutter (23764): dart-lang/http#41 SliverMultiBoxAdaptorElement.createChild.<anonymous c losure> (package:flutter/src/widgets/sliver.dart:1013:20) E/flutter (23764): dart-lang/http#42 BuildOwner.buildScope (package:flutter/src/widgets/fr amework.dart:2266:19) E/flutter (23764): dart-lang/http#43 SliverMultiBoxAdaptorElement.createChild (package:flu tter/src/widgets/sliver.dart:1006:11) E/flutter (23764): dart-lang/http#44 RenderSliverMultiBoxAdaptor._createOrObtainChild.<ano nymous closure> (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:274 :23) E/flutter (23764): dart-lang/http#45 RenderObject.invokeLayoutCallback. (package

redvg commented 5 years ago

so I found the reason: I was attaching EXIF tags to the image metadata which gcs attaches to the response headers:). implications for http lib might be:
.it has to learn to parse exotic headers .it has to provide a meaningful error info, like which header field it fails to process

karoitay commented 5 years ago

Another way to repro

import 'package:http/http.dart' as http;

main() async {
  await http.post("https://api.thetvdb.com/login");
}

Exception:

Unhandled exception:
Failed to parse header value
#0      IOClient.send (package:http/src/io_client.dart:65:7)
<asynchronous suspension>
dart-lang/http#1      BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38)
<asynchronous suspension>
dart-lang/http#2      BaseClient.post (package:http/src/base_client.dart:54:7)
dart-lang/http#3      post.<anonymous closure> (package:http/http.dart:70:16)
dart-lang/http#4      _withClient (package:http/http.dart:166:20)
<asynchronous suspension>
dart-lang/http#5      post (package:http/http.dart:69:5)
dart-lang/http#6      main (file:///usr/local/google/home/karoitay/Desktop/dart2/example/dart2_example.dart:4:9)
<asynchronous suspension>
dart-lang/http#7      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
dart-lang/http#8      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)

Result of curl -i -X POST https://api.thetvdb.com/login:

HTTP/2 401 
date: Mon, 29 Jul 2019 09:15:50 GMT
content-type: application/json; charset=utf-8
content-length: 26
set-cookie: __cfduid=d0a0c5ee3934fe0f08c3ddd136ab53e471564391749; expires=Tue, 28-Jul-20 09:15:49 GMT; path=/; domain=.thetvdb.com; HttpOnly
vary: Accept-Language
www-authenticate: JWT realm=jwt auth
x-powered-by: Thundar!
x-thetvdb-api-version: 2.2.0
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 4fddfd9498b5c2c2-FRA

{"Error":"Not authorized"}

Looks like parsing of www-authenticate: JWT realm=jwt auth fails because of the space after realm=jwt. Not sure if this is a valid header, but regardless, I think that callers should be able to get the 401 response and handle it instead.

Andranjo97 commented 4 years ago

Possible solution Hey guys so in case you are getting this error and assigning your response to a final, then change it to be var. That did the trick for me.

jgrandchavin commented 4 years ago

Same error for me, I have 2 different env and it works with one but not the other (I get Failed to parse header error)

liudonghua123 commented 3 years ago

I have a similar issue https://github.com/dart-lang/pub/issues/2658.

marioki commented 3 years ago

Any solution to this error? Im having the same issue. I found this but i have no idea what it means.

https://github.com/flutterchina/dio/issues/219#issuecomment-472797390

rvowles commented 3 years ago

The immediate fix would be to not use inline enums. I'll have time to resolve it specifically close to Xmas when I can't take done time of but I'm too busy right now sorry.

On Sun, Dec 13, 2020, 4:49 PM Mario Kirven notifications@github.com wrote:

Any solution to this error? Im having the same issue. I found this but i have no idea what it means.

flutterchina/dio#219 (comment) https://github.com/flutterchina/dio/issues/219#issuecomment-472797390

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dart-lang/http/issues/225#issuecomment-743944017, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAANTWHMMWYTTDLCDRJ4OCTSUQ2TZANCNFSM4GPMUFRA .

abhaysood commented 3 years ago

I'm facing the same issue. For 401s a server I need to interact with responds with the header: WWW-Authenticate: Bearer realm=<realm_value>, error=invalid-token, error_description=Invalid bearer token.

This fails with an exception HttpException: Failed to parse header value which makes it really hard to handle refresh token scenarios. I think it happens because of the spaces in header value. Note that this is not the case with other popular HTTP clients ( OkHttp on Android & Alamofire on iOS).

I don't think the HTTP RFC mentions any restriction on whitespace in the value of the WWW-Authenticate header. Let me know if I'm wrong here. Ref.

Can this be prioritised anytime soon? Happy to help in contributing if I can get some guidance.

objectivecosta commented 3 years ago

Also happens when you receive a header with an empty value... Eg.:

date: Tue, 22 Jun 2021 14:21:40 GMT content-type: application/json;charset=UTF-8 access-control-expose-headers: access-control-allow-origin: * vary: accept-encoding access-control-allow-methods: GET accept-encoding: application/json access-control-allow-headers: authorization,Access-Control-Allow-Origin,Content-Type,SOAPAction,Authorization,Authorization

The highlighted headers seem to break the http package.

natebosch commented 3 years ago

This appears to be a problem in the SDK. I can reproduce this with

import 'dart:io';

void main() async {
  var client = HttpClient();
  var request =
      await client.openUrl('POST', Uri.parse('https://api.thetvdb.com/login'));
  var response = await request.close();
  await response.drain();
  client.close();
}
objectivecosta commented 3 years ago

Strictly speaking, I even think that Dart is doing the ”canonically” right thing: out-of-spec headers, throw an exception. But given that libcurl (and most likely others) HTTP clients in the wild deal gracefully with this, it’s very likely that there are some out-of-spec servers out in the wild that would simply be incompatible with Dart.

(Assuming, of course, that this is an issue with the headers being out of spec)

wgoudsbloem commented 3 years ago

Seems that “Some out of spec sever” includes AWS HTTP API.

objectivecosta commented 3 years ago

Hey, I see that this thread is getting stale... I'd be happy to do the changes necessary on the code and the tests and submit a PR to the project if that's what's needed to get this issue out of the way! Is this confirmed as an issue with the dart-lang/sdk? Should I proceed with doing the changes and submitting a PR?

Happy to help in any way possible!

cc @natebosch (?)

RakaAlrian commented 2 years ago

Looks like this is an error your server causes

WWW-Authenticate →Bearer realm="service" error="invalid_token" error_description="The access token is invalid or has expired"

I'm having this similar header response, any workaround about this issue please?

RakaAlrian commented 2 years ago

I tried to create an simple server in my local with php and go, put this header WWW-Authenticate: Bearer realm="service" error="invalid_token" error_description="The access token is invalid or has expired", and then call it from flutter. I don't really understand the result because that header giving error(http failed to parse header) when I set server status code to 401, but its not giving error when I set server status code to 200.

carlosriveroib commented 2 years ago

3,5 years after this issue was opened, no solution or fix were provided...

MagicalTux commented 2 years ago

I'm guessing flutter will try to parse WWW-Authenticate when receiving a status 401 error, and fail when it's not a Basic but a Bearer auth, resulting in this bug.

This needs fixing...

MrIceman commented 2 years ago

This issue still happens with AWS Http Gateway

carlosriveroib commented 2 years ago

This issue still happens with AWS Http Gateway

Here is where I'm having the parse error

kuyazee commented 1 year ago

This issue still happens with AWS Http Gateway

Same here

thecodekitchen commented 1 year ago

I'm getting this error while trying to call a Firebase cloud function from a Flutter app that is authenticated with the same project the function is hosted on.

FrancoisG-WIMT commented 1 year ago

Almost the exact same problem as @thecodekitchen - Flutter app trying to call to a Firebase FaaS, however this function is fronted by Firebase Hosting for caching purposes.

FrancoisG-WIMT commented 1 year ago

tl;dr - the troublesome header, could not be identifier but we could make a Flutter app calling to Firebase work with authentication.

For any future travelers, the error we experienced was because the Cloud Function (which backs Firebase FaaS) was not set to allUsers - instead we thought that using allAuthenticatedUsers would (magically) "do the right thing" and validate the Firebase OAuth bearer token included by the calling app.

This doesn't work, and once we reverted to allUsers and implemented the code to validate the bearer token ourselves, we didn't see the same exception being raised by the http library. Authentication failures, including mismatches between the project the token was for vs the project the function was running in, were reported normally. Other 5xx and 4xx our function raised were also handled properly.

eggzotic commented 8 months ago

This is still a problem today. In my case, when receiving a 401 error, curl shows the headers to be: HTTP/1.1 401 Unauthorized

Server: nginx/1.18.0 (Ubuntu)
Date: Wed, 13 Dec 2023 22:34:41 GMT
Content-Type: application/json
Content-Length: 183
Connection: keep-alive
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type,Accept,Authorization
Access-Control-Allow-Methods: GET,HEAD,POST,PUT,PATCH,DELETE
Access-Control-Allow-Origin: 
Www-Authenticate: [Err: 0x00040C04] Token invalid.

I believe that last one, is breaking it due to that value [Err: ...] ... being invalid JSON (starts out looking like a list, but has extra text after the ]). So far I cannot convince dio to ignore that...

natebosch commented 8 months ago

cc @brianquinlan