1

I have a Flutter application which is attempting to connect to a server API using the Dio package. The server uses a self-signed certificate, which means using a Dio workaround to verify the certificate in order to connect to the server (see https://pub.dev/packages/dio#https-certificate-verification).

Below is what I have so far:

String url = 'https://...';             // Server API URL
Map<String, dynamic> data = ... // Request body
String PEM = "XXXXX";           // Certificate content

Dio dio = Dio();

(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate  = (client) {
    client.badCertificateCallback=(X509Certificate cert, String host, int port){
        if (cert.pem == PEM) {
            // Verify the certificate
            return true;
        }
        return false;
    };
};

Response response = await dio.post(url, data: data);

However this throws an HttpException:

HttpException: Failed to parse HTTP, uri = https://... // Server API url

No other information is shown in the Exception. The value of response.data is null.

It is important to note that the server is in fact responding with data - so this appears to be a Flutter / client side issue. The server response is a standard object of String fields and values:

{
    "status": "ok",
    "token": "...",
    "field1": "...",
    "field2": "...",
    "field3": "...",
    ...
}

How do I fix this issue?

3
  • Can you provide the actual URI? Commented Mar 6, 2020 at 10:06
  • 1
    could you share some more info so that we can reproduce the issue from our end? Commented Mar 6, 2020 at 10:07
  • Can you show us the network response before any d’art code to be involved? Commented Mar 6, 2020 at 14:22

3 Answers 3

3
+300

That exception is only thrown by 2 functions within the http_parser.dart file: _expect and _expectHexDigit. Both of those functions are in turn called in several different places, all of them within the _doParse function from the same file. This function is called on the bytes received from the client-server connection stream.

http_parser.dart is part of Flutter's http API, so the issue is not with Dio or other 3rd party packages, or with your app's code, but rather with your server.

You can find the file/functions by performing a 'Find in Path' search using Android Studio (Ctrl + Shift + F on Windows) and searching for 'Failed to parse HTTP' while having 'Scope' selected as well as 'All Places' from the dropdown. You can also place breakpoints on the lines where those exceptions are thrown, which should help you narrow down the issue to the specific failure.

You will notice that most of those _expect function invocations are checking for 'LF' or 'CR' characters (line breaks) between various sections of the response, but the only way to tell for sure what the issue is is by debugging with breakpoints and digging deeper.

Edit: The _doParse function has the following comments above it:

// From RFC 2616.
// generic-message = start-line
//                   *(message-header CRLF)
//                   CRLF
//                   [ message-body ]
// start-line      = Request-Line | Status-Line
// Request-Line    = Method SP Request-URI SP HTTP-Version CRLF
// Status-Line     = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
// message-header  = field-name ":" [ field-value ]

It is simply expecting your server response to comply with RFC 2616 in terms of response segmentation and line breaks between segments. Maybe there is some tool out there that you could use to check if your server/endpoint is compliant with RFC 2616? RFC 2616 is the HTTP/1.1 protocol, so if for example your server is using HTTP/2.0, that would explain the exception.

Sign up to request clarification or add additional context in comments.

8 Comments

Hi, this is really helpful to know, thank you. How would I include the Flutter engine files in the Android Studio? As the project folder is open in Android Studio (and the Flutter files are elsewhere), so searching with 'Scope' selected still only includes the files in the project folder.
To clarify, I meant adding the Flutter engine files to Android Studio in order to add breakpoints to them (specifically, http_parser.dart).
As I've mentioned above, in addition to having 'Scope' selected you also need to select 'All Places' from the dropdown that shows up once you select 'Scope'. You don't need to do anything else for those files to be searchable.
There is also a 'manual' way of navigating to that file - go into 'External Libraries' (bottom of project explorer in Android Studio), and then into 'Dart Packages/sky_engine/_http/http_parser.dart'
Thanks for the help so far. I have found that Android Studio does not seem to be aware of the breakpoints added to http_parser.dart. Is there an option to enable this in AS?
|
0

Are you testing on Android? If so it only will connect to HTTPS endpoints by default, but you can workaround this by adding android:usesCleartextTraffic="true" to you AndroidManifest.xml for testing.

1 Comment

The endpoint is HTTPS, I have updated the question to clarify that.
0

Flutter:

Probably your problem is caused because the certificate is invalid in some platform, you can use this method:

if (Platform.isAndroid) {
    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
        client.badCertificateCallback = (X509Certificate cert, String host, int port) {
            if (cert.pem == PEM) {
                // Verify the certificate
                return true;
            }
            return false;
        };
    };
}

Android:

Since Android 9.0 urls that are not SSL are not allowed by default (see about ClearTextTraffic).

If your app is targeting to API Level 28, you must use inside your app only https:// urls or you can enable to allow the use of http:// from your app adding inside your AndroidManifest.xml :

android:usesCleartextTraffic=true

example:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
</manifest>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.