Skip to content
This repository has been archived by the owner on Feb 11, 2024. It is now read-only.

Commit

Permalink
add support for user added headers in http requests (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
unsuitable001 authored Aug 17, 2021
1 parent db50ed6 commit 3870b22
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.5

* Added support for adding user specified headers in `HttpClientRequest`.

## 0.0.4+4

* Added support for 32 bit architecture.
Expand Down
1 change: 1 addition & 0 deletions lib/cronet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export 'src/exceptions.dart';
export 'src/http_client.dart';
export 'src/http_client_request.dart' hide HttpClientRequestImpl;
export 'src/http_client_response.dart' hide HttpClientResponseImpl;
export 'src/http_headers.dart' hide HttpHeadersImpl;
export 'src/quic_hint.dart';
23 changes: 16 additions & 7 deletions lib/src/http_client_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'exceptions.dart';
import 'globals.dart';
import 'http_callback_handler.dart';
import 'http_client_response.dart';
import 'http_headers.dart';
import 'third_party/cronet/generated_bindings.dart';

/// HTTP request for a client connection.
Expand Down Expand Up @@ -61,6 +62,8 @@ abstract class HttpClientRequest implements io.IOSink {

/// The uri of the request.
Uri get uri;

HttpHeaders get headers;
}

/// Implementation of [HttpClientRequest].
Expand All @@ -70,6 +73,8 @@ class HttpClientRequestImpl implements HttpClientRequest {
final Pointer<Cronet_Engine> _cronetEngine;
final CallbackHandler _callbackHandler;
final Pointer<Cronet_UrlRequest> _request;
final _requestParams = cronet.Cronet_UrlRequestParams_Create();
late final HttpHeadersImpl _headers;

/// Holds the function to clean up after the request is done (if nessesary).
///
Expand Down Expand Up @@ -97,18 +102,18 @@ class HttpClientRequestImpl implements HttpClientRequest {
: _callbackHandler =
CallbackHandler(wrapper.SampleExecutorCreate(), ReceivePort()),
_request = cronet.Cronet_UrlRequest_Create() {
_headers = HttpHeadersImpl(_requestParams);
// Register the native port to C side.
wrapper.RegisterCallbackHandler(
_callbackHandler.receivePort.sendPort.nativePort, _request.cast());
}

// Starts the request.
void _startRequest() {
final requestParams = cronet.Cronet_UrlRequestParams_Create();
if (requestParams == nullptr) throw Error();
if (_requestParams == nullptr) throw Error();
// TODO: ISSUE https://github.com/dart-lang/ffigen/issues/22
cronet.Cronet_UrlRequestParams_http_method_set(
requestParams, _method.toNativeUtf8().cast<Int8>());
_requestParams, _method.toNativeUtf8().cast<Int8>());
wrapper.InitSampleExecutor(_callbackHandler.executor);
final cronetCallbacks = cronet.Cronet_UrlRequestCallback_CreateWith(
wrapper.addresses.OnRedirectReceived.cast(),
Expand All @@ -122,7 +127,7 @@ class HttpClientRequestImpl implements HttpClientRequest {
_request,
_cronetEngine,
_uri.toString().toNativeUtf8().cast<Int8>(),
requestParams,
_requestParams,
cronetCallbacks,
wrapper.SampleExecutor_Cronet_ExecutorPtr_get(_callbackHandler.executor)
.cast());
Expand All @@ -138,13 +143,14 @@ class HttpClientRequestImpl implements HttpClientRequest {
_callbackHandler.listen(_request, () => _clientCleanup(this));
}

/// Returns [Future] of [HttpClientResponse] which can be listened for server
/// response.
/// Closes the request for input.
///
/// Throws [UrlRequestError] if request can't be initiated.
/// Returns [Future] of [HttpClientResponse] which can be listened to the
/// server response. Throws [UrlRequestError] if request can't be initiated.
@override
Future<HttpClientResponse> close() {
return Future(() {
_headers.isImmutable = true;
_startRequest();
return HttpClientResponseImpl(_callbackHandler.stream);
});
Expand Down Expand Up @@ -235,4 +241,7 @@ class HttpClientRequestImpl implements HttpClientRequest {
write(object);
write('\n');
}

@override
HttpHeaders get headers => _headers;
}
50 changes: 50 additions & 0 deletions lib/src/http_headers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';

import 'package:ffi/ffi.dart';

import 'globals.dart';
import 'third_party/cronet/generated_bindings.dart';

/// Headers for HTTP requests.
///
/// In some situations, headers are immutable:
/// [HttpClientRequest] have immutable headers from the moment the body is
/// written to. In this situation, the mutating methods throw exceptions.
///
/// For all operations on HTTP headers the header name is case-insensitive.
///
/// To set the value of a header use the `set()` method:
///
/// ```dart
/// request.headers.set('Content-Type',
/// 'application/json');
/// ```
abstract class HttpHeaders {
/// Sets the header [name] to [value].
void set(String name, Object value);
}

/// Implementation of [HttpHeaders].
class HttpHeadersImpl implements HttpHeaders {
final Pointer<Cronet_UrlRequestParams> _requestParams;
bool isImmutable = false;

HttpHeadersImpl(this._requestParams);

@override
void set(String name, Object value) {
if (isImmutable) {
throw StateError('Can not write headers in immutable state.');
}
final header = cronet.Cronet_HttpHeader_Create();
cronet.Cronet_HttpHeader_name_set(header, name.toNativeUtf8().cast());
cronet.Cronet_HttpHeader_value_set(
header, value.toString().toNativeUtf8().cast());
cronet.Cronet_UrlRequestParams_request_headers_add(_requestParams, header);
cronet.Cronet_HttpHeader_Destroy(header);
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.

name: cronet
version: 0.0.4+4
version: 0.0.5
homepage: https://github.com/google/cronet.dart
description: Experimental Cronet dart bindings.

Expand Down
49 changes: 49 additions & 0 deletions test/http_headers_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

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

import 'package:cronet/cronet.dart';
import 'package:test/test.dart';

const host = 'localhost';
const sentData = 'Hello, world!';

void main() {
group('Header Tests', () {
late HttpClient client;
late io.HttpServer server;
late int port;
setUp(() async {
client = HttpClient();
server = await io.HttpServer.bind(io.InternetAddress.anyIPv6, 0);
port = server.port;
server.listen((io.HttpRequest request) {
request.response.write(request.headers.value('test-header'));
request.response.close();
});
});

test('Send an arbitrary http header to the server', () async {
final request = await client.getUrl(Uri.parse('http://$host:$port/'));
request.headers.set('test-header', sentData);
final resp = await request.close();
final dataStream = resp.transform(utf8.decoder);
expect(dataStream, emitsInOrder(<Matcher>[equals(sentData), emitsDone]));
});

test('Mutating headers after request.close throws error', () async {
final request = await client.getUrl(Uri.parse('http://$host:$port/'));
await request.close();
expect(
() => request.headers.set('test-header', sentData), throwsStateError);
});

tearDown(() {
client.close();
server.close();
});
});
}

0 comments on commit 3870b22

Please sign in to comment.