Monday, November 12, 2012

Using Dart Sessions to Pass Information

‹prev | My Chain | next›

I ran into a bit of a problem with my Dart-based web server yesterday. It is not a huge problem, but one that I would like to solve. When serving up files from a “public” directory, I am forced to make duplicate file lookups:
#import('dart:io');

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

  app.addRequestHandler(
    (req) {
      if (req.method != 'GET') return false;

      String path = publicPath(req.path);
      if (path == null) return false;

      return true;
    },
    (req, res) {
      var file = new File(publicPath(req.path));
      var stream = file.openInputStream();
      stream.pipe(res.outputStream);
    }
  );
  app.listen('127.0.0.1', 8000);
}
In the first addRequestHandler() function, I use publicPath() to decide if this handler should handle the incoming request (if the file exists in the public directory). In the second function, I use the same publicPath() function to locate and read the file from the filesystem when responding to the request.

As I said, this is not a huge deal, but that publicPath() function ultimately calls a blocking existsSync() function:
String publicPath(String path) {
  if (pathExists("public$path")) return "public$path";
  if (pathExists("public$path/index.html")) return "public$path/index.html";
}

boolean pathExists(String path) => new File(path).existsSync();
I do not really mind one blocking lookup, but two? I could build some sort of global cache or variable to hold that information, but first I would like to some way to communication between matcher and responder in addRequestHandler.

I tried setting that information in the request headers yesterday, but Dart treats request heads as immutable. That seems wrong to me—I ought to be able to modify and manipulate header information on the server. But the Dart server is still relatively young, so I won't complain too much.

The only other thing that I can think to try is setting this information in the session. And that does the trick:
  app.addRequestHandler(
    (req) {
      if (req.method != 'GET') return false;

      String path = publicPath(req.path);
      if (path == null) return false;

      req.session().data = {'path': path};
      return true;
    },
    (req, res) {
      var file = new File(req.session().data['path']);
      var stream = file.openInputStream();
      stream.pipe(res.outputStream);
    }
  );
With that, I can again request files in my public directory and get 404s for files that are not there:
➜  public git:(app.dart) ✗ curl -i http://localhost:8000/
HTTP/1.1 200 OK
set-cookie: DARTSESSID=ce6e22679b7f6efdcbc6801def50a76d
transfer-encoding: chunked

<!DOCTYPE html>
<html>
<head>
  <title>Dart Comics</title>
...

➜  public git:(app.dart) ✗ curl -i http://localhost:8000/scripts/web/main.dart
HTTP/1.1 200 OK
set-cookie: DARTSESSID=8296f6c2e7ecbff7f3e2d2a973a610dc
transfer-encoding: chunked 

#import('Collections.Comics.dart', prefix: 'Collections');
#import('Views.Comics.dart', prefix: 'Views');
#import('Views.AddComic.dart', prefix: 'Views');

#import('dart:html');
#import('dart:json');
...

➜  public git:(app.dart) ✗ curl -i http://localhost:8000/asdf
HTTP/1.1 404 Not Found
content-length: 0
I do note (though I do not need it now) that Dart is creating a session ID for me—in case I needed anything in that session upon later requests.

Ultimately, that works, but is overkill for my server. Since the Dart VM is single threaded, I could just as easily use a function global variable to store the current path. Still, it is good to know that I have one way to transfer information to from route matcher to responder.



Day #568

No comments:

Post a Comment