Write HTTP clients & servers

What's the point?

  • Knowledge of asynchronous programming with futures and streamsis a prerequisite.
  • The HTTP protocol allows clients and servers to communicate.
  • The dart:io package has classes for writing HTTP programs.
  • Servers listen for requests on a host and port.
  • Clients send requests using an HTTP method request.
  • The http_server package provides higher-level building blocks.

HTTP (Hypertext Transfer Protocol) is a communication protocol usedto send data from one program to another over the internet.At one end of the data transfer is a serverand at the other end is a client.The client is often browser-based(either a user typing in a browser or a script running in a browser),but might also be a standalone program.

The server binds to a host and port (itmakes an exclusive connection to an IP address anda port number).Then the server listens for requests.Because of Dart’s asynchronous nature,the server can handle many requests at a single time,as follows:

  • Server listens
  • Client connects
  • Server accepts and receives request (and continues to listen)
  • Server can continue to accept other requests
  • Server writes response of request or several, possibly interleaved, requests
  • Server finally ends (closes) the response(s).

In Dart, the dart:io library containsthe classes and functions you need to write HTTPclients and servers. In addition, the packagecontains some higher-level classes that make it easier to writeclients and servers.

Important:Browser-based programs cannot use the dart:io library.

The APIs in the dart:io library work only with standalone, command-line programs. They do not work in the browser. To make HTTP requests from a browser-based client refer to the dart:html HttpRequest class.

This tutorial provides several examples that show how easyit is to write Dart HTTP servers and clients.Beginning with the hello world of servers,you learn how to write the code for a serverfrom binding and listening to responding to requests.You also learn about the client side: making differentkinds of requests (GET and POST),writing browser-based and command-line clients.

  • Get the Dart Tutorials
  • View the directory, which contains the sources you need for thistutorial.

Run the hello world server

Example file for this section:hello_world_server.dart.

Let’s begin with a small server that responds to all requestswith the string Hello, world!

At the command line, run the hello_world_server.dart script:

open_in_browserIn any browser, visit.The browser displays Hello, world!

In this case, the server is a Dart programand the client is the browser you used.However, you can write client programs in Dart—eithera browser-based client script, or a standalone program.

In the code for the hello world server,an HTTP server binds to a host and port,listens for HTTP requests, and writes a response.Note that the program importsthe dart:io library, which contains the HTTP-relatedclasses both for server-side programs and forclient-side programs (but not for web apps).

  1. import 'dart:io';
  2.  
  3. Future main() async {
  4. var server = await HttpServer.bind(
  5. InternetAddress.loopbackIPv4,
  6. 4040,
  7. );
  8. print('Listening on localhost:${server.port}');
  9.  
  10. await for (HttpRequest request in server) {
  11. request.response.write('Hello, world!');
  12. await request.response.close();
  13. }
  14. }

hello_world_server.dart

The next few sections cover server-side binding,making a client-side GET request,listening, and responding.

Binding a server to a host and port

Example for this section:

The first statement in main() uses HttpServer.bind() to create anHttpServer object and bind it to a host and port.

  1. var server = await HttpServer.bind(
  2. InternetAddress.loopbackIPv4,
  3. 4040,
  4. );

hello_world_server.dart

The code uses await to call the bind method asynchronously.

Hostname

The first parameter of bind() specifies the hostname.You can specify a particular hostname or IP address as a String.Alternatively, you can specify the host using these predefined valuesprovided by the class:

By default, when using a V6 internet address,a V4 listener is used as well.

Port

The second parameter to bind() is an integerthat specifies the port.The port uniquely identifies a service on the host computer.Port numbers below 1024 are reserved (except for 0)for standard services.For example, FTP data transfer typically runs on port 20,quote of the day on port 17, and HTTP on port 80.Your program should use port numbers from 1024 and higher.If the port is already in use,the connection for your server will be refused.

The server begins listening for HTTP requests using await for.For each request received, the code sends a “Hello, world!” response.

  1. await for (HttpRequest request in server) {
  2. request.response.write('Hello, world!');
  3. await request.response.close();
  4. }

hello_world_server.dart

You’ll learn more about what the HttpRequest object containsand how to write the response in the section.But first, let’s look at one way a client generates a request.

Example files for this section:number_thinker.dart and

This section features a command-line server thatrandomly chooses a number between 0 and 9.The client is a basic HTML web-page, make_a_guess.html,that you can use to guess the number.


Try it!

  • Run the number thinker server

At the command line, run the number_thinker.dart server.You should see something similar to the following:

  1. $ cd httpserver
  2. $ dart bin/number_thinker.dart
  3. I'm thinking of a number: 6
  • Launch the web server

Run webdev serve from the top directory of the app.

More information:webdev documentation

  • Open the HTML page

In a browser, go to.

  • Make a guess

Choose a number and press the Guess button.

The user makes a guess using a pull-down menu.


No Dart code is involved in the client.The client request is made from the browserto the Dart server through an HTML formwithin makea_guess.html,which provides an automatic way to formulate and send client HTTP requests.The form contains the pull-down list and the button.The form also specifies the URL, which includes the port number,and the kind of request (the _request method).It might also include elements that build a query string.

Here’s the form HTML from make_a_guess.html:

  1. <form action="http://localhost:4041" method="GET">
  2. <select name="q">
  3. <option value="0">0</option>
  4. <option value="1">1</option>
  5. <option value="2">2</option>
  6. <!-- ··· -->
  7. <option value="9">9</option>
  8. </select>
  9. <input type="submit" value="Guess">
  10. </form>

make_a_guess.html

Here’s how the form works:

  • The form’s action attribute is assigned theURL to send the request to.
  • The form’s method attribute definesthe kind of request, here a GET. Other commonkinds of request include POST, PUT, and DELETE.
  • Any element within the form that has a name, like the <select> element,becomes a parameter in the query string.
  • When pressed, the submit button (<input type="submit"…>) formulatesthe request based on the content of the form and sends it.

A RESTful GET request

REST (REpresentational State Transfer) is a set of principlesfor designing Web services.Well-behaved HTTP clients and servers observe the REST principlesdefined for GET requests.

A GET request:

  • only retrieves data
  • doesn’t change the state of the server
  • has length limits
  • can send query strings in the URL of the request

The client in this example makes a REST-compliant GET request.

Listening for and handling requests

Now that you’ve seen the browser-based client for this example,let’s take a look at the Dart code for the number thinker server,starting with main().

Once again the server binds to a host and port.Here, the top-level handleRequest() method is called for eachrequest received. Because HttpServer implements Stream,you can use await for to process the requests.

  1. import 'dart:io';
  2. import 'dart:math' show Random;
  3.  
  4. Random intGenerator = Random();
  5. int myNumber = intGenerator.nextInt(10);
  6.  
  7. Future main() async {
  8. print("I'm thinking of a number: $myNumber");
  9.  
  10. HttpServer server = await HttpServer.bind(
  11. 4041,
  12. );
  13. await for (var request in server) {
  14. handleRequest(request);
  15. }
  16. }

number_thinker.dart

When a GET request arrives, the handleRequest() method callshandleGet() to process the request.

  1. void handleRequest(HttpRequest request) {
  2. try {
  3. if (request.method == 'GET') {
  4. handleGet(request);
  5. } else {
  6. // ···
  7. }
  8. } catch (e) {
  9. print('Exception in handleRequest: $e');
  10. }
  11. print('Request handled.');
  12. }

number_thinker.dart

An object has many properties that provideinformation about the request.The following table lists some useful properties:

Using the method property

The code below from the number thinker example uses the HttpRequest methodproperty to determine what kind of request has been received.This server handles only GET requests.

number_thinker.dart

Typing a URL into a browser generates a GET request,which simply requests data from the specified resource.It can send a minimal amount of data along with the requestthrough a query string attached to the URI.

  1. void handleGet(HttpRequest request) {
  2. final guess = request.uri.queryParameters['q'];
  3. // ···
  4. }

number_thinker.dart

Use the uri property from the HttpRequest object to get a Uri objectthat contains the information about the URL typed by the user.The queryParameters property of the Uri object is a Map containingthe components of the query string.Refer to the desired parameter by name.This example uses q to identify the guessed number.

Setting the status code for the response

The server should set the status code to indicate the success orfailure of the request. Earlier you saw the number thinker setthe status code to methodNotAllowed to reject non-GET requests.Later in the code,to indicate that the request was successful and the response is complete,the number thinker server sets the HttpResponse status code to HttpStatus.ok.

  1. void handleGet(HttpRequest request) {
  2. final guess = request.uri.queryParameters['q'];
  3. final response = request.response;
  4. response.statusCode = HttpStatus.ok;
  5. // ···
  6. }

number_thinker.dart

and HttpStatus.methodNotAllowed aretwo of many predefined status codes in the class.Another useful predefined status code isHttpStatus.notFound (your classic 404).

In addition to statusCode,the HttpResponse object has other useful properties:

Writing the response to the HttpResponse object

Every HttpRequest object has a corresponding HttpResponse object.The server sends data back to the client through the response object.

Use one of the HttpResponse write methods(write(), writeln(), writeAll(), or writeCharCodes())to write the response data to the HttpResponse object.Or connect the HttpResponse object to a stream via addStreamand write to the stream.Close the object when the response is complete.Closing the HttpResponse objectsends the data back to the client.

  1. void handleGet(HttpRequest request) {
  2. // ···
  3. if (guess == myNumber.toString()) {
  4. response
  5. ..writeln('true')
  6. ..writeln("I'm thinking of another number.")
  7. ..close();
  8. // ···
  9. }
  10. }

number_thinker.dart

Making a POST request from a standalone client

Example files for this section:basic_writer_server.dart and

In the hello world and number thinker examples,the browser generated simple GET requests.For more complex GET requests and other kinds of requests, suchas POST, PUT, or DELETE,you need to write a client program, of which there are two kinds:

  • A standalone client program, which uses the HttpClientclass from dart:io.

  • A browser-based client, which uses API from This tutorial does not cover browser-based clients.To look at code for a browser-based client andrelated server, seenote_client.dart,and note_taker.html.

Let’s look at a standalone client, basic_writer_client.dart,and its server basic_writer_server.dart.The client makes a POST requestto save JSON data to a server-side file.The server accepts the request and saves the file.


Try it!

Run the server and client on the command line.

  • First, run the server:
  1. $ cd httpserver
  2. $ dart bin/basic_writer_server.dart
  • In a new terminal, run the client:
  1. $ cd httpserver
  2. $ dart bin/basic_writer_client.dart
  3. Wrote data for Han Solo.

Look at the JSON data that the server wrote to file.txt:

  1. {"name":"Han Solo","job":"reluctant hero","BFF":"Chewbacca","ship":"Millennium Falcon","weakness":"smuggling debts"}

The client creates an HttpClient object and uses thepost() method to make the request.Making a request involves two Futures:

  • The post() method establishes a networkconnection to the server and completes with the first Future,which returns an HttpClientRequest object.

  • The client composes the request object and closes it.The close() method sends the request to the serverand returns the second Future, which completes withan HttpClientResponse object.

  1. import 'dart:io';
  2. import 'dart:convert';
  3.  
  4. String _host = InternetAddress.loopbackIPv4.host;
  5. String path = 'file.txt';
  6.  
  7. Map jsonData = {
  8. 'name': 'Han Solo',
  9. 'job': 'reluctant hero',
  10. 'BFF': 'Chewbacca',
  11. 'ship': 'Millennium Falcon',
  12. 'weakness': 'smuggling debts'
  13. };
  14.  
  15. Future main() async {
  16. HttpClientRequest request = await HttpClient().post(_host, 4049, path) /*1*/
  17. ..headers.contentType = ContentType.json /*2*/
  18. ..write(jsonEncode(jsonData)); /*3*/
  19. HttpClientResponse response = await request.close(); /*4*/
  20. await utf8.decoder.bind(response /*5*/).forEach(print);
  21. }

basic_writer_client.dart

  • The post() method requires the host, port, and the path to the requestedresource. In addition to post(), the class providesfunctions for making other kinds of requests, including postUrl(),get(), and open().

  • An HttpClientRequest object has an object, whichcontains the request headers. For some headers, like contentType,HttpHeaders has a property specific to that header. For other headers, usethe set() method to put the header in the HttpHeaders object.

  • The client writes data to the request object using write(). The encoding,JSON in this example, matches the type specified in the ContentTypeheader.

  • The close() method sends the request to the server and, when complete,returns an object.

  • The UTF-8 response from the server is decoded. Use a transformer defined inthe dart:convert library to convert the data into regular Dart stringformat.

Similar to GET requests, REST provides guidelines for POST requests.

A POST request:

  • creates a resource (in this example, a file)
  • uses a URI that has a structure similiar to file and directory pathnames;for example, the URI has no query string
  • transfers data as JSON or XML
  • has no state and does not change the state of the server
  • has no length limits

The client in this example makes REST-compliant POST requests.

To see client code that makes REST-compliant GET requests,look at It’s a standalone client for the number thinker serverthat makes periodic guesses until it guesses correctly.

Example files for this section:basic_writer_server.dart and

An HttpRequest object is a stream of byte lists (Stream<List<int>>).To get the data sent from the client,listen for data on the HttpRequest object.

The basic_writer_server.dart file implementsa server that follows this pattern.

basic_writer_server.dart

  • The request has an HttpHeaders object. Recall that the client set thecontentType header to JSON (application/json). This server rejectsrequests that are not JSON-encoded.

  • A POST request has no limit on the amount of data it can send and the datamight be sent in multiple chunks. Furthermore, JSON is UTF-8, and UTF-8characters can be encoded over multiple bytes. The join() method puts thechunks together.

  • The data sent by the client is JSON formatted. The server decodes it usingthe JSON codec available in the dart:convert library.

  • The URL for the request is. The codereq.uri.pathSegments.last extracts the file name from the URI:file.txt.

A note about CORS headers

If you want to serve clients that are running on a different origin(a different host or port), you need to add CORS headers.The following code,take from note_server.dart,allows POST and OPTIONS requests from any origin.Use CORS headers with caution,because they can open your network up to security risks.

  1. void addCorsHeaders(HttpResponse response) {
  2. response.headers.add('Access-Control-Allow-Origin', '*');
  3. response.headers
  4. .add('Access-Control-Allow-Methods', 'POST, OPTIONS');
  5. response.headers.add('Access-Control-Allow-Headers',
  6. 'Origin, X-Requested-With, Content-Type, Accept');
  7. }

note_server.dart

For more information, refer to Wikipedia’s articleCross-origin resource sharing.

Using the http_server package

Example files for this section: andstatic_file_server.dart.

For some higher-level building blocks,we recommend that you try thepub package,which contains a set of classes that,together with the HttpServer class in the dart:io library,make it easier to implement HTTP servers.

In this section, we compare a server writtenusing API only from dart:io to a serverwith the same functionalitywritten using dart:io together with http_server.

You can find the first server in mini_file_server.dart.It responds to all requests by returning the contents of theindex.html file from the web directory.

Try it!

  • Run the server on the command line:
  1. $ cd httpserver
  • Type localhost:4044 into the browser.The server displays an HTML file:

The file served by mini_file_server.dart.

Here’s the code for mini file server:

  1. File targetFile = File('web/index.html');
  2. Future main() async {
  3. var server;
  4. try {
  5. server = await HttpServer.bind(InternetAddress.loopbackIPv4, 4044);
  6. } catch (e) {
  7. print("Couldn't bind to port 4044: $e");
  8. exit(-1);
  9. }
  10. await for (HttpRequest req in server) {
  11. if (await targetFile.exists()) {
  12. print("Serving ${targetFile.path}.");
  13. req.response.headers.contentType = ContentType.html;
  14. try {
  15. await req.response.addStream(targetFile.openRead());
  16. } catch (e) {
  17. print("Couldn't read file: $e");
  18. exit(-1);
  19. }
  20. } else {
  21. print("Can't open ${targetFile.path}.");
  22. req.response.statusCode = HttpStatus.notFound;
  23. }
  24. await req.response.close();
  25. }
  26. }

mini_file_server.dart

This code determines whether the file exists,and if it does, opens the file and pipes the contentsto the HttpResponse object.

The second server, whose code you can find inuses the http_server package.

Try it!

  • Run the server on the command line:
  1. $ cd httpserver
  2. $ dart bin/basic_file_server.dart
  • Type into the browser.The server displays the same index.html file as the previous:

In this server, the code for handling the request is much shorter,because the VirtualDirectory class handles the details of serving the file.

  1. import 'dart:io';
  2. import 'package:http_server/http_server.dart';
  3.  
  4. File targetFile = File('web/index.html');
  5.  
  6. Future main() async {
  7. VirtualDirectory staticFiles = VirtualDirectory('.');
  8.  
  9. var serverRequests =
  10. await HttpServer.bind(InternetAddress.loopbackIPv4, 4046);
  11. await for (var request in serverRequests) {
  12. staticFiles.serveFile(targetFile, request);
  13. }
  14. }

basic_file_server.dart

Here, the requested resource, index.html, is served bythe serveFile() method in the VirtualDirectory class.You don’t need to write code to open a file and pipe its contentsto the request.

Another file server, static_file_server.dart,also uses the http_server package.This server serves any file from the server’s directoryor subdirectory.

Run static_file_server.dart, and test it with the URL.

Here is the code for static_file_server.dart.

  1. import 'dart:io';
  2. import 'package:http_server/http_server.dart';
  3.  
  4. Future main() async {
  5. var staticFiles = VirtualDirectory('web');
  6. staticFiles.allowDirectoryListing = true; /*1*/
  7. staticFiles.directoryHandler = (dir, request) /*2*/ {
  8. var indexUri = Uri.file(dir.path).resolve('index.html');
  9. staticFiles.serveFile(File(indexUri.toFilePath()), request); /*3*/
  10. };
  11.  
  12. var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 4048);
  13. print('Listening on port 4048');
  14. await server.forEach(staticFiles.serveRequest); /*4*/
  15. }

static_file_server.dart

  • Allows clients to request files within the server’s directory.

  • An anonymous function that handles requests for the directory itself, thatis, the URL contains no filename. The function redirects these requests toindex.html.

  • The serveFile method serves a file. In this example, it servesindex.html for directory requests.

  • The serveRequest method provided by the VirtualDirectory class handlesrequests that specify a file.

Using https with bindSecure()

Example for this section:hello_world_server_secure.dart.

You might have noticed that the HttpServer class defines amethod called bindSecure(), which provides a secure connectionusing HTTPS (Hyper Text Transfer Protocol with Secure Sockets Layer).To use the bindSecure() method, you need a certificate,which is provided by a Certificate Authority (CA).For more information about certificates refer to

For illustrative purposes only,the following server, hello_world_server_secure.dart,calls bindSecure() usinga certificate created by the Dart team for testing.You must provide your own certificates for your servers.

  1. import 'dart:io';
  2.  
  3. String certificateChain = 'server_chain.pem';
  4. String serverKey = 'server_key.pem';
  5.  
  6. Future main() async {
  7. var serverContext = SecurityContext(); /*1*/
  8. serverContext.useCertificateChain(certificateChain); /*2*/
  9. serverContext.usePrivateKey(serverKey, password: 'dartdart'); /*3*/
  10.  
  11. var server = await HttpServer.bindSecure(
  12. 'localhost',
  13. 4047,
  14. serverContext, /*4*/
  15. );
  16. print('Listening on localhost:${server.port}');
  17. await for (HttpRequest request in server) {
  18. request.response.write('Hello, world!');
  19. await request.response.close();
  20. }
  21. }

hello_world_server_secure.dart

  • Optional settings for a secure network connection are specified in aSecurityContext object. There is a default object,SecurityContext.defaultContext, that includes trusted root certificates forwell-known certificate authorities.

  • A file containing the chain of certificates from the server certificate upto the root of the signing authority, in PEM format.

  • A file containing the (encrypted) server certificate private key, in

  • The context argument is required on servers, optional for clients. If it isomitted, then the default context with built-in trusted roots is used.

What next?

  • If you haven’t yet tried the server-side codelab,try writing a server app.
  • links to resources for writing standalone Dart applications,including servers.