BREAKING CHANGE: HTTP server request handling has changed

130 views
Skip to first unread message

Søren Gjesse

unread,
Apr 18, 2012, 9:23:39 AM4/18/12
to General Dart Discussion
I have just (r6683) landed a change that will bread existing use
of the HTTP server.

The HttpServer class used to have a setter called onRequest to
set the function that was called for all requests received. This setter
has been removed in favour of multiple handler registration.

Look for the new function addRequestHandler and new setter
defaultRequestHandler on the HttpServer class.

In existing code changing setter onRequest to setter
defaultRequestHandler is the only change required to make HTTP
server code work again, but think about using addRequestHandler.

For usage examples see the following test and sample:

  tests/standalone/src/io/HttpServerHandlerTest.dart
  samples/chat/chat_server_lib.dart

Regards,
Søren

Ladislav Thon

unread,
Apr 18, 2012, 9:35:21 AM4/18/12
to Søren Gjesse, General Dart Discussion
I have just (r6683) landed a change that will bread existing use
of the HTTP server.

The HttpServer class used to have a setter called onRequest to
set the function that was called for all requests received. This setter
has been removed in favour of multiple handler registration.

I've just seen the commit and I'd like to ask the community: do you think this is the right approach and the HTTP server should be more high-level, or should it stay low-level, allowing different kinds of abstraction to be built on top of it?

I've got one example in my mind right now: the approach chosen is "first matching handler wins", but I can easily imagine a scenario when all matching handlers should have a chance to contribute (one could, for example, collect statistics, one could provide common page decoration, and the last one will render the body). I can easily implement it, but then, I can easily implement the first strategy too, so...

I imagine that the idea here is to have something Sinatra-like baked right in the standard library, and while I have some sympathies for this, I also have doubts. What do other people think?

LT

Chris Buckett

unread,
Apr 18, 2012, 9:52:39 AM4/18/12
to Ladislav Thon, Søren Gjesse, General Dart Discussion
Hi Ladislav,

My feeling is that the core HttpServer should be a low level one.  The dart team and third parties could then provide higher levels of abstraction to serve different needs (such as Target and Crimson http servers).

The model that you mention around first-matching or contributing handlers is the model I've taken for my http server (CrimsonHttp) - blatantly taken from the sencha-connect server for node.js.  Also in node.js the even higher level sinatra-inspired "express" framework is built upon sencha-connect, and I imagine that these kind of libraries will also be created by third parties (and possibly also Google).

(I'm guessing that I'll need to fix up Crimson in  the light of this change  :)  

Cheers,
Chris.




2012/4/18 Ladislav Thon <lad...@gmail.com>

Alexander Orlov

unread,
Apr 18, 2012, 10:00:40 AM4/18/12
to mi...@dartlang.org, Søren Gjesse


On Wednesday, April 18, 2012 3:35:21 PM UTC+2, ladicek wrote:
I've just seen the commit and I'd like to ask the community: do you think this is the right approach and the HTTP server should be more high-level, or should it stay low-level, allowing different kinds of abstraction to be built on top of it?

Well, you still can implement an own HttpServer derivate using HttpConnection, ServerSocket etc. 

I played around with Go on App Engine and Go's mini server is using the same handler based approach. I guess the "best solution" depends on the kind of apps you want to build predominantly. With Dart we speak of a new class of (GWT-like) apps where the client just fetches static app code and communicates with the backend over XHRs. There will be no "common page decoration handler" because the page decoration will be part of the same (static) app code that will be fetched via the same static content handler.

That's just my idea, I could be wrong :)

Mads Ager

unread,
Apr 18, 2012, 10:25:06 AM4/18/12
to Alexander Orlov, mi...@dartlang.org, Søren Gjesse
Just to reiterate what Søren wrote: If you want to use the old
low-level behavior all you have to do is have only a default handler
and the interface is exactly the same as it used to be.

This was added as a convenience that you don't have to use. It does
not really change the low-level nature of the basic API in my mind.

We are still thinking about the way we want these interfaces to look,
so thanks for starting a discussion of this. We do want to keep the
low-level APIs. However, we are also starting to add higher-level APIs
in various places to help users who do not need the full flexibility
of the low level. The handler registration seemed like something a lot
of users would probably want to do in any case and it made sense to
have support for that.

Cheers, -- Mads

Eric J. Smith

unread,
Apr 18, 2012, 11:00:59 AM4/18/12
to mi...@dartlang.org, Søren Gjesse
I really hope you guys are looking at Node.js as THE platform to emulate, because it has been insanely popular for a reason.

Ladislav Thon

unread,
Apr 18, 2012, 11:06:43 AM4/18/12
to Alexander Orlov, mi...@dartlang.org, Søren Gjesse
I played around with Go on App Engine and Go's mini server is using the same handler based approach. I guess the "best solution" depends on the kind of apps you want to build predominantly. With Dart we speak of a new class of (GWT-like) apps where the client just fetches static app code and communicates with the backend over XHRs. There will be no "common page decoration handler" because the page decoration will be part of the same (static) app code that will be fetched via the same static content handler.

I still think of Dart as a general-purpose programming language suitable both for single-page, XHR-heavy apps and traditional web sites with just a sprinkle of ajax here and there. Dart is scalable, you see.

We are still thinking about the way we want these interfaces to look,
so thanks for starting a discussion of this. We do want to keep the
low-level APIs. However, we are also starting to add higher-level APIs
in various places to help users who do not need the full flexibility
of the low level.

I know that this is hard to do if you want to keep the API coherent and easy to use. In a lot of cases, it's best to split low-level and high-level API into different classes. Now this isn't case, I think -- I'd say that you did a good job API-wise (but it will probably have to evolve a bit, because in a lot of cases, you don't only want to match on path, but at least on the method+path combination). I only wanted to express my doubt about this specific change and whether it starts a new trend of adding higher-level stuff into core libraries (which I both welcome and worry about :-) ).

LT

Alexander Orlov

unread,
Apr 18, 2012, 11:25:03 AM4/18/12
to mi...@dartlang.org, Søren Gjesse


On Wednesday, April 18, 2012 5:00:59 PM UTC+2, Eric J. Smith wrote:
I really hope you guys are looking at Node.js as THE platform to emulate, because it has been insanely popular for a reason.


I hope not :) because popular != well architectured. There is also a reason why Google is building Dart as an alternative to the very popular JavaScript

Alexander Orlov

unread,
Apr 18, 2012, 11:28:05 AM4/18/12
to mi...@dartlang.org, Søren Gjesse


On Wednesday, April 18, 2012 5:25:03 PM UTC+2, Alexander Orlov wrote:
I hope not :) because popular != well architectured

Btw, popular != <a lot of things>  

Eric J. Smith

unread,
Apr 18, 2012, 11:53:31 AM4/18/12
to mi...@dartlang.org, Søren Gjesse
I completely understand popular != well architected, but IMO the reason why Node.js is so popular is because they got the server model right despite how poor JS the language is.  Also, making something that is similar to the Node.js model might ease the conversion process for the 10s of thousands of Node.js packages that have been built and the Dart community can only hope that people will convert some of them.

Also, I have to say that Node.js == simplicity.  Don't undervalue simplicity.  It is VERY easy to get caught up in "good architecture" and end up with a complex system that nobody cares to understand.

Also, speaking of architecture.  I think you guys need to standardize on an eventing pattern because I'm seeing several different patterns for the same things so far.

Sean Eagan

unread,
Apr 19, 2012, 10:51:49 PM4/19/12
to Eric J. Smith, mi...@dartlang.org, Søren Gjesse

Regarding RequestHandler, are you planning on adding other methods besides onRequest in the future ?  If not then I don't see why it exists.  You could instead just have:

void addRequestHandler(bool matcher(HttpRequest), void handler(HttpRequest, HttpResponse));
void set defaultHandler(void handler(HttpRequest, HttpResponse));

or possibly

typedef void RequestHandler(HttpRequest, HttpResponse);
//...
void addRequestHandler(bool matcher(HttpRequest), RequestHandler);
void set defaultHandler(RequestHandler);

That seems better than having `Object handler` parameters which don't really enforce anything.

Cheers,
Sean

Søren Gjesse

unread,
Apr 20, 2012, 4:11:43 AM4/20/12
to Sean Eagan, Eric J. Smith, mi...@dartlang.org
One of the reasons for introducing the interface RequestHandler was to make it easier to have libraries of request handlers. E.g. a FileRequestHandler class which can deal with all the details of serving files from disk. Providing these request handlers as classes instead of functions seemed to be the best solution. There are no immediate plans of extending the RequestHandler, but going forward having that option is convenient.

Regarding previous comments of splitting the code into two classes I was considering doing that but having either a sub class or a wrapper class of the HttpServer called WebServer which would have the method addRequestHandler and setter defaultHandler. For this change I decided against it, but as things evolve we might reconsider. We are very focused on keeping things as simple as possible but still with a bias against supporting the most common use cases.

Regards,
Søren

Ladislav Thon

unread,
Apr 20, 2012, 4:19:34 AM4/20/12
to Søren Gjesse, Sean Eagan, Eric J. Smith, mi...@dartlang.org
Regarding previous comments of splitting the code into two classes I was considering doing that but having either a sub class or a wrapper class of the HttpServer called WebServer which would have the method addRequestHandler and setter defaultHandler. For this change I decided against it, but as things evolve we might reconsider. We are very focused on keeping things as simple as possible but still with a bias against supporting the most common use cases.

Somewhere in the issue tracker, there is an proposal to merge query params and form params. It's probably convenient on the higher level, but I'd very much not like it on the low level. So moving the higher-level WebServer out from the low-level HttpServer will likely be a good idea in the future.

LT

Sean Eagan

unread,
Apr 20, 2012, 9:05:29 AM4/20/12
to Søren Gjesse, Eric J. Smith, mi...@dartlang.org
On Fri, Apr 20, 2012 at 3:11 AM, Søren Gjesse <sgj...@google.com> wrote:
One of the reasons for introducing the interface RequestHandler was to make it easier to have libraries of request handlers. E.g. a FileRequestHandler class which can deal with all the details of serving files from disk. Providing these request handlers as classes instead of functions seemed to be the best solution. There are no immediate plans of extending the RequestHandler, but going forward having that option is convenient.

My main concern is just with the `Object handler` arguments.  Since Dart doesn't support overloading, I think it's best to either only support one type of argument, or have two separate methods, one for each type.  Personally I think it would be best to just support Functions for now, and then you could always add RequestHandlers later by changing the argument's type back to Object, or by adding new methods.

Cheers,
Sean Eagan

Ladislav Thon

unread,
Apr 20, 2012, 9:28:53 AM4/20/12
to Sean Eagan, Søren Gjesse, Eric J. Smith, mi...@dartlang.org
My main concern is just with the `Object handler` arguments.  Since Dart doesn't support overloading, I think it's best to either only support one type of argument, or have two separate methods, one for each type.

Or add union types to the language :-) Yeah, I know, that's not gonna happen... but I've already came to this situation several times. Ability to pass different types of objects to one function leads to nicer APIs, and what is the purpose of having dynamically typed language when you don't want to use it? Supporting only one argument type or splitting the function into two separate ones looks like a terrible code smell to me. We will have to deal with this, one day. In the mean time, I "solve" it by documenting it extra-linguistically (i.e., in the documentation comment, not by the type annotations).

LT

Dirk Detering

unread,
Apr 20, 2012, 11:07:41 AM4/20/12
to Ladislav Thon, Sean Eagan, Søren Gjesse, Eric J. Smith, mi...@dartlang.org
Haven't closely followed the discussion, but would another alternative not be to make classes functions?

Imagine that every class that implements an 'apply' or 'call' method could be used as a function ...
(Somehow generalizing the 'runnable' concept).

2012/4/20 Ladislav Thon <lad...@gmail.com>

Eric J. Smith

unread,
Apr 20, 2012, 12:20:54 PM4/20/12
to mi...@dartlang.org
Here are some questions that come to mind when looking at the current code:

  1. Would I be able to have multiple request handlers process a single request?  How do you tell the server to continue on to the next handler?
  2. How can I plug bits and pieces of functionality into the request pipeline that may change the request or response data but not actually do the core request processing?  Say I want to add out of process session state or logging or some other feature to the pipeline.
  3. How would I be able to add additional custom information to the request and response objects?  Should they have a generic Map on them to allow us to add custom data to them?

Bob Nystrom

unread,
Apr 20, 2012, 12:36:01 PM4/20/12
to Dirk Detering, Ladislav Thon, Sean Eagan, Søren Gjesse, Eric J. Smith, mi...@dartlang.org
On Fri, Apr 20, 2012 at 8:07 AM, Dirk Detering <mail...@googlemail.com> wrote:
Haven't closely followed the discussion, but would another alternative not be to make classes functions?

Imagine that every class that implements an 'apply' or 'call' method could be used as a function ...
(Somehow generalizing the 'runnable' concept).

You are exactly right. Gilad's call operator proposal does just that: http://www.dartlang.org/articles/emulating-functions/.

This has a nice property in that it lets you have an API start simply using a function and be able to evolve it later into a full interface if you find yourself needing other operations.

- bob

Dirk Detering

unread,
Apr 20, 2012, 2:47:41 PM4/20/12
to Bob Nystrom, mi...@dartlang.org, Søren Gjesse, Sean Eagan, Ladislav Thon, Eric J. Smith

Wow, proposal from january, how could I have missed that?
Sound really good!

Ladislav Thon

unread,
Apr 20, 2012, 6:14:56 PM4/20/12
to Dirk Detering, Sean Eagan, Søren Gjesse, mi...@dartlang.org, Bob Nystrom, Eric J. Smith


> Wow, proposal from january, how could I have missed that?
> Sound really good!

AFAIK, this isn't a proposal anymore, it's already specified (albeit not implemented, but I hope this changes soon).

LT

Søren Gjesse

unread,
Apr 25, 2012, 10:04:10 AM4/25/12
to Ladislav Thon, Dirk Detering, Sean Eagan, mi...@dartlang.org, Bob Nystrom, Eric J. Smith
Based on the feedback we have decided to get rid of the RequestHandler interface (see r6939). It did't really add anything, so now the signatures for the addRequestHandler method and the defaultRequesthandler setter are:

  addRequestHandler(bool matcher(HttpRequest request),
                    void handler(HttpRequest request, HttpResponse response));

  void set defaultRequestHandler(
      void handler(HttpRequest request, HttpResponse response));

To use a class instance to handle a request the Dart auto closurerization works fine, e.g.

class Handler {
  void onRequest(HttpRequest request, HttpResponse response) {
    ...
  }
}

...

HttpServer server = ...

server.defaultRequestHandler = new Handler().onRequest;

Regards,
Søren

Eric J. Smith

unread,
Apr 25, 2012, 12:23:04 PM4/25/12
to mi...@dartlang.org
I never heard back on my questions.  I'm just wondering if these scenarios are supported and if you guys are thinking about them.  I haven't actually tested this, but right now it sounds like once a request handler match has been found, then the pipeline ends.  I guess I will grab latest and see how it actually works.

Ramón

unread,
May 7, 2012, 7:21:10 PM5/7/12
to mi...@dartlang.org
Hi,

So I've been playing around with sqljocky and was able to successfully query my MySQL database and have moved on to writing a simple HTTP server that displays the results.  I was following the sample at  http://www.dartlang.org/articles/io/#web-servers and of course it didn't work and it took me a while to find this discussion. So, I'm trying to get this working:

#import('dart:io');

main() {
  var server = new HttpServer();
  server.listen('127.0.0.1', 26570);
  server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
    response.outputStream.write('Hello, world'.charCodes());
    response.outputStream.close();
  };
}

using lynx as my client.  The 'Hello, world' text is not displayed until I kill the dart app.  What am I doing wrong?  Any help is appreciated.

Also, the doc http://api.dartlang.org/io/HttpServer.html#listen still references the removed onRequest method so that was confusing too until I found this thread.


On Wednesday, April 18, 2012 8:23:39 AM UTC-5, Søren Gjesse wrote:

Sam McCall

unread,
May 7, 2012, 7:38:57 PM5/7/12
to Ramón, mi...@dartlang.org
On Mon, May 7, 2012 at 4:21 PM, Ramón <ra.ro...@gmail.com> wrote:
Hi,

So I've been playing around with sqljocky and was able to successfully query my MySQL database and have moved on to writing a simple HTTP server that displays the results.  I was following the sample at  http://www.dartlang.org/articles/io/#web-servers and of course it didn't work and it took me a while to find this discussion. So, I'm trying to get this working:

#import('dart:io');

main() {
  var server = new HttpServer();
  server.listen('127.0.0.1', 26570);
  server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
    response.outputStream.write('Hello, world'.charCodes());
    response.outputStream.close();
  };
}

using lynx as my client.  The 'Hello, world' text is not displayed until I kill the dart app.  What am I doing wrong?  Any help is appreciated.
I just filed http://code.google.com/p/dart/issues/detail?id=2918 today, that might be the issue? lynx seems to use HTTP/1.0 and HttpServer isn't compliant.

Seth Ladd

unread,
May 7, 2012, 7:40:09 PM5/7/12
to Ramón, mi...@dartlang.org
Hi Ramon,

Apologies for this out of date document. I've just queued it up to get updated, I'll treat this as a high priority.  In the meantime, you'll probably find this very interesting: http://dartwatch.com/index.php/2012/05/dart-in-the-browser-dart-on-the-server-a-simple-http-example/

Meanwhile, I've tried your example below and it worked for me. I tried it with Chrome. Maybe there is something with lynx?

Seth

On Mon, May 7, 2012 at 4:21 PM, Ramón <ra.ro...@gmail.com> wrote:

Seth Ladd

unread,
May 7, 2012, 8:18:29 PM5/7/12
to Ramón, mi...@dartlang.org
FYI I've updated the article: http://www.dartlang.org/articles/io/ Also, I've filed http://code.google.com/p/dart/issues/detail?id=2934 to track the changes to the docs. Thanks again for the report, let us know if we can help with something else.

Ramón

unread,
May 8, 2012, 12:37:05 PM5/8/12
to mi...@dartlang.org, Ramón
On Monday, May 7, 2012 6:38:57 PM UTC-5, Sam McCall wrote:

I just filed http://code.google.com/p/dart/issues/detail?id=2918 today, that might be the issue? lynx seems to use HTTP/1.0 and HttpServer isn't compliant.
 

Sam, I applied the proposed patch in that issue and it works correctly now so that must have been the cause.

Seth, In regards to the article at http://www.dartlang.org/articles/io/ The second example under "Writing web servers" has been updated but the first one is still showing "server.onRequest".  And thanks for the link to that blog.  Looks like it will be a big help now that I can move on to the next steps.

Thanks for the help, guys!

Seth Ladd

unread,
May 8, 2012, 4:45:26 PM5/8/12
to Ramón, mi...@dartlang.org
Thanks Ramon. I've updated the article, and we've updated the API docs: http://api.dartlang.org/io/HttpServer.html  (btw we have app cache turned on for API docs, it might take a while for your docs to refresh. we'll add a UX component to make this more clear)
Reply all
Reply to author
Forward
0 new messages