dox_http_native 3.0.0-alpha.7
dox_http_native: ^3.0.0-alpha.7 copied to clipboard
Fast native HTTP/1.1 server for Dart and the Dox framework. Rust (hyper) parses HTTP, your handlers stay in Dart. Implements the dart:io HttpServer API.
dox_http_native #
Native HTTP/1.1 server for Dart. It is the default HTTP server of Dox.
Part of Dox, a web backend framework for Dart, like Laravel. Source code: github.com/dartondox/dox-http-native.
Install #
You usually do not add this package. dox_core depends on it and uses it by default.
Add it only to use the server without Dox:
dart pub add dox_http_native
Dox 3.x is in alpha (3.0.0-alpha.x). The API can still change before the stable release.
How it works #
- Rust (hyper) owns the sockets and parses HTTP/1.1.
- Your handlers stay in Dart.
- It implements the
dart:ioHttpServer,HttpRequestandHttpResponseAPI. Code written fordart:ioworks unchanged.
The native library #
A build hook (hook/build.dart) gets the Rust library. It tries these in order:
- A path you set in your app
pubspec.yaml(hooks: user_defines: dox_http_native: lib_path: ...). - A ready-built library from the GitHub release, checked with sha256. There are builds for macOS (arm64, x64), Linux (x64, arm64) and Windows (x64).
- A build from source with
cargo, when Rust is installed.
So on these systems you do not need Rust.
Use dart run, dart test or dart build cli. dart compile exe does not run build hooks.
Use it #
import 'dart:io';
import 'package:dox_http_native/dox_http_native.dart';
void main() async {
NativeHttpServer server = await NativeHttpServer.bind(3000);
server.listen((HttpRequest req) {
req.response.write('Hello World');
req.response.close();
});
}
Dox always runs on this server. When the native library does not load, Dox stops at start and prints why (eg. an ABI mismatch) and how to fix it.
WebSocket #
server.listen((HttpRequest req) async {
if (!NativeWebSocket.isUpgradeRequest(req)) return; // a normal request
WebSocket ws = await NativeWebSocket.upgrade(
req,
protocol: 'chat', // optional subprotocol
maxMessageSize: 1024 * 1024,
);
ws.listen((dynamic message) => ws.add('echo: $message'));
});
How it works:
- Rust marks a request that can upgrade. Your Dart code runs first, like
for any request: routing, middleware, the
Origincheck. Answer with a normal response (eg.403) to refuse it. NativeWebSocket.upgradeaccepts it. Rust (tokio-tungstenite) sends the101and speaks the WebSocket protocol: ping and pong, the close handshake and the UTF-8 check of text.- A message or frame over
maxMessageSizebytes closes the socket with1009. Dart never gets it. - Compression (permessage-deflate) is never agreed.
- Dart gets whole messages: a
Stringfor text, aUint8Listfor binary. They come in the same batches as requests. NativeWebSocketimplements thedart:ioWebSocketAPI.
Features #
- Each isolate can bind the same port (
SO_REUSEPORTon Linux, one shared socket on macOS). - Each isolate has its own Rust network thread.
DOX_HTTP_THREAD=sharedputs them on one thread. 400for bad requests,431for headers over 16 KB,413for bodies overmaxBodySize(maxMultipartSizefor a multipart form). A too bigContent-LengthwithExpect: 100-continuegets413before the body is sent. Any other too big body is read and dropped first, so the client sees the413.408when the client sends no body bytes forbodyTimeout(idle time, default 120 seconds). A slow but steady upload is fine.bind(port, address: '127.0.0.1')listens on one host. With no address it listens on all interfaces.close()is graceful: no new connections, open requests get their answer, then it completes.close(force: true)closes every connection at once.- WebSocket upgrade with
NativeWebSocket.upgrade, see above.detachSocketis not supported. - On a graceful
close(), every open WebSocket gets close code1001(going away). - Not yet: streaming a response while it is written. The body is sent on
close.
Documentation #
License #
MIT. See LICENSE.