NAME

Plack::Middleware::SubSpec::ParseRequest - Parse HTTP request into SS request

VERSION

version 0.12

SYNOPSIS

# in your app.psgi
use Plack::Builder;

builder {
    enable "SubSpec::ParseRequest",
        uri_pattern => m!^/api/v1/(?<module>[^?]+)?/?(?<sub>[^?/]+)?!,
        allowed_modules => qr/^My::API/;
};

DESCRIPTION

This middleware parses SS request from HTTP request (PSGI environment) and should normally be the first middleware put in the stack.

Parsing result

The result of parsing will be put in 'ss.request' PSGI environment key.

Parsing process

From HTTP header and request body. The parsing is done as per Sub::Spec::HTTP specification. First, all X-SS-Req-* request headers are parsed for SS request key. When an unknown header is found, HTTP 400 error is returned. Then, request body is read for arguments. 'application/json' document type is accepted, and also 'text/yaml' (if accept_yaml configuration is enabled) and 'application/vnd.php.serialized' (if accept_phps configuration is enabled).

Additionally, the following are also done:

From URI. Request URI is checked against uri_pattern configuration. If URI doesn't match this regex, a 400 error response is returned. It is a convenient way to check for valid URLs as well as set SS request keys, like:

qr!^/api/(?<output_format>json|yaml)/!;

Other named captures not matching known SS request keys will be stored in $env->{"ss.uri_pattern_matches"}. For convenience, module and/or sub will be used to set uri (if it's not already defined). For example:

qr!^/api/v1/(?<module>[^?/]+)/(?<sub>[^?/]+)!

will set uri to "pm:$+{module}/$+{sub}". For convenience, module is also preprocessed, all nonalphanumeric character groups will be converted to "::".

The default uri_pattern is qr/.?/, which matches anything, but won't parse/capture any information.

From web form variables. If parse_args_from_web_form is enabled, args request key will be set (or added) from GET/POST request variables, for example: http://host/api/foo/bar?a=1&b:j=[2] will set arguments a and b (":j" suffix means value is JSON-encoded; ":y" and ":p" are also accepted if the accept_yaml and accept_phps configurations are enabled). In addition, request variables -ss-req-* are also accepted for setting other SS request keys. Unknown SS request key or encoding suffix will result in 400 error.

If request format is JSON and web form variable callback is defined, then it is assumed to specify callback for JSONP instead part of args. "callback(json)" will be returned instead of just "json".

From URI (2). If parse_args_from_path_info configuration is enabled, and uri SS request key contains module and subroutine name (so spec can be retrieved), args will be set (or added) from URI path info. Note that portion matching uri_pattern will be removed first. For example, when uri_pattern is qr!^/api/v1(?:/(?<module>[\w:]+)(?:/(?<sub>\w+)))?!:

http://host/api/v1/Module::Sub/func/a1/a2/a3

will result in ['a1', 'a2', 'a3'] being fed into Sub::Spec::GetArgs::Array. An unsuccessful parsing will result in HTTP 400 error.

CONFIGURATIONS

  • uri_pattern => REGEX (default qr/.?/)

    Regexp with named captures to match against URI, to extract SS request keys from. Additionally, module and sub are also converted into 'pm' URI, if uri is not already specified.

    If regexp doesn't match, a 400 error response will be generated.

  • allowed_uri_schemes => ARRAY|REGEX (default ['pm'])

    Which URI schemes are allowed. If SS request's uri has a scheme not on this list, a HTTP 403 error will be returned.

  • allowed_commands => ARRAY|REGEX (default [qw/about call help list_mods list_subs spec usage/])

    Which commands to allow. Default is all commands. If you want to disable certain commands, exclude it from the list. In principle the most important command is 'call', while the others are just helpers.

  • allowed_modules => ARRAY|REGEX (default [])

    Which modules to allow. Needs to be set.

  • parse_args_from_web_form => BOOL (default 1)

    Whether to parse arguments from web form (GET/POST parameters). If an argument is already defined (e.g. via X-SS-Req-* HTTP header), it will be skipped.

  • parse_args_from_path_info => BOOL (default 1)

    Whether to parse arguments from path info. This will only be done if uri contains module and subroutine name, so its spec can be retrieved (spec is required for parsing from PATH_INFO).

    removed from URI before args are extracted. Also, parsing arguments from path info (array form, /arg0/arg1/...) requires that we have the sub spec first. So we need to execute the Plack::Middleware::SubSpec::LoadSpec first. The actual parsing is done by Plack::Middleware::SubSpec::ParseArgsFromPathInfo first.

  • accept_phps => BOOL (default 1)

    Whether to accept PHP serialization-encoded data (either in GET/POST request variables, etc). If you only want to deal with, say, JSON encoding, you might want to turn this off.

  • accept_yaml => BOOL (default 1)

    Whether to accept YAML-encoded data (either in GET/POST request variables, etc). If you only want to deal with, say, JSON encoding, you might want to turn this off.

  • per_arg_encoding => BOOL (default 1)

    Whether we should allow each GET/POST request variable to be encoded, e.g. http://foo?arg1:j=%5B1,2,3%5D ({arg1=>[1, 2, 3]}).

  • allow_logs => BOOL (default 1)

    Whether to allow request for returning log messages (request option 'log_level' with value larger than 0). You might want to turn this off on production servers.

  • after_parse => CODE

    If set, the specified code will be called with arguments ($self, $env) to allow doing more parsing/checks.

SEE ALSO

Sub::Spec::HTTP

Sub::Spec::HTTP::Client

AUTHOR

Steven Haryanto <stevenharyanto@gmail.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2012 by Steven Haryanto.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.