=head1 NAME Warabe::App - A fundamental building block of server-side Web applications =head1 SYNOPSIS use Wanage::HTTP; use Warabe::App; $http = Wanage::HTTP->new_cgi; $http = Wanage::HTTP->new_from_psgi_env; $http->send_response (onready => sub { $app = Warabe::App->new_from_http ($http); $app->execute (sub { unless ($app->path_segments->[0] eq 'hoge') { $app->throw_error (404); } $app->send_plain_text (get_data ($app->path_segments->[1])); }); }); =head1 DESCRIPTION The L module is the primary component of B, a server-side Web application construction toolkit. It provides a number of convinient methods handling HTTP request and response in the way that would be useful for typical Web applications. Warabe accesses the HTTP request and response through the interface provided by L. Therefore, Warabe can handle any server-application interface supported by L, including CGI and PSGI. Warabe constructs higher-level operations for more concrete use cases on the top of APIs provided by Wanage HTTP object. It does not mean a Web application using Warabe should not access Wanage HTTP object directly - in fact, Warabe does not provide complete access to HTTP request and response, as it can be easily accessed through the HTTP object directly from the application, if necessary. +------+ +------+ +------+ +------+ +------+ |Your |<->|Warabe|<->|Wanage|<---->|HTTP |<---->|HTTP | | App.| +------+ | | CGI |server| HTTP |client| | |<------------>| | PSGI | | | | +------+ +------+ +------+ +------+ App. Common Generic TCP/HTTP specific Web app. HTTP protocol logic operations interpret. handling =head1 SUBCLASSING An application can define a subclass of L such that additional methods can be added to the application object. Such a subclass can incorporate additional features by also inheriting plugin modules, such as C. Example: package My::Warabe::App; use base qw(Warabe::App::Role::JSON Warabe::App); sub my_user { my $self = shift; my $session_id = $self->http->request_cookies->{my_session}; ...; return $user; } Although there is no formal naming rule for methods, it is recommended for subclass-defined methods to contain a short identifier of the class within its name (e.g. C in the example above) to avoid confliction with future additions to base classes. =head1 METHODS Following methods are available on the C object: =head2 Constructor =over 4 =item $app = Warabe::App->new_from_http ($http) Create and return a new application object, associated with the given HTTP interface object (i.e. an instance of L). =item $http = $app->http Return the HTTP interface object (i.e. an instance of L) associated with the application object. =cut =head2 Request data accessors =over 4 =item $components = $app->path_segments Return the list (L) object of the path segments (C-separated components in the path component) of the requested URL. The path segments are percent-decoded and then interpreted as UTF-8 texts. If the request does not contain a valid URL with path segments, the method return an empty list object. (Maybe you want to return a C<400> or C<404> response in this case.) Examples: "/" -> List::Ish->new (['']) "/foo/bar" -> List::Ish->new (['foo', 'bar']) "/foo%2Fbar" -> List::Ish->new (['foo/bar']) "/%EF%AC%AD" -> List::Ish->new (["\x{FB2D}"]) "/\xEF\xAC\xAD" -> List::Ish->new (["\x{FB2D}"]) "/ab%9Fa%A0%00" -> List::Ish->new (["ab\x{FFFD}a\x{FFFD}\x00"]) "/foo//bar/" -> List::Ish->new (["foo", "", "bar", ""]) "" -> List::Ish->new ([]) "*" -> List::Ish->new ([]) "foo/bar" -> List::Ish->new ([]) =back Following methods extract data from request parameters, where request parameters consist of C-encoded data in the query component of the request URL and C- or C-encoded data in the request-body. Please note that the C header of the request must have the value C or C for the request-body to be parsed as parameters. =over 4 =item $value = $app->text_param ($name) Return the value for the parameter whose name is given as the argument. If there are multiple parameters with the same name, the first one is returned. Parameters in the query component of the request URL appear before parameters in the request-body. If there is no parameter with the specified name, the C value is returned. The parameter names and values are interpreted as UTF-8 texts. The argument must be a character string. The return value, if it is not C, is a character string. This method ignores the C<_charset_> hack, which is a willful violation to the HTML Standard, which defines the decoding algorithm for C. If your application has to support non-UTF-8 character encodings, you have to define your own method to decode such data. =item $list = $app->text_param_list ($name) Return the list (L) object containing the parameter values whose name is given as the argument. If there are multiple parameters with the same name, their values are added to the list in order. Parameters in the query component of the request URL appear before parameters in the request-body. If there is no parameter with the specified name, an empty list is returned. The parameter names and values are interpreted as UTF-8 texts. The argument must be a character string. The returned list contains zero or more character strings. This method ignores the C<_charset_> hack, which is a willful violation to the HTML Standard, which defines the decoding algorithm for C. If your application has to support non-UTF-8 character encodings, you have to define your own method to decode such data. =item $value = $app->bare_param ($name) Return the value for the parameter whose name is given as the argument. If there are multiple parameters with the same name, the first one is returned. Parameters in the query component of the request URL appear before parameters in the request-body. If there is no parameter with the specified name, the C value is returned. The parameter names and values are interpreted as octet-streams. The argument must be a byte string. The return value, if it is not C, is a byte string. This method does not decode any character encoding in use, which is a willful violation to the HTML Standard, which defines the decoding algorithm for C, and RFC 2388 and MIME, which defines the semantics for C entity and any entity-body within it. =item $list = $app->bare_param_list ($name) Return the list (L) object containing the parameter values whose name is given as the argument. If there are multiple parameters with the same name, their values are added to the list in order. Parameters in the query component of the request URL appear before parameters in the request-body. If there is no parameter with the specified name, an empty list is returned. The parameter names and values are interpreted as octet-streams. The argument must be a byte string. The returned list contains zero or more byte strings. This method does not decode any character encoding in use, which is a willful violation to the HTML Standard, which defines the decoding algorithm for C, and RFC 2388 and MIME, which defines the semantics for C entity and any entity-body within it. =back =head2 Response construction =over 4 =item $app->send_plain_text ($text) Send a plain text, encoded in UTF-8. The C header is set to C. =item $app->send_html ($text) Send an HTML document, encoded in UTF-8. The C header is set to C. =item $app->send_redirect ($url_as_string, %args) Send a redirect response. The argument must be the URL to which the request is redirected. It must be a character string. The URL will be resolved with respect to the URL of the request (i.e. C<< $app->url >>). Following options are available: =over 4 =item status => $code Specify the HTTP status code. Unless C is true, it's default is C<302> and, if specified, it must be one of HTTP redirect status codes. If C is true, it's default is C<200>. =item reason_phrase => $string Specify the C in the C of the HTTP response. The value must be a short string of US-ASCII printable characters. If this option is not specified, the default response phrase from HTTP specifications is used. Depending on the interface, C might be ignored. CGI does support C, while PSGI does not. =item refresh => BOOLEAN If true, instead of HTTP redirect, a short HTML document with C<< >> directive is used. =back Examples: $app->send_redirect_response ('/page/1234'); $app->send_redirect_response ('http://www.google.com/'); By subclassing and defining the C method, you can modify the redirect URL just before it is set to the C header. The method receives an argument, which is the L canonicalized URL object to be used in the C header. The method must return a L object, which may or may not be same as the argument. The returned URL object is then serialized and set to the L header. Example: package My::App; use base qw(Warabe::App); sub send_redirect_response { my $url = shift; if ($url->stringify =~ m{^http://myhost.example.com/}) { return $url; } else { my $orig_url = $url->stringify; return Wanage::URL->new_from_parsed_url ({ scheme => 'http', hostname => 'myhost.example.com', path => '/', query => 'url=' . percent_encode_c $orig_url, }); } } $app->send_redirect_response (q); # Location: http://myhost.example.com/ $app->send_redirect_response (q); # Location: http://myhost.example.com/?url=http%3A%2F%2Fwww.google.com%2F =item $app->send_error_response ($code, %args) Send an error response with the specified HTTP status code. Following option is available: =over 4 =item reason_phrase => $text Specify the C in the C of the HTTP response. The value must be a short string of US-ASCII printable characters. If this option is not specified, the default response phrase from HTTP specifications is used. Depending on the interface, C might be ignored. CGI does support C, while PSGI does not. =back Examples: $app->send_error_response (404 => 'Resource not found'); $app->send_error_response (409); =back =head2 Throw-or-process application model The application object supports the throw-or-process application model, that is, the application may throw an exception to signal the completion of the construction of the response, in the middle of processing code, to precent from the subsequent lines being executed. This feature might be useful when the application detects an error in the input such that it wants to stop the processing at the beginning of a long method. Example: $app->execute (sub { my $object = My::Object->find_by_id ($app->text_param ('id')); $app->throw_error (404) unless $object; $app->send_html ($object->as_html); }); In this example, if the C parameter identifies a valid object, the HTML representation of the object is sent to the client with C<200> status. Otherwise, a C<404> error response is sent. The C method is not invoked in this case. =over 4 =item $app->execute (CODE) Execute the specified code reference. The code should construct the response, with optional invocation of the C method. The code should not throw any other exception. If it does, the exeception is reported to the C handler. Additionally, if the response headers are not sent yet, a C<500> Internal Server Error response is sent. (If the response headers have already sent, it cannot be changed to an error response anymore.) =item $app->throw Throw a signal that the response construction has been done. This method should be invoked after the response has been sent. =item $app->throw_redirect (ARGS) Send a redirect response and then throw. This method is equivalent to invocations of C and C methods. =item $app->throw_error (ARGS) Send an error response and then throw. This method is equivalent to invocations of C and C methods. =item CODE = $app->onexecuteerror =item $app->onexecuteerror (CODE) Specifying a code reference as the C handler, you can receive the exception thrown within the C stage (not including ones thrown by the C method, however). The code reference is invoked with the exception, i.e. the C<$@> value at the time of the failure of the execution as the argument. By default, the handler simply Cs the exception. The code reference itself is not expected to throw any exception. In addition, the handler cannot be used to change the response. This handler is intended for the hook point for reporting the exception to the developer, not to the user. This method can also be used by the promise-based application model. =item CODE = $app->onclose =item $app->onclose (CODE) Specifying a code reference as the C handler, you can hook when the response has been sent to the server (for sending back to the client). The code reference is not expected to throw any exception. This method can also be used by the promise-based application model. =item SECONDS = $app->elapsed_time Return the elapsed time, i.e. the time from the instantiation of the C<$app> object to the completion of the response, in seconds. The method returns C before the the C handler is invoked as the value is by definition not available yet. This method can also be used by the promise-based application model. =back =head2 Promise-based application model The application object supports the promise-based application model, that is, the application may either immediately process the request synchronously, or return a promise object to defer the process to generate the response until some asynchronous processing has been done. Example: $app->execute_by_promise (sub { my $path = $app->path_segments; if (@$path == 1 and $path->[0] eq 'data') { return get_as_promise ('http://internal/data.json')->then (sub { $app->send_json ($_[0]->response_text); }, sub { $app->send_error (502); }); } else { $app->send_error (404); } }); In this example, the C function returns a promise object which is to be resolved after the specified URL is fetched. As a promise is returned when the path is C, handling of the request will not be completed until the promise has been resolved. For the purpose of this application model, promises are objects implementing API which has similar semantics to C JavaScript objects. An example of such promise class is the L module available at . By default, the C method requires the L module. This can be altered by setting the C<$Warabe::App::PromiseClass> global variable. =over 4 =item $app->execute_by_promise (CODE) Execute the specified code reference synchronously. The code should construct the response synchronously or asynchronously. If the process to construct the response has not been completed synchronously, it must return a promise, which is resolved upon the completion. The code should not throw any exception. If it does, the exeception is reported to the C handler. Additionally, if the response headers are not sent yet, a C<500> Internal Server Error response is sent. (If the response headers have already sent, it cannot be changed to an error response anymore.) =back Methods C, C, and C are also applicable to this application model. =head2 Validation methods Following methods can be used to validate the request data. They throw appropriate error response when the request data is not acceptable. Therefore, they must be used within the code executed in the C method described above. =over 4 =item $app->requires_valid_url_scheme Check whether the URL scheme of the request is supported by application or not. The list of valid URL schemes are provided as hash reference C<$Warabe::App::AllowedURLSchemes>, where keys are normalized URL schemes and values are true values. By default, the value is C<< {http => 1, https => 1} >>. =item $app->requires_https Check whether the URL scheme of the request is C or not. If the request URL scheme is I C and is a safe method (i.e. C or C), the request is redirected to the C URL with same authority, path, query, and fragment components as the request. If the method is not safe, a C<400> error is returned instead. =item $app->requires_valid_hostname Check whether the host component of the request URL is supported by application or not. Whether the host is valid or not is determined by whether the canonicalized representation of host matches the regular expression C<$Warabe::App::AllowedHostname>. By default, the regular expression is C<.*>, i.e. any host is allowed. =item $app->requires_valid_content_length (%args) Check the length of the request-body and throw C<413> Request-body Too Large error response if it is too large. Folowing option is available: =over 4 =item max => $max_length The maximum number of bytes allowed by the application. If this option is not specified, the value of the global variable C<$Warabe::App::MaxContentLength>, whose default is C<1024 * 1024> (i.e. 1MB), is used. =back =item $app->requires_mime_type ({$allowed_type => 1, ...}) Check whether the MIME type of the request is one of known types or not. If it is not a known type, the C<415> Unsupported MIME Type error is thrown. The list of allowed MIME types can be specified as the argument. If it is not specified, the C<$Warabe::App::AllowedMIMETypes> value is used instead. In either case, the list must be supplied as a hash reference where keys are lowercase-normalized form of MIME types and values are true values. The MIME types cannot contain parameters; Any parameters are ignored for the purpose of this method. The default list is: C<< {'application/x-www-form-urlencoded' => 1, 'multipart/form-data' => 1} >>. If there is no request-body, or the length of request-body is zero, this method does not throw when the MIME type is not specified in the C header. Otherwise, the request has to contain a C header which contains one of allowed MIME types. =item $app->requires_request_method ({$allowed_method => 1, ...}) Check whether the request method is one of supported methods or not. If it is not a known type, the C<405> Method Not Allowed error is thrown. The list of allowed methods can be specified as the argument. If it is not specified, the C<$Warabe::App::AllowedRequestMethods> value is used instead. In either case, the list must be supplied as a hash reference where keys are request methods and values are true values. The default list is: C<< {GET => 1, HEAD => 1, POST => 1} >>. =item $app->requires_basic_auth ({$userid => $password, ...}, %args) Check whether the request contains a credentials of HTTP Basic authentication whose userid-password pair is contained in the hash reference given as the argument. The argument must be a hash reference where key-value pairs represent the allowed userid-password pairs. Values of userid and password must be byte strings. It should only contain ASCII printable characters for HTTP compliance and interoperability. The userid cannot contain the C<:> character. If the request does not contain credentials for Basic authentication, or it does contain a wrong or inappropriate credentials, a C<401> Authorization Required response is thrown. Following option is available: =over 4 =item realm => $string A short string that represents the realm, the scope of the authentication. Unless this option is specified, the realm is set to the empty string. If specified, the realm can only contain printable ASCII characters other than C<"> and C<\>. =back =item $app->requires_same_origin Check whether the C header value is same as the origin of the request URL or not. If not (i.e. is different origin or has no C header), a C<400> response is thrown. =item $app->requires_same_origin_or_referer_origin Check whether the C header value or the origin of the C header value is same as the origin of the request URL or not. If not (i.e. is different origin or has no C and C headers), a C<400> response is thrown. If there are both header fields, only C header is taken into account. =back =head1 SEE ALSO L. =head1 AUTHOR Wakaba . =head1 LICENSE Copyright 2012-2021 Wakaba . This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut