package Riap::HTTP; # just to make PodWeaver happy

our $VERSION = '1.1.13'; # VERSION

1;
# ABSTRACT: Riap over HTTP


__END__
=pod

=head1 NAME

Riap::HTTP - Riap over HTTP

=head1 VERSION

version 1.1.13

=head1 DESCRIPTION

Riap::HTTP is designed to be implemented by API service providers. It allows
features like custom URL layout, multiple serialization format, and logging;
while depends on HTTP for other features like authentication, encryption
(HTTPS), etc. For a more lightweight alternative, look at L<Riap::TCP>.

Server listens to HTTP requests, parses them into Riap requests, executes the
Riap requests, and sends the results to clients.

=head2 Additional Riap request keys

=over 4

=item * fmt => STR

Specify output serialization format to use. The format names are decided by the
implementation, but server MUST support C<json> as the fallback. If unspecified,
default in most cases should also be C<json>, but server can default to other
format, like say L<HTML> or L<text>, for viewing convenience of certain clients
like GUI browser. Server should detect appropriate default output format from
C<Accept> HTTP request header.

To find out what serializations are supported, client can perform an C<srvinfo>
action.

Server should fallback to C<json> if requested result format is not supported.

=back

=head2 Additional actions

=head3 Action: B<srvinfo>

Just like C<info> action, but get information about the server instead. Server
MUST at least return the following keys in the result hash.

 {
  // server's absolute URL
  "srvurl": "http://localhost:5000/",

  // supported formats
  "fmt": ["Console","HTML","JSON","PHPSerialization","YAML","json"],
 }

=head2 Parsing Riap request from HTTP request

Server can provide defaults for some/all Riap request keys, so client does not
need to explicitly set a Riap request key. But server MUST provide a way for
client to set any Riap request key.

First, server MUST parse Riap request keys from HTTP C<X-Riap-*> request
headers, e.g. C<X-Riap-Action> header for setting the C<action> request key. In
addition, the server MUST parse the C<X-Riap-*-j-> headers for JSON-encoded
value (notice the ending C<->), e.g.

 X-Riap-Args-j-: {"arg1":"val1","arg2":[1,2,3]}

This allows the server to have its own URL scheme, while allowing a way for a
common access mechanism.

The server MUST also accept C<args> from HTTP request body. The server MUST
accept at least body of type C<application/json>, but it can accept other types.
The server MUST not interpret C<application/x-www-form-urlencoded> for this
purpose (and interpret it as a normal web form). Server MUST return response 400
if document type is not recognized.

The server can also accept Rinci request keys or function arguments using other
means. For example, L<Plack::Middleware::PeriAHS::ParseRequest>, from Perl
server implementation, allows parsing Riap request keys like C<uri> from URI
path, and C<args> keys (as well as other Riap request keys, using C<-riap-*>
syntax) from request variables. For example:

 http://HOST/api/PKG/SUBPKG/FUN?a1=1&a2:j=[1,%202]

might result in the following Riap request:

 {
  "uri": '/PKG/SUBPKG/FUN',
  "action": 'call',
  "args": {"a1":1, "a2":[1,2]},
 }

Another example:

 http://HOST/api/PKG/FUN?-riap-action=complete_arg_val&-riap-arg=a1&-riap-word=x

will result in the following Rinci request:

 {
  "uri": '/PKG/FUN',
  "action": 'complete_arg_val',
  "arg": 'a1',
  "word": 'x',
 }

=head1 SPECIFICATION VERSION

 1.1

=head1 ABSTRACT

This document specifies using HTTP/HTTPS as the transport layer for L<Riap>, or
Riap::HTTP for short.

=head1 LOGGING

Riap over HTTP also allows a mechanism to pass logging messages during function
calls by using HTTP chunked response.

Two additional Riap request keys are recognized for this:

=over 4

=item * B<loglevel>

An integer number with value either 0 (for none, the default), 1 (for sending
fatal messages), 2 (error), 3 (warn), 4 (info), 5 (debug), and 6 (trace). When a
value larger than 0 specified, server must return chunked HTTP response and each
log message should be sent as a separate chunk, and the result as the last
chunk.

=item * B<marklog>

A bool, default to 0. When set to true, server will prepend each log message
with "L" (and the result with "R"). Only useful/relevant when turning on
loglevel, so clients can parse/separate log message from result.

=back

=head1 EXAMPLES

Below are some examples of what is sent and received over the wire. For these
examples, the server has the following URL scheme http://example.org/api/<URI>.
Server also assume the default values for these Riap request keys so they do not
have to be specified by client: C<v> (1.1), C<action> (call).

Call a function, passing function arguments via query parameter, unsuccessfully
because of missing argument:

 --- Request ---
 GET /api/Math/multiply2?a=2 HTTP/1.0
 Accept: application/json

 --- Response ---
 HTTP/1.0 200 OK
 Date: Sat, 14 Jan 2012 17:11:40 GMT
 Server: PeriAHS/0.01
 Content-Type: application/json

 [400,"Missing required argument: b"]

Call the same function, successfully this time. As a variation we pass function
arguments through the X-Riap-Args HTTP header:

 --- Request ---
 GET /api/Math/multiply2 HTTP/1.0
 X-Riap-Args-j-: {"a":2,"b":3}
 Accept: application/json

 --- Response ---
 HTTP/1.0 200 OK
 Date: Sat, 14 Jan 2012 17:11:50 GMT
 Server: PeriAHS/0.01
 Content-Type: application/json

 [200,"OK",6]

=head1 FAQ

=head2 Why not directly return status from enveloped result as HTTP response status?

Since enveloped result is modeled somewhat closely after HTTP message,
especially the status code, it might make sense to use the status code directly
as HTTP status. But this means losing the ability to differentiate between the
two. We want the client to be able to differentiate whether the 500 (Internal
server error) or 404 (Not found) code it is getting is from the HTTP
server/proxies/client or from the enveloped result.

=head2 Riap/Riap::HTTP vs REST? Which one to choose for my web service?

Riap maps code entities (packages and functions), while REST maps resources
(like business entities) to URI's. For example:

 # Riap
 /User/             # package
 /User/list_users   # function
 /User/create_user  # function

 # REST (verb + URI)
 GET /user          # list users
 GET /user/123      # get specific user
 POST /user         # create a new user
 PATCH /user/123    # modify user
 DELETE /user/123

Riap is more RPC style, and it is more straightforward if you want to export a
set of code modules and functions as API. But it also defines standard and
extensible "verbs" (actions). It also helps service discoverability and
self-description due to the C<list> action and the rich Rinci metadata.
Riap::HTTP can also use custom routing so if you want you can make it use REST
style, while still fulfilling request with Riap in the backend. For example, see
L<Serabi> in Perl.

=head1 SEE ALSO

L<Riap::TCP>

=head1 AUTHOR

Steven Haryanto <stevenharyanto@gmail.com>

=head1 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.

=cut

