w_transport 3.0.3

w_transport

Pub Build Status codecov.io documentation


Transport library for sending HTTP requests and opening WebSockets.

HTTP support includes plain-text, JSON, form-data, and multipart data, as well as custom encoding. Also supports automatic retrying and request & response interception.

WebSocket support includes native WebSockets in the browser and the VM with the option to use SockJS in the browser.

All transport classes are platform-independent and can be configured to work in the browser or on the Dart VM. Additionally, all transport classes can be mocked out and controlled through an API included with this library.


Docs & Help

Older Versions

Installing

As of version 3.0.0, w_transport will be following a versioning and stability commitment that guarantees a compatibility lifespan of two major versions.

If you're installing w_transport for the first time, simply depend on the latest major version and you'll get all patches and minor versions as they are released:

dependencies:
  w_transport: ^3.0.0

If you're upgrading from version 2.x, you can use the above version range without breaking any existing code. Check out the 3.0.0 upgrade guide.

Importing

The main entry point contains all of the transport classes necessary for sending HTTP requests and establishing WebSocket connections. It is also platform-independent (depends on neither dart:html nor dart:io), which means you can use it to build components, libraries, or APIs that will be reusable in the browser and on the Dart VM.

import 'package:w_transport/w_transport.dart' as transport;

We strongly recommend importing with the prefix transport because there are some classes whose names conflict with classes from the Dart SDK.

The end consumer will make the decision between browser and VM, most likely in a main() block.

Dart SDK

As of version 3.0.0 of the w_transport package, the minimum required Dart SDK version is 1.14.0 (released Jan 28, 2016).

Versioning and Stability

This library follows semver to the best of our interpretation of it. We want this library to be a stable dependency that’s easy to keep current. A good explanation of the versioning scheme that we intend to follow can be seen here from React.js:

https://facebook.github.io/react/blog/2016/02/19/new-versioning-scheme.html

In short: our goal is for every major release to be backwards compatible with the previous major version, giving consumers a lifespan of two major versions to deal with deprecations.

Credits

This library was influenced in many ways by the http package, especially with regard to multipart requests, and served as a useful source for references to pertinent IETF RFCs.

Development

This project leverages the dart_dev package for most of its tooling needs, including static analysis, code formatting, running tests, collecting coverage, and serving examples. Check out the dart_dev readme for more information.

To run integration tests, you'll need two JS dependencies for a SockJS server. Run npm install to download them.

Changelog

3.0.2

March 15th, 2017

  • Bug Fix: When a request is canceled, it will now always result in the done Future resolving. Previously it was possible for the done Future to never resolve if the request was canceled at a certain point during the request lifecycle.

3.0.2

February 3rd, 2017

  • Bug Fix: When MockTransports is installed with "fall-through" enabled, the optional SockJS params that are available on the new transport.WebSocket() constructor are now properly passed through. Previously, a standard WebSocket implementation would have been constructed erroneously if useSockJS: true was set, but it now correctly constructs the SockJS implementation.

3.0.1

January 18th, 2017

  • Bug Fix: If a request's autoRetry.test function is supplied and returns true when the response is null, a request that was canceled would erroneously be retried. Canceling a request now properly makes it ineligible for retry regardless of other factors.

3.0.0

October 25th, 2016

This major release includes deprecations and an increased minimum Dart SDK version. This release should be backwards compatible with version 2.x.

To upgrade, check out the v3.0.0 upgrade guide.

Features

Changes/Deprecations

  • Renamed entry points:

    • w_transport_browser.dart --> browser.dart
    • w_transport_mock.dart --> mock.dart
    • w_transport_vm.dart --> vm.dart
  • Other renames:

    • Client --> HttpClient
    • WSocket --> WebSocket
    • WSocketException --> WebSocketException
    • RetryBackOff.duration --> RetryBackOff.interval
  • Other deprecations:

    • WSocketCloseEvent
    • MockBaseRequest
    • MockClient
    • MockFormRequest
    • MockJsonRequest
    • MockPlainTextRequest
    • MockResponse
    • MockStreamedRequest
    • MockStreamedResponse
    • MockWSocket

Documentation

  • The w_transport documentation has been revamped with an emphasis on examples and guides. Check them out now!

2.9.4

October 14, 2016

  • Bug Fix: The Future returned from WSocket.cancel() no longer waits for the WebSocket to be closed.

2.9.3

September 8, 2016

  • Bug Fix: if a request is canceled right before it would also have exceeded the timeout threshold, a StateError may be thrown due to a Completer being completed more than once. This is fixed now.

2.9.2

August 11, 2016

  • Widen the version range for the http_parser dependency to speed up and/or fix downstream consumers experiencing version conflicts.

2.9.1

August 2, 2016

  • Bug Fix: previously, listening to a WSocket instance and then canceling the subscription before closing the socket would result in the "done" state never being reached. This is fixed now and the Future returned from WSocket.done and WSocket.close() will always resolve once the connection is closed.

2.9.0

July 26, 2016

  • Improvement: All request classes now have a bool isDone getter that can be read to determine whether or not a request is complete (i.e. succeeded, failed, or canceled).

  • Bug Fix: Calling request.abort() more than once will no longer throw a StateError.

2.8.0

July 21, 2016

  • Improvement: Mock transport handlers can now be canceled. This will allow consumers to remove HTTP or WebSocket mock handlers without having to call MockTransports.reset() or MockTransports.uninstall.

    var uri = Uri.parse('/example');
    
      var myHttpHandler = MockTransports.http.when(uri, (request) { ... });
      myHttpHandler.cancel();
    
      var myWebSocketHandler = MockTransports.webSocket.when(uri,
          handler: (protocols, headers) { ... });
      myWebSocketHandler.cancel();
    
      /// The same works for the `whenPattern()` methods, as well.

2.7.1

July 20, 2016

  • Bug Fix: previously, you could not retry a request that failed with a null response. This is now allowed, but still has the same default behavior. To retry a request with a null response, use the autoRetry.test method accordingly:

    var request = new Request();
      request.autoRetry
        ..enabled = true
        ..test = (request, response, willRetry) {
          if (response == null) return true;
          return willRetry;
        };

2.7.0

June 23, 2016

  • Deprecation: autoRetry.backOff.duration has been deprecated in favor of the more aptly named autoRetry.backOff.interval.

  • Improvement: Automatic request retrying will now add jitter to the backoff intervals by default. To disable jitter, set autoRetry.backOff.withJitter = false;.

  • Improvement: You can now put a cap on the backoff interval used during automatic request retrying.

    request.autoRetry.backOff.maxInterval = new Duration(minutes: 2);
  • Bug Fix: A request's encoding property can no longer be set to null. This would have most likely caused an RTE when the request was sent, so now an ArgumentError will be thrown immediately.

  • Bug Fix: As of 2.6.0, if you were to set a request's content-type manually without a charset or with an unknown charset, it was possible to hit an RTE due to a null encoding. The HttpBody class has been updated to be more resilient to a missing encoding or charset. Additionally, all request classes will now pass in the value of its encoding property, which should now always be non-null.

2.6.0

June 20, 2016

  • Improvement: The MockTransport utilities now support expecting and registering handlers for HTTP requests and WS connections that match a Pattern instead of exactly matching a URI. Handlers will also receive the Match instance.

    var response = new MockResponse.ok();
      var webSocket = new MockWSocket();
    
      var uriPattern = new RegExp('(http|ws)s:\/\/example.com\/(.*)');
    
      // Capture any GET request to example.com/
      MockTransports.http.expectPattern('GET', uriPattern, respondWith: response);
    
      // Register a handler for any GET request to https://example.com/
      // The `Match` instance will be given to the handler, where it can be used
      // to read any of the captured groups.
      MockTransports.http.whenPattern(uriPattern, (request, match) async {
        print('path: ${match.group(2)}');
        return response;
      }, method: 'GET');
    
      // Capture any WS connection attempt to example.com/
      MockTransports.webSocket.expectPattern(uriPattern, connectTo: webSocket);
    
      // Register a handler for an WS connection attempt to example.com/
      // The `Match` instance will be given to the handler, where it can be used
      // to read any of the captured groups.
      MockTransports.webSocket.whenPattern(uriPattern, handler:
          (uri, {protocols, headers, match}) async {
        print('path: ${match.group(2)}');
        return webSocket;
      });
  • Improvement: the content-type for HTTP requests can now be set manually.

    var request = new Request()
        ..uri = Uri.parse('/example')
        ..contentType =
            new MediaType('application', 'x-custom', {'charset': UTF8.name});
    • The content-type still has a default value based on the type of request (Request - text/plain, JsonRequest - application/json, etc.).

    • The content-type's charset parameter will still be updated automatically when you set the encoding, but once you manually set contentType, this behavior will stop. In other words, we are assuming that if you set contentType manually, you are intentionally overriding the defaults and are taking responsibility of setting the charset parameter appropriately.

  • Bug Fix: the StreamedRequest now properly verifies that the request has not been sent when setting contentType. It will now throw a StateError like the rest of the request types.

2.5.1

June 16, 2016

  • Error Messaging: When a response body cannot be properly decoded/encoded using the Encoding dictated by the content-type header, a ResponseFormatException will now be thrown with a much more descriptive message. The content-type, encoding, and body will be included.

2.5.0

June 15, 2016

  • Bug Fix: WSocket extends Stream and StreamSink, but was not fulfilling those contracts in all scenarios. In particular:

    • After obtaining a StreamSubscription instance from a call to WSocket.listen(), reassigning the onData(), onError(), and onDone() handlers had no effect.

      var webSocket = await WSocket.connect(...);
        var subscription = webSocket.listen((data) { ... });
      
        // This does nothing:
        subscription.onData((data) { ... });
        // Same goes for onError() and onDone()
    • A subscription to a WSocket instance did not properly respect pause and resume signals. This could produce a memory leak by buffering WebSocket events indefinitely.

    • A WSocket instance was immediately listening to the underlying WebSocket and buffering events from the underlying WebSocket until a listener was registered. This is not how a standard Dart Stream works.

    • The SockJS configuration was not properly handling the fact that the SockJS Client produces WebSocket events with a broadcast stream.

    • All of these issues have been addressed, and every WSocket instance should now behave exactly as a standard Stream and StreamSink would, regardless of the platform (VM, browser, SockJS, or mock).

The WSocketCloseEvent class has been deprecated. This class was only used internally and should not have been exported as a part of the public API.

The WSocket implementations are no longer registering an onError handler for the underlying WebSocket stream. If an error occurs on the server, it will not add the error to the stream, it will just close the connection. As a result, the MockWSocket.triggerServerError() method has been deprecated - use MockWSocket.triggerServerClose() instead.

2.4.0

May 4, 2016

  • Improvement: FormRequest now supports fields with multiple values.

    var request = new FormRequest()
        ..fields['multi'] = ['one', 'two'];
  • SDK Compatibility: Dart 1.16 exposed a new Client class from the dart:html library that conflicted with the Client class in this library. This has been fixed by adjusting our imports internally, but it may still affect consumers of this library.

  • Documentation: fixed inaccurate documentation around mocking & testing with WebSockets.

2.3.2

March 2, 2016

  • Bug Fix: requests created from a Client now properly inherit all of the autoRetry configuration. Previously the backOff, forTimeouts, and maxRetries settings were missing.

2.3.0

February 11, 2016

Features

  • Implemented retry back-off to allow fixed or exponential back-off between request retries. By default, there is no back-off.

    // Fixed back-off: 1 second between attempts.
      var request = new Request();
      request.autoRetry
        ..enabled = true
        ..backOff = new RetryBackOff.fixed(new Duration(seconds: 1));
    
      // Exponential back-off: 250ms, 500ms, 1s, 2s, etc (base*2^attempt)
      var request = new Request();
      request.autoRetry
        ..enabled = true
        ..backOff = new RetryBackOff.exponential(new Duration(milliseconds: 125));
  • Added methods to all request classes for manually retrying failures. This is mainly useful for corner cases where the request's success is dependent on something else and where automatic retrying won't help.

    var request = new Request();
      // send request, catch failure
    
      var response = await request.retry(); // normal
      var response = await request.streamRetry(); // streamed
  • Improved error messaging around failed requests. If automatic retrying is enabled, the error message for a failed request will include each individual attempt and why it failed.

2.2.0

February 8, 2016

Features

  • Added an autoRetry.forTimeouts flag (defaults to true) to the Client class and all request classes. This flag determines whether or not requests that are canceled due to exceeding the timeout threshold should be retried.

    // This request will retry if the timeout is exceeded.
      var request = new Request()
        ..timeoutThreshold = new Duration(seconds: 10)
        ..autoRetry.enabled = true;
    
      // This request will NOT retry if the timeout is exceeded.
      var request = new Request()
        ..timeoutThreshold = new Duration(seconds: 10)
        ..autoRetry.enabled = true
        ..autoRetry.forTimeouts = false;
  • Added a Duration sockJSTimeout config option to WSocket.connect().

    // browser only
      var socket = await WSocket.connect(Uri.parse('...'),
          useSockJS: true, sockJSTimeout: new Duration(seconds: 5));

2.1.0

January 7, 2016

Deprecation: SockJS global configuration

As of v2.0.0, this library could be configured to use SockJS under the hood when the WSocket class was used to establish WebSocket connections. This configuration occurred on a global basis (meaning it affected every WSocket instance) which is undesirable for applications with a mixed usage of native WebSockets and SockJS. This global configuration has been deprecated.

As of v2.1.0, passing useSockJS: true to the configureWTransportForBrowser() method will cause a deprecation warning to be printed to the console.

The SockJS configuration should now occur on a per-socket basis via the WSocket.connect() method:

Uri uri = Uri.parse('ws://echo.websocket.org');
WSocket webSocket = await WSocket.connect(uri,
   useSockJS: true, sockJSProtocolsWhitelist: ['websocket', 'xhr-streaming']);

Features

  • Added a baseUri field to Client that all requests from the client will inherit.

  • All request classes now support a timeout threshold via the timeoutTreshold field. This was also added to the Client class and all requests created from a client will inherit this value.

  • Request and response interception is now supported. This can be done directly on a request instance, but more usefully through a Client instance. See "request & response interception" and "intercepting requests & responses from a client" in the README.

  • All request classes and the Client class now include an API for automatic retrying via the autoRetry field. See "automatic request retrying" in the README.

  • Added a replace method to Response and StreamedResponse to allow simple creation of new responses based on another response, while changing only the fields you specify. This is particularly useful during response interception.

Bug Fixes

  • Headers passed into a request's dispatch method (ex: .get(headers: {...})) are now merged with any existing headers on the request (previously they were being ignored).

2.0.0

November 24, 2015

The 2.0.0 release is a major breaking release. While many of the patterns from 1.0.x were maintained, the HTTP API was broken up into several request classes and two response classes for a much more robust and useful API. As such, there is no backwards compatibility, but a migration guide is included below.

Features

  • WebSockets

    • Single API for the browser and the Dart VM.
    • Option to use SockJS library in place of native WebSockets for the ability to fall back to XHR streaming (configuration only, no API usage difference).
  • HTTP

    • Support for most commonly used request types:
      • Request (content-type: text/plain)
      • JsonRequest (content-type: application/json)
      • FormRequest (content-type: application/x-www-form-urlencoded)
      • MultipartRequest (content-type: multipart/form-data)
    • Synchronous access to response bodies as bytes, text, and JSON.
    • Asynchronous request bodies (StreamedRequest).
    • Asynchronous response bodies via streamGet(), streamPost(), etc. on any of the above request classes.
    • Automatic request encoding and response decoding.
  • Mocks

    • Because this library is designed to be platform-agnostic, it's easy to introduce mocks simply by treating tests as another platform, just like the browser or the Dart VM.
    • Import package:w_transport/w_transport_mock.dart and call configureWTransportForTest() to configure w_transport to use mock implementations for every class.
    • No changes necessary to your source code!
    • Utilize the MockTransports class to control WebSocket connections and HTTP requests.
  • Testing

    • A big initiative in this 2.0.0 release was to increase our test coverage - which we've done. With almost 1000 statements, w_transport has 99.7% coverage!
    • Since this library is concerned with transport protocols, it is imperative that our testing included rigorous integration tests. We have over 1000 integration tests that run in the browser and on the Dart VM against real servers.
    • Our test suites run against our mock implementations as well to ensure they are in parity with the real implementations.

Migration Guide

WRequest

The WRequest class attempted to cover all HTTP request use cases. Its closest analog now is Request - the class for sending plain-text requests. All other request classes share a similar base API with additional support for a specific type of request data (JSON, form, multipart, or streamed).

WResponse

The WResponse class made request meta data (status, headers) available as soon as the request had finished; however, in an attempt to unify the API between the dart:io HTTP requests and dart:html XHR requests, the response body was only available asynchronously (as a stream, an untyped future, or decoded to text). This meant two asynchronous steps were required for every request - one to get the response, and one to get the response body.

This has been greatly improved by switching to two different response classes:

  • Response - response meta data and body available synchronously
  • StreamedResponse - response meta data available synchronously, body available as a stream of bytes

1.0.1

June 23, 2015

Bug Fixes:

  • Allow request data to be set to null.
  • Canceling an in-flight request now properly results in the returned Future completing with an error.
  • Request data type validation now happens when sending the request instead of upon assignment, allowing intermediate data assignments.
  • Verify w_transport configuration has been set before constructing a WHttp instance.

1.0.0

May 21, 2015

  • Initial version of w_transport: a fluent-style, platform-agnostic library with ready to use transport classes for sending and receiving data over HTTP.

1. Depend on it

Add this to your package's pubspec.yaml file:

dependencies:
  w_transport: "^3.0.3"

2. Install it

You can install packages from the command line:

$ pub get

Alternatively, your editor might support pub. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:w_transport/w_transport.dart';

About

Transport library for sending HTTP requests and opening WebSockets. Platform-independent with builtin support for browser and Dart VM (even supports SockJS). Includes mock utilities for testing.

Author

Email clientplatform@workiva.com Workiva Client Platform Team
Email dustin.lessard@workiva.com Dustin Lessard
Email evan.weible@workiva.com Evan Weible
Email jay.udey@workiva.com Jay Udey
Email maxwell.peterson@workiva.com Max Peterson
Email trent.grover@workiva.com Trent Grover

Homepage

github.com/Workiva/w_transport

Documentation

www.dartdocs.org/documentation/w_transport/3.0.3/

Source code (hyperlinked)

www.crossdart.info/p/w_transport/3.0.3/

Uploader

jay.udey@workiva.com
evan.weible@workiva.com
maxwell.peterson@workiva.com

Share