Giter Site home page Giter Site logo

http2's Introduction

Dart CI pub package package publisher

This library provides an http/2 interface on top of a bidirectional stream of bytes.

Usage

Here is a minimal example of connecting to a http/2 capable server, requesting a resource and iterating over the response.

import 'dart:convert';
import 'dart:io';

import 'package:http2/http2.dart';

Future<void> main() async {
  final uri = Uri.parse('https://www.google.com/');

  final transport = ClientTransportConnection.viaSocket(
    await SecureSocket.connect(
      uri.host,
      uri.port,
      supportedProtocols: ['h2'],
    ),
  );

  final stream = transport.makeRequest(
    [
      Header.ascii(':method', 'GET'),
      Header.ascii(':path', uri.path),
      Header.ascii(':scheme', uri.scheme),
      Header.ascii(':authority', uri.host),
    ],
    endStream: true,
  );

  await for (var message in stream.incomingMessages) {
    if (message is HeadersStreamMessage) {
      for (var header in message.headers) {
        final name = utf8.decode(header.name);
        final value = utf8.decode(header.value);
        print('Header: $name: $value');
      }
    } else if (message is DataStreamMessage) {
      // Use [message.bytes] (but respect 'content-encoding' header)
    }
  }
  await transport.finish();
}

An example with better error handling is available here.

See the API docs for more details.

Features and bugs

Please file feature requests and bugs at the issue tracker.

http2's People

Contributors

a14n avatar athomas avatar dependabot[bot] avatar devoncarew avatar franklinyow avatar iinozemtsev avatar jakobr-google avatar jonasfj avatar kevmoo avatar michaelrfairhurst avatar mit-mit avatar mkustermann avatar mosuem avatar mraleph avatar natebosch avatar sethladd avatar sigurdm avatar srawlins avatar szakarias avatar tvolkert avatar wertklop avatar xclud avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

http2's Issues

Issue with race of cancelling data on client side and sending data on server side

If the client side cancels a stream and the server side sends more data at the same time, the client reacts incorrectly to the incoming data frames.

From grpc/grpc-dart#131 (comment):

With the following events:

  • client calls a method that streams results
  • server starts the stream and send the first DataFrame
  • client receives the first DataFrame
  • client cancels the stream
  • server (that has not yet received the cancellation) sends the second DataFrame
  • server receives cancellation and stops sending DataFrame
  • client receives the second DataFrame

The last line leads today to an error (grabbed with some additionnal logs):

Connection._catchProtocolErrors: Bad state: Was in the process of closing.
#0      _StreamMessageQueueIn&Object&TerminatableMixin&ClosableMixin.ensureNotClosingSync (package:http2/src/error_handler.dart:73:7)
#1      StreamMessageQueueIn.enqueueMessage (package:http2/src/flowcontrol/stream_queues.dart:217:5)
#2      ConnectionMessageQueueIn._tryDispatch (package:http2/src/flowcontrol/connection_queues.dart:317:10)
#3      ConnectionMessageQueueIn._addMessage (package:http2/src/flowcontrol/connection_queues.dart:295:5)
#4      ConnectionMessageQueueIn.processDataFrame (package:http2/src/flowcontrol/connection_queues.dart:253:5)
#5      StreamHandler._handleDataFrame (package:http2/src/streams/stream_handler.dart:634:19)
#6      StreamHandler._processStreamFrameInternal.<anonymous closure> (package:http2/src/streams/stream_handler.dart:596:11)
#7      _StreamHandler&Object&TerminatableMixin.ensureNotTerminatedSync (package:http2/src/error_handler.dart:33:13)
#8      StreamHandler._processStreamFrameInternal (package:http2/src/streams/stream_handler.dart:509:12)
#9      StreamHandler.processStreamFrame (package:http2/src/streams/stream_handler.dart:469:7)
#10     Connection._handleFrameImpl (package:http2/src/connection.dart:368:16)
#11     Connection._setupConnection.<anonymous closure>.<anonymous closure> (package:http2/src/connection.dart:155:34)
#12     Connection._catchProtocolErrors (package:http2/src/connection.dart:300:9)
#13     Connection._setupConnection.<anonymous closure> (package:http2/src/connection.dart:155:7)
#14     StackZoneSpecification._registerUnaryCallback.<anonymous closure>.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart:129:26)
#15     StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)
#16     StackZoneSpecification._registerUnaryCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart:129:14)
#17     _rootRunUnary (dart:async/zone.dart:1132:38)
#18     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#19     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
#20     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
#21     _DelayedData.perform (dart:async/stream_impl.dart:584:14)
#22     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:700:11)
#23     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:660:7)
#24     StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)
#25     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart:119:48)
#26     _rootRun (dart:async/zone.dart:1120:38)
#27     _CustomZone.run (dart:async/zone.dart:1021:19)
#28     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#29     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
#30     StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)
#31     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart:119:48)
#32     _rootRun (dart:async/zone.dart:1124:13)
#33     _CustomZone.run (dart:async/zone.dart:1021:19)
#34     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#35     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
#36     _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#37     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#38     _runPendingImmediateCallback (dart:isolate/runtime/libisolate_patch.dart:113:13)
#39     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:166:5)

This leads to connection (and all its streams) closing.

IMO this second DataFrame shoud be discard. In the http2 spec I see:

However, after sending the RST_STREAM, the sending endpoint MUST be prepared to receive and
process additional frames sent on the stream that might have been sent by the peer prior to the arrival
of the RST_STREAM.

From grpc/grpc-dart#131 (comment):

There are still issue with the removal of the stream from StreamHandler._openStreams by streams_handler.dart . Once called the next incoming frame doesn't match any open stream and this leads to a protocol error. It looks like cancelled streams should also be tracked in this class.

Status?

Is there a status update on this package? Will it make it easy for me to transition my current HTTP Server code to HTTP2?

GoawayFrame puts http2 connection in a bad state

Spawned from dart-lang/appengine#117

package:appengine registers a single gcloud:DatastoreDB instance upon server startup, which triggers the following objects to be persisted as part of its object graph:

  • ↳ gcloud:DatastoreDB maintains a reference to a gcloud:Datastore
    • (implemented by appengine:GrpcDatastoreImpl)
  • appengine:GrpcDatastoreImpl maintains a reference to grpc:ClientChannel
  • grpc:ClientChannel maintains a reference to grpc:Http2ClientConnection
  • grpc:Http2ClientConnection maintains a reference to http2:ClientTransportConnection

I'm seeing cases where the following code is hitting:

} else if (frame is GoawayFrame) {
_streams.processGoawayFrame(frame);
_finishing(active: false);

This causes the connection's state to get marked as IsFinishing/PassiveFinishing. Then later, grpc tries to issue another request:

https://github.com/grpc/grpc-dart/blob/1213bc546b46da385afc94e645dd44fefcfb7f82/lib/src/client/http2_connection.dart#L130

And it results in the following error:

throw new StateError(
'The http/2 connection is finishing and can therefore not be used to '
'make new streams.');

Since the underlying http2 connection is retained by our server-wide connection to the datastore, every single subsequent HTTP request to my App Engine server that tries to talk to the datastore fails with this error ("gRPC Error (14, Error making call: Bad state: The http/2 connection is finishing and can therefore not be used to make new streams")

@jonasfj @mkustermann @sigurdm @a14n

Dart 2 VM runtime errors in transport_test

Testing with Dart 2.0.0-dev.56.0, I see that transport_test fails:

$ dart --preview-dart-2 test/transport_test.dart
00:00 +0: transport-test ping
00:00 +1: transport-test terminated-client-ping
00:00 +2: transport-test terminated-server-ping
00:00 +3: transport-test disabled-push
00:00 +4: transport-test enabled-push-100
00:04 +5: transport-test early-shutdown
00:04 +5 -1: transport-test early-shutdown [E]
  HTTP/2 error: Connection error: Connection is being forcefully terminated. (errorCode: 10)
  dart:async/stream_controller.dart 594:43                      _StreamController.addError
  package:http2/src/flowcontrol/stream_queues.dart 245:28       StreamMessageQueueIn.onTerminated
  package:http2/src/error_handler.dart 19:7                     _StreamMessageQueueIn&Object&TerminatableMixin.terminate
  package:http2/src/streams/stream_handler.dart 757:26          StreamHandler._closeStreamAbnormally
  package:http2/src/streams/stream_handler.dart 189:9           StreamHandler.onTerminated.<fn>
...

Eventsource

Hi! I'm trying to use this package for a SSE server behind a http2 proxy.
I'm able to connect to the server and get the first response but I'm unable to get the event's coming after the stream.incomingMessages only seems to return the values in the first response.

Any idea how I should solve this?

How to catch the exception thrown by 'ClientTransportConnection.terminate'?

my code is a little different from the big await loop in example:

final req = transport.makeRequest(
    headers.entries.map((e) => Header.ascii(e.key, e.value)).toList(growable: false),
    endStream: false,
  );
req.sendData(utf8.encode(body), endStream: true);
final incoming = req.incomingMessages.asBroadcastStream();
final dataStream = incoming.whereType<DataStreamMessage>().map((msg) => utf8.decode(msg.bytes));
final headerStream = incoming.whereType<HeadersStreamMessage>().expand((e) => e.headers);
final detecting = await dataStream.take(10).join('');
if (somecondition(detecting)) {
  await transport.terminate();
  return;
}
await for (final str in dataStream) {
}

but an exception is thrown when running 'somecondition' branch:

Unhandled exception:
HTTP/2 error: Connection error: Connection is being forcefully terminated. (errorCode: 0)

I added a try-catch and ,but it was not working:

try {
  await  transport.terminate();
} catch (e) {
}
await transport.terminate().catchError((e, s) {
}

How could I catch it?

[Question] Make a part of std

Hello there! Great work, I been waiting for the library for a long time. Just a question, are there any plans to make the library be part of sdk?

Migrate to null-safety

This issue can serve as a status check for people interested in the null safety release of this package.

http2 hung at await for stream.incomingMessages

in http2 demo there is such code to deal message;

var stream = transport.makeRequest(headers, endStream: true);
  await for (var message in stream.incomingMessages) {

but if the connection is closed after makeRequest, then the await will hung there;

http2 seems to be slow

Hi

I have tested speed of http2 library and it is much slower comparing to http. I tested with client and server being on my local. Server is written in Go and client in Dart. To downloaded 100mb file with HttpClient it takes 4s and http2 takes 15s.
I faced this issue with GRPC. We are downloading files with GRPC streams and speed depending on how far the server is, can be 8 times slower comparing to http download. I was trying to isolate the issue and it seems http2 package seems to be the main problem.
I have Dart VM version: 2.8.4
and tested with
http2: ^1.0.0
http: ^0.12.2

I used the code from "minimal example" to download file with http2. Is there a way to improve speed?

Dart 3 compatibility: some classes are used as mixins

These 3 classes should be changed to mixin, otherwise the usages marked below them are an error with Dart language version 3.0

Making a request with header name containing uppercase letter results in the stream termination

Package version: 1.0.0

Steps to reproduce:

  • Open ClientTransportConnection to e.g. https://www.google.com
  • Make a request containing an arbitrary header that has uppercase letter in its name:
    final transport = ClientTransportConnection.viaSocket(socket);
    final stream = transport.makeRequest(
      [
        Header.ascii(':method', 'GET'),
        Header.ascii(':path', '/'),
        Header.ascii(':scheme', uri.scheme),
        Header.ascii(':authority', uri.host),
        Header.ascii('A', 'a'),
      ],
    );
  • Start listening to response stream:
stream.incomingMessages.listen((message) {
  --- code from example here
}

Observation:

  • Listening immediately fails with error: HTTP/2 error: Stream error: Stream was terminated by peer (errorCode: 1).
  • Changing header name to lowercase (e.g. Header.ascii('a', 'a')) results in successful request.

It is not a pressing issue as header names are case-insensitive by the standard but it can take some time for a user to find out the cause.

Smarter logic for when to send window updates

There's already a TODO in window_handler.dart for this, but this just recently bit me. Basically we can get in a state in our application where if we have enough streams going there's not enough CPU available to read from the socket quick enough. The first symptom I noticed was increased memory usage in these cases. Investigating it further I found that I'm also sending a completely excessive amount of data while receiving a stream (20+ Mbps). After doing a quick 10 second wireshark to analyze the data it turned out that 330,654 of 356,707 packets were window_update packets. Given that each window_update packet is 67 bytes this amounts to the receiver sending 22MB worth of data over the network in 10 seconds. Many of these updates are less than 100 bytes and some are even 1 byte. This is clearly not efficient at all.

I'll give a bit more background on the application and use case here. This is a flutter cross platform (desktop and mobile) video streaming application that supports multiple streams of video. We use grpc-dart for communication which obviously uses this library. I've seen this on desktop and mobile. The only difference is mobile generally just takes less streams in order to hit this scenario. In the scenario mentioned above I was running the desktop version and streaming 8 video streams with decoding/display disabled just to rule out anything else using CPU and memory. The intended behavior here when we don't have enough CPU to receive all frames is the server side (our software) should start dropping frames based on how fast we're receiving them to slow down the amount of data being sent. I haven't yet checked to see if that's actually happening, but I suspect even if it were this issue might still prevent that from helping.

Sadly as I was just looking up the version to add here I realized we're using #98 and I looked at the changes and clearly those changes will cause more window_updates, but I don't necessarily think that's the root of the problem. I'm pretty sure those changes are valid and should be happening to keep the window size in sync anyway. I will do more testing without those changes (assuming everything still works without them).

Support for half-open connections

A tcp-socket can be closed in one direction (the client can close one direction after it is done sending the request, and still listen for more response).
We should support this in package:http2.

Currently this program:

import 'dart:io';
import 'dart:async';
import 'dart:isolate';
import 'package:http2/http2.dart';

client(port) async {
  Socket s = await Socket.connect('localhost', port);
  final ctc = ClientTransportConnection.viaSocket(s);
  final cts = ctc.makeRequest([Header.ascii("a", "b")]);
  
  cts.incomingMessages.listen(print);
  await Future.delayed(Duration(seconds: 2));
  print("closing client socket outgoing");
  s.close();
}

spawnClient(port) async {
  await Isolate.spawn(client, port);
}

main() async {
  ServerSocket ss = await ServerSocket.bind('localhost', 0);
  spawnClient(ss.port);
  Socket s = await ss.first;
  ServerTransportStream ts;
  print("Listening");
  new ServerTransportConnection.viaSocket(s).incomingStreams.listen((stream) {
    print("got stream");
    ts = stream;
  }, onDone: () {
    print("done");
    ts.sendHeaders([Header.ascii("foo", "bar")]);
  });
}

prints:

Listening
got stream
closing client socket
Unhandled exception:
Bad state: StreamSink is bound to a stream
#0      _StreamSinkImpl.close (dart:io/io_sink.dart:218:7)
#1      _Socket.close (dart:io/runtime/binsocket_patch.dart:1627:27)
#2      client (file:///usr/local/google/home/sigurdm/projects/blah/bin/http2sample.dart:14:5)
<asynchronous suspension>
#3      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:292:17)
#4      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
done
Unhandled exception:
Bad state: Cannot add event after closing
#0      _StreamController.add (dart:async/stream_controller.dart:585:24)
#1      _StreamSinkWrapper.add (dart:async/stream_controller.dart:858:13)
#2      TransportStream.sendHeaders (package:http2/transport.dart:145:10)
#3      main.<anonymous closure> (file:///usr/local/google/home/sigurdm/projects/blah/bin/http2sample.dart:32:8)
#4      _RootZone.runGuarded (dart:async/zone.dart:1302:10)
#5      _BufferingStreamSubscription._sendDone.sendDone (dart:async/stream_impl.dart:389:13)
#6      _BufferingStreamSubscription._sendDone (dart:async/stream_impl.dart:399:15)

Rename the main library to "http2.dart"

It's a strong convention among Dart packages to have a top-level library with the same name as the package (usually as the only top-level library). It's confusing that this package is named http2 but the top-level library is named transport.dart rather than http2.dart.

DATA frames received before remote receives RST_STREAM incorrectly ignored

Per RFC 7540, 5.1 Stream States:

  Flow-controlled frames (i.e., DATA) received after sending
  RST_STREAM are counted toward the connection flow-control window.
  Even though these frames might be ignored, because they are sent
  before the sender receives the RST_STREAM, the sender will
  consider the frames to count against the flow-control window.

Our usage is:

  1. gRPC over HTTP/2, DATA frames about 4k-8k, ~30/second (video)
  2. Cancel (terminate) is invoked on the stream causing RST_STREAM to be sent
  3. Several DATA frames are sent by server before it receives RST_STREAM
  4. Server reduces connection window size for sent frames (cumulative)
  5. Connection window eventually reduced to 0, so no more data can be sent
  6. Dart client ignores frames, so window will never be increased; appears to hang

Status and priority

Hi,
what's the status of HTTP2 (roadmap) ? Is anybody working on this and does it have priority in the Dart team? What can we expect in the next months? I would like to develop a POC in Dart instead Node.Js with the new capabilities of HTTP2. Thanks.

Add a `http.Client` compatible client

With the introduction of the cupertino_http and cronet_http only Windows and Linux Dart and Flutter apps can't use the benefits of HTTP2 while staying compatible with he abstract http.Client interface.
So it would be nice if this was added to this package.

Can't catch exception occur during goaway frame

Hello! Thanks for the lib). I ran into the following problem. When server send me goaway frame, StreamHandler class create following exception:

StreamException( id, 'Remote end was telling us to stop. This stream was not processed ' 'and can therefore be retried (on a new connection).')

This StreamException class is not provide outside the http2 lib. I can check this exception only if i directly import it:

import 'package:http2/src/sync_errors.dart'

But it is not cool providing class this way)
Maybe there is an any other way to check server send me goaway frame?

Don't use new function syntax

Commit e787697 uses the new function syntax which is broken on 1.24.2

This is breaking folks – ship 0.1.4+1 using typedefs.

Would be good to get tests running on Travis to validate things...

traffic from this package does not show up in DevTools

The traffic from this package does not show up in the DevTools networking page. Does this package need to be adopted to support DevTools or vice versa? We should update this issue with the expected work (or move to the flutter/devtools repo if we expect the bulk of the work to be done there).

CRLF injection using headers

Example code and a full description can be found here: https://github.com/n0npax/dart-test-http2

Basically, a header like the one below was accepted by the library and passed to the downstream service.

  var headers = [
// ...
    Header.ascii(':scheme', uri.scheme),
    Header.ascii('test0', "llama0\r\nHackiery: example.com"),
    Header.ascii('test1', "llama1"),
// ...
  ]
  var stream = transport.makeRequest(headers, endStream: true);

The request was interpreted by the server as:

Remote-Addr: 127.0.0.1
Host: localhost
Test0: llama0
Hackiery: example.com
Test1: llama1
Test2: llama2

As you can see Hackiery: example.com was interpreted as a new header.

This looks like a CRLF injection/header forgery issue.
I believe this is a security risk if the user can manipulate a header value.

How to check if ALPN contains h2?

like this

// getAlpnProtocols as List<String>
if (context.getAlpnProtocols.contains('h2')) {
  final server = await MultiProtocolHttpServer.bind(address, port ?? 443, context);
  // code ...
} else {
  final server = await HttpServer.bindSecure(address, port ?? 443, context);
  // code ...
}```

Publish 0.1.8+1

It looks like the latest Dart 2 updates for this haven't been published. I'm rolling from a commit hash for now.

how to set proxy ?

I have two questions:

  1. I don't know how to send data with post method.
  2. how to set proxy.
    Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.