[http] Design ideas for a request router
Hi list,
I've been designing a HTTP library for Boost inclusion for the last two
years. First it was funded as a GSoC 2014 project. Then I continued to
polish the implemented bits on my own and the library was peer reviewed
(and rejected) last year.
I'm currently addressing the requests made during last year's review and
one of the requested items was a higher-level interface built on top the
current building blocks that would be easier to use. One of such
abstractions would be an HTTP request router.
I usually do my research and check what others have done (not only in C++),
read a lot of spec and do a design on my own (sometimes I ask for feedback
of a few chosen ones). I rarely ask for public feedback because I usually
can solve the interface issues on my own. However, Boost.Http is a new
thing and I'm exploring an unexplored ground. I've been facing an issue for
the last months and I'd like to have "open" feedback of anyone interested
in this topic. Maybe it'll speed me up.
The issue I'm facing is that (1) I haven't found enough diversity of HTTP
request routers and (2) Boost.Http is a new thing and problems must be
approached differently (what other HTTP library will let you do HTTP over
uTP using Boost.Asio threading/async model?).
Detailing the problem a little more: I categorize current HTTP request
routers into two styles.
The "tree style" is the style where you declare some paths to be handled
and each request will be routed to at most one handler. You'll find this
style usually when the author advertise a ReST support library
(e.g.crow[1]).
The "chained style" is the style where you declare a chain of
AMDG On 03/14/2016 12:58 AM, Vinícius dos Santos Oliveira wrote:
Knowing this is Boost and Boost is about serious C++ users I know I must be concerned about performance. Therefore, many may not like the std::function<> approach. It can imply some hidden allocation that you pay even if the handler is not going to give up on the route.
[func.wrap.func.con]: "[Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f’s target is an object holding only a pointer or reference to an object and a member function pointer. — end note]" In Christ, Steven Watanabe
2016-03-14 15:28 GMT-03:00 Steven Watanabe
On 03/14/2016 12:58 AM, Vinícius dos Santos Oliveira wrote:
Knowing this is Boost and Boost is about serious C++ users I know I must
concerned about performance. Therefore, many may not like the std::function<> approach. It can imply some hidden allocation that you
be pay
even if the handler is not going to give up on the route.
[func.wrap.func.con]: "[Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f’s target is an object holding only a pointer or reference to an object and a member function pointer. — end note]"
I'm aware of that, but my solution is still cheaper. I think my solution end up being "cleaner" too (although is not complete yet and there is lots of open questions still). -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
one of the requested items was a higher-level interface built on top the current building blocks that would be easier to use. One of such abstractions would be an HTTP request router.
I usually do my research and check what others have done (not only in C++), read a lot of spec and do a design on my own (sometimes I ask for feedback of a few chosen ones). I rarely ask for public feedback because I usually can solve the interface issues on my own.
The "tree style" is the style where you declare some paths to be handled and each request will be routed to at most one handler. You'll find this style usually when the author advertise a ReST support library (e.g.crow[1]).
Tree is what vast majority of web frameworks do. Note that also Node.JS is "hip" today it does stuff in very unconventional way because it is JS and because it is deeply event driven (not always a good thing) Take a look what I do in CppCMS with URL mapping.
I thought I could combine and allow both styles. Actually, Boost.Http don't force you to use any style and both could be used.
I suggest do ONE thing but do it well.
Knowing this is Boost and Boost is about serious C++ users I know I must be concerned about performance. Therefore, many may not like the std::function<> approach. It can imply some hidden allocation that you pay even if the handler is not going to give up on the route.
Negligible - you should create routing + callbacks at the application start so it does not matter how much std::function allocates as long as you don't copy it on each request. Also note that in CppCMS I use a variant of function called "callback" that is reference counted and had much cheaper copy.
Another approach would be to turn the handlers into template functors. However, this would be too much burden on the user.
Please don't... Think about end users and compilation times.
Other APIs use a passive style and constantly check if there is network activity and schedule new socket reads. Boost.Asio is more explicit and Boost.Http follows along. An active style is useful because it'll allow you to defer new operations to later when your server is under heavy load. Anyway, I felt that maybe I'll need a "done" function and I've added an empty one for now. I'll see if I'll really need it later when I integrate everything together. For now, just know that the "done" function does nothing.
See... it is nice to have defer functionality for stuff like server-sent-events or another hip called web sockets. But vast majority of web api/web work need to do stuff synchronously...
Some points that I'll tackle LATER will be:
- A router is a handler too, then you can have nested routers.
It is actually must as usually you work as tree
- The router doesn't take the rule, only the handler. Later, I'll make algorithms that adapts rules and conditionally call your handler or pass the route to the next handler. This design is more flexible and I wish I could also use it in "tree style too".
I don't understand but sounds like bad idea - when you build routing tables it should be clear what path is mapped to what if you separate it in the code it would be messy ------------------------------ DISCLAIMER : I'm THE author of CppCMS [1] high performance C++ web framework I've been working on CppCMS for MANY years - and there still lots of work to do because web is virtually infinite field. Implementing fancy HTTP library that allows to map callbacks to URLs is about 1%-5% of actual stuff usable HTTP system needs. Basic HTTP libraries would always limit you because web is a huge field. I'd suggest take a look on any of existing C++ web frameworks that has a good user base and contribute to it as lots of work had already been done in very good way. And of course I mean - look at CppCMS that already solved huge amount of problems you are talking about. Artyom Beilis [1] http://cppcms.com/
2016-03-14 17:10 GMT-03:00 Artyom Beilis
Take a look what I do in CppCMS with URL mapping.
I will. Thanks for the advice.
I thought I could combine and allow both styles. Actually, Boost.Http don't
force you to use any style and both could be used.
I suggest do ONE thing but do it well.
I'm not building a megazord application. There will be many of them and the user will choose what it thinks it fits better. It's not like other frameworks that create a megazord class that will be its own executor containing its own thread pool being its own request provider and request router and so on and so on.
concerned about performance. Therefore, many may not like the std::function<> approach. It can imply some hidden allocation that you
Knowing this is Boost and Boost is about serious C++ users I know I must be pay
even if the handler is not going to give up on the route.
Negligible - you should create routing + callbacks at the application start so it does not matter how much std::function allocates as long as you don't copy it on each request.
For now I'm discussing the "chained style" and in this style the "next" argument will be passed around and around (it IS being to be copied). It's not like something that would be done at application startup and not modified again. Anyway, I implemented the "next" more like a search index than a type erased generic functor.
Another approach would be to turn the handlers into template functors.
However, this would be too much burden on the user.
Please don't... Think about end users and compilation times.
I don't like this approach either. I'm just mentioning it.
Other APIs use a passive style and constantly check if there is network
activity and schedule new socket reads. Boost.Asio is more explicit and Boost.Http follows along. An active style is useful because it'll allow you to defer new operations to later when your server is under heavy load. Anyway, I felt that maybe I'll need a "done" function and I've added an empty one for now. I'll see if I'll really need it later when I integrate everything together. For now, just know that the "done" function does nothing.
See... it is nice to have defer functionality for stuff like server-sent-events or another hip called web sockets.
But vast majority of web api/web work need to do stuff synchronously...
In no way I'm gonna remove flexibility. It's easier for you to see me implementing two interfaces (a "lower-level" and a "higher-level") than you seeing me making the API less useful (i.e. usable in less situations).
algorithms that adapts rules and conditionally call your handler or
- The router doesn't take the rule, only the handler. Later, I'll make pass
the route to the next handler. This design is more flexible and I wish I could also use it in "tree style too".
I don't understand but sounds like bad idea - when you build routing tables it should be clear what path is mapped to what if you separate it in the code it would be messy
It is more like:
static_router::make
DISCLAIMER : I'm THE author of CppCMS [1] high performance C++ web framework
And I'm glad I caught your attention. I may add that I'm a fan of the work done with the benchmarks of CppCMS: - http://cppcms.com/wikipp/en/page/benchmarks_php - http://cppcms.com/wikipp/en/page/benchmarks_all I also remember seeing other nice things like "taking cache to its limits", but it has been so much time since the last time I took a look. -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
concerned about performance. Therefore, many may not like the std::function<> approach. It can imply some hidden allocation that you
Knowing this is Boost and Boost is about serious C++ users I know I must be pay
even if the handler is not going to give up on the route.
Negligible - you should create routing + callbacks at the application start so it does not matter how much std::function allocates as long as you don't copy it on each request.
For now I'm discussing the "chained style" and in this style the "next" argument will be passed around and around (it IS being to be copied). It's not like something that would be done at application startup and not modified again.
Anyway, I implemented the "next" more like a search index than a type erased generic functor.
I suggest start from requirement i.e. what is needed - some samples and see how it works. To be honest copying callback isn't something that should be done - may be pass a reference/iterator to a next callback - but copy? Also remember that usually stuff that is being executed is rarely simple function but has lots of relations so... simple lambda isn't enough.
Other APIs use a passive style and constantly check if there is network
activity and schedule new socket reads. Boost.Asio is more explicit and Boost.Http follows along. An active style is useful because it'll allow you to defer new operations to later when your server is under heavy load. Anyway, I felt that maybe I'll need a "done" function and I've added an empty one for now. I'll see if I'll really need it later when I integrate everything together. For now, just know that the "done" function does nothing.
See... it is nice to have defer functionality for stuff like server-sent-events or another hip called web sockets.
But vast majority of web api/web work need to do stuff synchronously...
In no way I'm gonna remove flexibility. It's easier for you to see me implementing two interfaces (a "lower-level" and a "higher-level") than you seeing me making the API less useful (i.e. usable in less situations).
I don't tell remove flexibility (for the record CppCMS has both synchronous applications that run in a thread pool and asynchronous that live in event loop for defer style of operations) But I'd suggest don't take it to extreme as node.js does - remember C++ isn't JavaScript and nested handlers JS style with lambdas isn't such a good idea Artyom
2016-03-15 8:04 GMT-03:00 Artyom Beilis
I suggest start from requirement i.e. what is needed - some samples and see how it works. To be honest copying callback isn't something that should be done - may be pass a reference/iterator to a next callback - but copy?
Also remember that usually stuff that is being executed is rarely simple function but has lots of relations so... simple lambda isn't enough.
Previously you mentioned the "tree style" as the majority of what people need and the "chained style" as just a hip from NodeJS people. I intend to implement the "tree style" later (and I think I'll try to mirror the crow design a lot). For now, I think you're suggesting I should revisit how I design things and question every of my decisions again. I don't undestand what you're suggesting by what the "next" argument should be. I may certainly be misinterpreting a (large) part of what you're arguing. However, I need to do some effort and show that I need better clarification so we can understand each other. Things got complicated when I tried to think what should be the type of "next". If I build a static router (like the one I'm currently drafting), I just take a rule and a handler (whose type can be different each time) and create a new type out of it. The only reasonable answer I found was using a std::function<> or creating a custom http::function<> that would serve the same purpose (which I don't like). I thought about using an iterator instead next, but because each added handler will return a different router there isn't a single collection with multiple items and iterator would also need type erasure. I decided the type erased object would be statically allocated internally in each layer of the router (I can revisit this later) and you'd only pay for the indirection if needed to "reroute"/call-next. A plus point is that you'll only reserve the size you need (different than std::function which may store a few extra bytes to avoid heap allocation). Anyway I was still being puzzled about what signature the handler should have (doubts mainly provoked by the "next" argument). I'm concerned about providing some form of http::utils that would have lots of "ready" handlers (like file server, 404 not found...) and the signature must be consistent. The signature would require a message already and I thought I could just embedded the next iterator/function/type-erased-next-searcher into the message. This decision would allow me to share the same "handler algorithm/utility-function" within "chained routers" and "tree routers" because of compatible signatures (tree routers don't have a "next" argument). I guess this is confusing not only to me and I'll have to finish the whole set of routers before the next round of feedback. Anyway, I have pushed new commits into the router2 branch and I'd like to know what you guys think about this way of declaring a message_t: https://github.com/vinipsmaker/asiohttpserver/blob/router2/example/router.cp... I'll have lots of with_ types (e.g. with_session, with_date) that will move information from the headers to properly typed fields and do other things like the router embedded the searcher/next-handler into the message. All this is just to the user convenience and none will be required (you just pay for what you use). However I wanted an easy way to declare new message types and after the feedback of the brazilian C++ user groups I've come up with this API. What do you guys think about it? -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
Previously you mentioned the "tree style" as the majority of what people need and the "chained style" as just a hip from NodeJS people.
I intend to implement the "tree style" later (and I think I'll try to mirror the crow design a lot).
For now, I think you're suggesting I should revisit how I design things and question every of my decisions again.
I say two things: Take a look what widely used frameworks do (Django, Rails, Struts) and don't look at NodeJS as major reference as it is way to new... Tree is much more intuitive to use and understand - if you want to start from something do it first. You asked for an opinion? Right?
Anyway, I have pushed new commits into the router2 branch and I'd like to know what you guys think about this way of declaring a message_t: https://github.com/vinipsmaker/asiohttpserver/blob/router2/example/router.cp...
You have read way too much NodeJS code ;-) On more serious note: What is context of http::static_router::done() what it does? Where is the context? What is request? What is response? See having lambda for some router is cool but usually you need something more heavy - some context, session, request, response at least in general some class that does useful stuff with data. Think of a backed that handles data, for example how do you implement http://www.todobackend.com/ With multiple threads or event loop, with some stuff that actually does useful thing? I don't understand from you sample how stuff is connected to each other. Don't get me wrong - you are doing nice stuff - but please Keep It Simple and Clear. Don't force yourself to fancy paradigms that may be useful but aren't common. Regards, Artyom
2016-03-15 14:05 GMT-03:00 Artyom Beilis
I say two things:
Take a look what widely used frameworks do (Django, Rails, Struts) and don't look at NodeJS as major reference as it is way to new...
I think we're understanding each other now. =) Tree is much more intuitive to use and understand - if you want to
start from something do it first.
So easy that I can already kind of imagine some implementations. On a more serious note, I'll need to build one sooner anyway so I can see how I can provide utility handlers (e.g. not_found_404) that would work on both router styles. You asked for an opinion? Right?
Yes, and I apprciate you're spending your time to evaluate my designs. What is context of http::static_router::done() what it does?
For now it does nothing. The drafted router is very artifical right now. Thanks to Boost.Http exposing a socket (which follows Asio active style a lot) you need to explicitly ask for the next request. This responsibility is part of the handler, but the router intercepts the handlers (and it may be no viable handler). Therefore I thought it should be part of handler. However the router needs to know when the reply is done so it can ask for the next request. I'll know for sure later when the router is less artificial. Where is the context? What is request? What is response?
The context should be socket + input message. I'd like also to pass the reply as an object to the handler. Therefore I hope the handler to have a signature close to `void handler(Socket &socket, input_message_t &request, output_message_t &response)`. See having lambda for some router is cool but usually you need
something more heavy - some context, session, request, response at least in general some class that does useful stuff with data.
Agreed. This stuff can be embedded in the message. I plan to make the declaration of the message type something easy where you select what you'll need. Think of a backed that handles data, for example how do you
implement http://www.todobackend.com/ With multiple threads or event loop, with some stuff that actually does useful thing?
I don't understand from you sample how stuff is connected to each other.
A big part is my fault because I haven't put much documentation/comments on this prototype. For now it is not connected yet. It's just exposing a router that takes a string and choose a handler. This todobackend project looks an interesting thing I should take a look on. Thanks. Don't get me wrong - you are doing nice stuff - but please Keep It Simple
and Clear. Don't force yourself to fancy paradigms that may be useful but aren't common.
A simple example is still missing indeed. It was one of the most important complaints in the last year review. I'm working on it (an existing router is better than no router). -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
On Mon, Mar 14, 2016 at 5:10 PM, Artyom Beilis
I'd suggest take a look on any of existing C++ web frameworks that has a good user base and contribute to it as lots of work had already been done in very good way.
And of course I mean - look at CppCMS that already solved huge amount of problems you are talking about.
I believe that competition is really healthy here. CppCMS is dual LGPL3 and commercially licensed, according to the website you link. That reason alone, to me, justifies Vinicius' approach to provide a Boost Licensed library without any legal restrictions. However, on the technical side, I'm more on the idea that everything that can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all. If I were in Vinicius' shoes I'd make every little aspect configurable statically so the compiler and linker have everything they need to optimize HTTP code to the fullest, which is the rare case where users will resort to C++ for Web dev. I see no gain in using a dynamic HTTP solution that doesn't leverage extreme C++ performance possibilities. I'd rather stick to Node.js and use C++ modules where appropriate. My quarter cent, Madera [1] https://github.com/ipkn/crow
On 15/03/2016 15:58, Rodrigo Madera wrote:
However, on the technical side, I'm more on the idea that everything that can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all.
While compile time should be the preferred option, it still must be possible to modify routes (and other things) at runtime as well (even if that requires adding a "dynamic_route" rule at compile time or similar). The majority of cases will work fine with static routes, but dynamic ones are sometimes needed too, and can't be ignored.
On Tue, Mar 15, 2016 at 5:19 AM, Gavin Lambert
On 15/03/2016 15:58, Rodrigo Madera wrote:
However, on the technical side, I'm more on the idea that everything that can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all.
While compile time should be the preferred option, it still must be possible to modify routes (and other things) at runtime as well
There is another critical point. Compilation time - and I'm serious about it. Web development is very dynamic by its nature if you make too many things in "fancy-template-meta-programming-compile-time" you'll loose users. If you try to do all the stuff by GCC/Clang as Turing machine you'll end with horrible compilation times and unmaintainable code For example one of the reasons I use Boost.Asio very carefully due its header-only nature with almost impossible compilation times. Artyom
On Tue, Mar 15, 2016 at 7:41 AM, Artyom Beilis
There is another critical point.
Compilation time - and I'm serious about it. Web development is very dynamic by its nature if you make too many things in "fancy-template-meta-programming-compile-time" you'll loose users.
I don't think that Vinicius' Boost.HTTP tries to be a Web container of some sort, but rather a library used by such containers. That would encapsulate a lot of that dynamic nature into the next architecture layer. If users want C++ for web server development, you would think that they know the nature of C++ and the implications of wanting such optimized code. If not, then maybe provide a macro (something like BOOST_HTTP_FAST_COMPILATION) where optimization is not a goal, but rather compilation times. I don't know if this impacts greatly on the implementation though, or if it only concerns a small portion of the library.
If you try to do all the stuff by GCC/Clang as Turing machine you'll end with horrible compilation times and unmaintainable code
That would be encapsulated into a library, not user code. Indeed maintaining a library is no easy task.
For example one of the reasons I use Boost.Asio very carefully due its header-only nature with almost impossible compilation times.
Better to have high compilation times and very decent optimizes code than not to. But again, this is MHO. Madera
On Tue, Mar 15, 2016 at 12:19 AM, Gavin Lambert
On 15/03/2016 15:58, Rodrigo Madera wrote:
However, on the technical side, I'm more on the idea that everything that can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all.
While compile time should be the preferred option, it still must be possible to modify routes (and other things) at runtime as well (even if that requires adding a "dynamic_route" rule at compile time or similar).
I agree that runtime configuration is a primary concern. However, I beg to differ on privileging compile times over (possibly) more efficient code. If possible, make this a define so the user can choose at will. I'd rather invest in very good hardware and have very optimized code than to have small compile times and leave some paths sub optimal at runtime. I don't wish to be extreme, of course, but C++11 and up make this meta-programming burden quite faster. It won't always be possible to get that extra gain, but it's worth a try IMHO.
The majority of cases will work fine with static routes, but dynamic ones are sometimes needed too, and can't be ignored.
Agreed. Madera
On 16/03/2016 06:02, Rodrigo Madera wrote:
However, on the technical side, I'm more on the idea that everything that can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all.
While compile time should be the preferred option, it still must be possible to modify routes (and other things) at runtime as well (even if that requires adding a "dynamic_route" rule at compile time or similar).
I agree that runtime configuration is a primary concern. However, I beg to differ on privileging compile times over (possibly) more efficient code. If possible, make this a define so the user can choose at will.
To clarify, by "compile time" I meant "code evaluated during compile" or "static routing", not "the duration of compilation". ie. constexpr/template-based routing, which is what seemed to be under discussion.
On Tue, Mar 15, 2016 at 7:37 PM, Gavin Lambert
On 16/03/2016 06:02, Rodrigo Madera wrote:
However, on the technical side, I'm more on the idea that everything that
can be done at compile time, should be done at compile time. Handlers, even routes, should be handled in compile time. [1]. This is C++, after all.
While compile time should be the preferred option, it still must be possible to modify routes (and other things) at runtime as well (even if that requires adding a "dynamic_route" rule at compile time or similar).
I agree that runtime configuration is a primary concern. However, I beg to differ on privileging compile times over (possibly) more efficient code. If possible, make this a define so the user can choose at will.
To clarify, by "compile time" I meant "code evaluated during compile" or "static routing", not "the duration of compilation". ie. constexpr/template-based routing, which is what seemed to be under discussion.
Indeed, I misread your words. Sorry for the confusion. Anyway, we agree on that. Madera
Hi all, On Sun, Mar 13, 2016 at 11:58 PM, Vinícius dos Santos Oliveira <> wrote:
The "tree style" is the style where you declare some paths to be handled and each request will be routed to at most one handler. You'll find this style usually when the author advertise a ReST support library (e.g.crow[1]).
This is also the style we used in an in house server side library
meant to be used mainly for REST and WebSocket connections.
The routing is inspired from Python Flask and Python aiohttp [1] with
these features:
- the routes can be added and removed at runtime (and from other route handlers)
- we needed dynamic routes - moreover the dynamic part needed to match
multiple path segments and more "specific" route handlers needed to
take precedence over the more "generic" ones (example below)
- support for asynchronous (deferred) route handlers - to support the
cases when handling a request requires a lengthy operation.
- full access to request / response objects to offload as much logic
as possible from the core server library.
Example of specific / generic route handlers:
("POST", "/api/v1/
Hello everybody it is my first post here,
I am implementing a routing algorithm for boost, i intend to create a
"PATRICIA Tree", this is a reperesentation of the structure:
"/ip/%s/to_bin"N[1]
"/ip/%s/to_bin"N[10] --->
"/client/list"N[8]
"/ip/%s/to_bin" ---> "/ip/%s/to_dec" "/client/list/%i"
--> "/client/%i/select"N[11] --> "/client/%s/find" --> ... [ ]
"/client/%i/select" --> "/client/%d/update"
I believe that this algorithm has the following advantages:
1. Allocating and copying strings are not needed to create the structure
(because they are defined as "const char *");
2. Because of this algorithm it is possible to test each route from a
certain neighborhood without need to test all the routes, which means that
after the first character "/", in this case, if the current character is
'i', and it is accepted, there is not need of going to the "/client" side,
so the complexity of this algorithm is from the size of the key (in our
case an URL string), in comparison with a normal three algorithm, that
should be be "Log(N) * the_key_size", because the same key is tested
"Log(N)" times;
3. Seems it is possible to make some improvement, by ordering the
"neighbors": (at first it can be treated as a list, but some improvement
can be done here);
5. Once each character is verified separately it is possible to make some
"parsing" from the URL received from the user side (the URL searching
algorithm is actually a parsing);
4. This implementation considers "%i", "%d" and "%f" as the same, which
means that they will be placed in group,
but "%s" (which means an string before the character '/') or "%%" (which
means all the characters from the current position) are placed separately;
5. Seems it is possible to have a PATRICIA Tree "structure" completly made
of TMP because it don't need to split strings, but just mark the point
where some route is different from another (the problem maybe is splitting
a "node" vertically, which occurs when a node is "divided", or when the
tree grows);
6. The URL patterns "%d","%i","%f","%s","%%" are short;
7. The maximum routes per node is predefined, so, at least, in this point
of the code there is no dynamic allocation (no pointer to next).
This implementation is not complete, the class 'http_caller' is based on
CROW, and already has support to compile time type checking (that is also
my only concern about this, code because I'm somehow using their idea of
"numeric system", I've reading their code and understood how they did some
things, it is all commented in the source code). I'll be attempting to
integrate to the boost.http at the time I finish testing my code. Of course
you can choose never use it, but if you just consider I'll be very
satisfied.
My best regards,
Uisleandro Costa dos Santos
On Thu, Mar 17, 2016 at 2:00 PM, Sorin Fetche
Hi all,
On Sun, Mar 13, 2016 at 11:58 PM, Vinícius dos Santos Oliveira <> wrote:
The "tree style" is the style where you declare some paths to be handled and each request will be routed to at most one handler. You'll find this style usually when the author advertise a ReST support library (e.g.crow[1]).
This is also the style we used in an in house server side library meant to be used mainly for REST and WebSocket connections.
The routing is inspired from Python Flask and Python aiohttp [1] with these features: - the routes can be added and removed at runtime (and from other route handlers) - we needed dynamic routes - moreover the dynamic part needed to match multiple path segments and more "specific" route handlers needed to take precedence over the more "generic" ones (example below) - support for asynchronous (deferred) route handlers - to support the cases when handling a request requires a lengthy operation. - full access to request / response objects to offload as much logic as possible from the core server library.
Example of specific / generic route handlers: ("POST", "/api/v1/
/operation_a/") - handles some POST requests ("POST", "<url>/") - handles all the other POST requests regardless how many path segments (matches both "/a/" and "a/b/" and so on) [1] Python aiohttp: http://aiohttp.readthedocs.org/en/stable/web.html
Best regards, Sorin Fetche
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2016-03-20 19:06 GMT-03:00 Uisleandro
4. This implementation considers "%i", "%d" and "%f" as the same, which means that they will be placed in group,
I don't like the use of printf-style formatters. For one, you have to learn these letter and they're useless anyway (C++ has the necessary machinery to detect the used type). Another reason to avoid them is the easy-to-misuse/lack-of-planned-extensibility/unsafety they tend to acquire as they evolve. Besides the type, you might need formatting options and using printf this is text you put between the '%' sign and the type letter. Expressions start to get long and confuse and it's hard to tell when they end. It's way easier to use something like Python's {} where you know where are the beginning/end points (even if you have several formatting options). Another issue is localization, but it may be less important in this domain. Printf style formatting doesn't allow you to reorder arguments, which is very important in localization where different languages may use the arguments in different order. -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
On 22/03/2016 09:49, Vinícius dos Santos Oliveira wrote:
2016-03-20 19:06 GMT-03:00 Uisleandro
: 4. This implementation considers "%i", "%d" and "%f" as the same, which means that they will be placed in group,
I don't like the use of printf-style formatters.
For one, you have to learn these letter and they're useless anyway (C++ has the necessary machinery to detect the used type).
Another reason to avoid them is the easy-to-misuse/lack-of-planned-extensibility/unsafety they tend to acquire as they evolve. Besides the type, you might need formatting options and using printf this is text you put between the '%' sign and the type letter. Expressions start to get long and confuse and it's hard to tell when they end. It's way easier to use something like Python's {} where you know where are the beginning/end points (even if you have several formatting options).
Another issue is localization, but it may be less important in this domain. Printf style formatting doesn't allow you to reorder arguments, which is very important in localization where different languages may use the arguments in different order.
http://www.boost.org/doc/libs/1_60_0/libs/format/doc/format.html This encourages a clean syntax by default, although it does have printf-based underpinnings as well.
"%%" (which means all the characters from the current position) are placed separately;
This sort of thing is usually treated as an escape for an actual % in the resulting string; it seems like a bad idea to give this a different meaning. It also seems like a bad idea to use % in general in the context of URL strings, as this already has a defined (hex escape) meaning. While granted it would be rare to use unusual characters that require escapes in API-style endpoint addresses, there's no particular reason that someone should be prevented from doing so due to overloaded meanings.
2016-03-17 14:00 GMT-03:00 Sorin Fetche
This is also the style we used in an in house server side library meant to be used mainly for REST and WebSocket connections.
Thanks for sharing your experience, Sorin. The routing is inspired from Python Flask and Python aiohttp [1] with
these features: - the routes can be added and removed at runtime (and from other route handlers)
I wasn't considering changing the routes within a handler. Indeed it needs to be explicitly taken into account. It reminds me of the Boost.Signals library. - we needed dynamic routes - moreover the dynamic part needed to match
multiple path segments and more "specific" route handlers needed to take precedence over the more "generic" ones (example below)
I'd use the term overlapping rules in this case instead dynamic. - support for asynchronous (deferred) route handlers - to support the
cases when handling a request requires a lengthy operation. - full access to request / response objects to offload as much logic as possible from the core server library.
These don't seem hard to support/implement. -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
participants (7)
-
Artyom Beilis
-
Gavin Lambert
-
Rodrigo Madera
-
Sorin Fetche
-
Steven Watanabe
-
Uisleandro
-
Vinícius dos Santos Oliveira