@@ -110,6 +110,15 @@ sub build_handler ($controller, $destination)
110110 }
111111 }
112112
113+ # NOTE: this method will not send the response if we already sent or if
114+ # the response is not ready. It does not check whether the context is
115+ # consumed altogether, so a manually consumed context with a non-ready
116+ # response will not send anything, likely rendering some kind of error
117+ # page (but not 404). Currently, a PAGI error is raised, informing
118+ # about app returning without sending respnose.
119+ # NOTE: this needs to be here, since we want to use $send from this context
120+ await $ctx -> try_send_res;
121+
113122 # if this is a bridge and bridge did not render, it means we are
114123 # free to go deeper. Avoid first match, as it was handled already
115124 # above
@@ -214,6 +223,10 @@ Scripts must be called using C<pagi-server> (or other PAGI-specific software).
214223Running the script using C<perl > does nothing, as the application cannot run
215224itself - it will be built, but it will not set up a webserver.
216225
226+ C<pagi-server > can be obtained separately from L<PAGI::Server> module.
227+ Thunderhorse does not automatically include PAGI::Server as its dependency,
228+ since it is not coupled with any specific PAGI server implementation.
229+
217230=back
218231
219232=head2 The thunderhorse script
@@ -388,29 +401,29 @@ Return value of the destination sub is by default sent to the requestor as
388401C<text/html > with status code C<200 > . This is a common and handy shortcut, but
389402it is equally easy to do something else. Take the following destination example:
390403
391- async sub send_custom ($self, $ctx)
404+ async sub build_custom ($self, $ctx)
392405 {
393- await $ctx->res->text('Plaintext response');
406+ $ctx->res->text('Plaintext response');
394407 return 'this will not get rendered';
395408 }
396409
397- This takes response (L<Thunderhorse::Response> ) from context, and sends
398- plaintext manually. This action I<consumes > the context, marking it as
399- finished. In this case, return value of the destination is ignored. Note that
400- the await call on C<< ->text >> method is mandatory.
410+ This takes response (L<Thunderhorse::Response> ) from context, and sets
411+ plaintext body manually. This action I<consumes > the context, marking it as
412+ finished. In this case, return value of the destination is ignored.
413+
401414
402415Another example:
403416
404- sub send_custom2 ($self, $ctx)
417+ sub set_custom2 ($self, $ctx)
405418 {
406- $ctx->res->status(400)-> content_type('text/plain');
407- return 'this is rendered as plaintext and status 400 ';
419+ $ctx->res->content_type('text/plain');
420+ return 'this is rendered as plaintext';
408421 }
409422
410423This time, the return value of the destination is not ignored, since only
411424setting response metadata does not cause the context to be consumed. Status and
412425I<Content-Type > header will not be overridden, so the response will be sent as
413- plaintext. In this case, there is no need to await anything.
426+ plaintext.
414427
415428While not very common, a destination can be unimplemented when C<to > is
416429skipped. Unimplemented locations will be "stepped over" during request
@@ -523,10 +536,10 @@ bridge is created when you call C<add> on the result of another C<add>:
523536
524537When C</admin/users > is requested, both C<check_admin > and C<list_users > will
525538be called in sequence. The bridge destination receives the same arguments as
526- regular destinations. If the bridge consumes the context (by sending a
527- response), further matching stops. Otherwise, the next matching location is
528- called. For this reason, bridge destinations should return C<undef > explicitly
529- to avoid consuming the context by accident:
539+ regular destinations. If the bridge consumes the context, further matching
540+ stops. Otherwise, the next matching location is called. For this reason, bridge
541+ destinations should return C<undef > explicitly to avoid consuming the context
542+ by accident:
530543
531544 sub check_admin ($self, $ctx)
532545 {
@@ -644,7 +657,7 @@ would expect.
644657One unique feature of Thunderhorse is that it does not stop searching for
645658matches once it finds a match. Instead, it gathers a list of matching locations
646659and then proceeds to execute them in order. It stops once one of the handlers
647- consumes the context, which is usually done by sending a response. If no
660+ consumes the context, which is usually done by setting a response body . If no
648661handlers consumed the context, a I<404 Not Found > error page is rendered.
649662
650663This allows for superb flexibility, but has a couple of interesting side
@@ -765,6 +778,30 @@ matching. If we let C<important_auth> run before C<login_page>, for example by
765778setting its order to C<-2 > , it will effectively become a bridge for
766779C<login_page > .
767780
781+ Currently, the context can be consumed by:
782+
783+ =over
784+
785+ =item * Setting the response body in L<Thunderhorse::Context/res>
786+
787+ =item * Setting the response status to one of the statuses which does not require a body
788+
789+ =item * Closing a websocket connection in L<Thunderhorse::Context/ws>
790+
791+ =item * Closing a sse connection in L<Thunderhorse::Context/sse>
792+
793+ =item * Manually sending any response via PAGI, triggering C<response_started > in C<pagi.connection > scope key
794+
795+ =item * Manually consuming the context via L<Thunderhorse::Context/consume> call
796+
797+ =back
798+
799+ This system should be pretty bulletproof, however once you use the last option
800+ and call L<Thunderhorse::Context/consume> , all safety measures are off - it's
801+ now your responsibility to make sure the route handler will render something
802+ eventually. If it doesn't, you will get a low-level PAGI exception and an error
803+ page completely bypassing any Thunderhorse error rendering. Use with caution.
804+
768805=head2 Controllers
769806
770807By default, all routes defined in the application's C<build > method belong to
@@ -1286,7 +1323,7 @@ This hook's method B<cannot be declared on a controller level>.
12861323The C<on_error > hook is called when an exception occurs during request
12871324processing.
12881325
1289- This hook should consume the context by sending a response. The default handler
1326+ This hook should consume the context by setting a response. The default handler
12901327calls L</render_error> method a text page with an error message.
12911328
12921329=head3 Overriding system methods
@@ -1311,7 +1348,7 @@ following things:
13111348
13121349=item * tries to set C<Content-Type > header to C<text/html > (if it was not set already)
13131350
1314- =item * awaits sending C<$result > to the client using L<PAGI::Response/send> method (as text)
1351+ =item * sets C<$result > as the response body
13151352
13161353=back
13171354
@@ -1323,11 +1360,12 @@ references and render them as JSON/YAML.
13231360 async sub render_error($self, $ctx, $code, $message = undef) { ... }
13241361 async sub render_error($self, $controller, $ctx, $code, $message = undef) { ... }
13251362
1326- This method's default implementation sends a plain text response with code
1363+ This method's default implementation builds a plain text response with code
13271364C<500 > . The default implementation checks C<is_production > method of the
13281365application to avoid rendering the original error message which may contain
13291366sensitive information. It also acknowledges the existence of L<Gears::X::HTTP> ,
1330- which may change the error code to something else.
1367+ which may change the error code to something else. Original response is
1368+ discarded and a new one is built.
13311369
13321370=head2 Performance tuning
13331371
0 commit comments