Friday, November 9, 2012

Getting Started with Dart HTTP Servers

‹prev | My Chain | next›

One of my regrets from Dart for Hipsters is that the Dart Comics sample app requires node.js to run the backend. If I recall correctly, Dart's HttpServer did exist, but I opted to focus on the client side. Also, I really like express.js and especially node-dirty for quick and easy server-side storage.

I doubt that I want to re-implement node-dirty in Dart, but before I even consider that, I would like to play with the HTTP server. I start with a skeleton, just to make sure that I can start the server:
#import('dart:io');

main() {
  HttpServer app = new HttpServer();

  app.listen('127.0.0.1', 8000);
}
I fire that server up with dart app.dart and it seems to work. The command does not exit or crash. netstat confirms that port 8000 is occupied by a Dart process:
➜  scripts git:(M1) ✗ sudo netstat -nlp | grep 8000
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      25569/dart      
There is nothing on the server yet:
➜  scripts git:(M1) ✗ curl -i http://localhost:8000
HTTP/1.1 404 Not Found
content-length: 0
Looking at the method signature of addRequestHandler, I need to supply two functions. The first returns a boolean that informs the server if this handler handles a request. The second is the actual response.

For my first response, I supply a route matcher function that always returns true and reply with "Hello":
  app.addRequestHandler(
    (_) => true, 
    (req, res) {
      res.outputStream.writeString('Hello');
      res.outputStream.close();
    }
  );
And that actually seems to work:
➜  scripts git:(M1) ✗ curl -i http://localhost:8000
HTTP/1.1 200 OK
transfer-encoding: chunked

Hello%
Nice!

Next up, I make the route matcher a little smarter. Instead of matching everything, I only match to the root URL:
  app.addRequestHandler(
    (req) => req.path == '/',
    (req, res) {
      res.outputStream.writeString('Hello');
      res.outputStream.close();
    }
  );
Now, when I access the root URL, I get the "Hello" response and when I access something else, I get a 404:
➜  scripts git:(M1) ✗ curl -i http://localhost:8000
HTTP/1.1 200 OK
transfer-encoding: chunked

Hello%
➜  scripts git:(M1) ✗ curl -i http://localhost:8000/foo
HTTP/1.1 404 Not Found
content-length: 0

The last thing that I would like to try tonight is differentiating between HTTP methods. In this case, I would like to respond only to GET requests of the root URL. This should do the trick:
app.addRequestHandler(
    (req) => req.method == 'GET' && req.path == '/',
    (req, res) {
      res.outputStream.writeString('Hello');
      res.outputStream.close();
    }
  );
Indeed, this works exactly as desired. If I GET the root URL, I get my "Hello" response:
➜  scripts git:(M1) ✗ curl -v http://localhost:8000 
* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1... connected
> GET / HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8000
> Accept: */*
> 
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< 
* Connection #0 to host localhost left intact
* Closing connection #0
Hello%  
And, if I POST to the root URL, I get a 404:
➜  scripts git:(M1) ✗ curl -v -d '' http://localhost:8000
* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1... connected
> POST / HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8000
> Accept: */*
> Content-Length: 0
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 404 Not Found
< content-length: 0
* HTTP error before end of send, stop sending
<
* Closing connection #0
That is a good start with Dart HTTP servers. I don't think anyone is going to confuse app.addRequestHandler() + route matcher functions for express.js or Sinatra routes, but this is not too bad. Plus, it was easy to get up and running with this API and it certainly would not be hard for someone to build a slightly nicer API. For my needs, I will probably live with addRequestHandler() and focus on more important things. Like dirt simple backend data stores.


Day #565

No comments:

Post a Comment