.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Continuity 3" .TH Continuity 3 "2011-09-17" "perl v5.14.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Continuity \- Abstract away statelessness of HTTP, for stateful Web applications .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #!/usr/bin/perl \& \& use strict; \& use Continuity; \& \& my $server = new Continuity; \& $server\->loop; \& \& sub main { \& my $request = shift; \& $request\->print("Your name:
"); \& $request\->next; # this waits for the form to be submitted! \& my $name = $request\->param(\*(Aqname\*(Aq); \& $request\->print("Hello $name!"); \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Continuity is a library to simplify web applications. Each session is written and runs as a persistent application, and is able to request additional input at any time without exiting. This is significantly different from the traditional \s-1CGI\s0 model of web applications in which a program is restarted for each new request. .PP The program is passed a \f(CW$request\fR variable which holds the request (including any form data) sent from the browser. In concept, this is a lot like a \f(CW$cgi\fR object from \s-1CGI\s0.pm with one very very significant difference. At any point in the code you can call \f(CW$request\fR\->next. Your program will then suspend, waiting for the next request in the session. Since the program doesn't actually halt, all state is preserved, including lexicals \*(-- getting input from the browser is then similar to doing \f(CW\*(C`$line = <>\*(C'\fR in a command-line application. .SH "GETTING STARTED" .IX Header "GETTING STARTED" The first thing to make a note of is that your application is a continuously running program, basically a self contained webserver. This is quite unlike a \&\s-1CGI\s0.pm based application, which is re-started for each new request from a client browser. Once you step away from your \s-1CGI\s0.pm experience this is actually more natural (\s-1IMO\s0), more like writing an interactive desktop or command-line program. .PP Here's a simple example: .PP .Vb 1 \& #!/usr/bin/perl \& \& use strict; \& use Continuity; \& \& my $server = new Continuity; \& $server\->loop; \& \& sub main { \& my $request = shift; \& while(1) { \& $request\->print("Hello, world!"); \& $request\->next; \& $request\->print("Hello again!"); \& } \& } .Ve .PP First, check out the small demo applications in the eg/ directory of the distribution. Sample code there ranges from simple counters to more complex multi-user ajax applications. All of the basic uses and some of the advanced uses of Continuity are covered there. .PP Here is an brief explanation of what you will find in a typical application. .PP Declare all your globals, then declare and create your server. Parameters to the server will determine how sessions are tracked, what ports it listens on, what will be served as static content, and things of that nature. You are literally initializing a web server that will serve your application to client browsers. Then call the \f(CW\*(C`loop\*(C'\fR method of the server, which will get the server listening for incoming requests and starting new sessions (this never exits). .PP .Vb 3 \& use Continuity; \& my $server = Continuity\->new( port => 8080 ); \& $server\->loop; .Ve .PP Continuity must have a starting point when starting new sessions for your application. The default is \f(CW\*(C`\e&::main\*(C'\fR (a sub named \*(L"main\*(R" in the default global scope), which is passed the \f(CW$request\fR handle. See the Continuity::Request documentation for details on the methods available from the \f(CW$request\fR object beyond this introduction. .PP .Vb 4 \& sub main { \& my $request = shift; \& # ... \& } .Ve .PP Outputting to the client (that is, sending text to the browser) is done by calling the \f(CW\*(C`$request\->print(...)\*(C'\fR method, rather than the plain \f(CW\*(C`print\*(C'\fR used in \s-1CGI\s0.pm applications. .PP .Vb 2 \& $request\->print("Hello, guvne\*(Aq
"); \& $request\->print("\*(Aqow ya been?"); .Ve .PP \&\s-1HTTP\s0 query parameters (both \s-1GET\s0 and \s-1POST\s0) are also gotten through the \&\f(CW$request\fR handle, by calling \f(CW\*(C`$p = $request\->param(\*(Aqx\*(Aq)\*(C'\fR, just like in \&\s-1CGI\s0.pm. .PP .Vb 3 \& # If they go to http://webapp/?x=7 \& my $input = $request\->param(\*(Aqx\*(Aq); \& # now $input is 7 .Ve .PP Once you have output your \s-1HTML\s0, call \f(CW\*(C`$request\->next\*(C'\fR to wait for the next response from the client browser. While waiting other sessions will handle other requests, allowing the single process to handle many simultaneous sessions. .PP .Vb 3 \& $request\->print("Name:
"); \& $request\->next; # <\-\- this is where we suspend execution \& my $name = $request\->param(\*(Aqn\*(Aq); # <\-\- start here once they submit .Ve .PP Anything declared lexically (using my) inside of \f(CW\*(C`main\*(C'\fR is private to the session, and anything you make global is available to all sessions. When \&\f(CW\*(C`main\*(C'\fR returns the session is terminated, so that another request from the same client will get a new session. Only one continuation is ever executing at a given time, so there is no immediate need to worry about locking shared global variables when modifying them. .SH "ADVANCED USAGE" .IX Header "ADVANCED USAGE" Merely using the above code can completely change the way you think about web application infrastructure. But why stop there? Here are a few more things to ponder. .SS "Coro::Event" .IX Subsection "Coro::Event" Since Continuity is based on Coro, we also get to use Coro::Event. This means that you can set timers to wake a continuation up after a while, or you can have inner-continuation signaling by watch-events on shared variables. .SS "Multiple sessions per-user" .IX Subsection "Multiple sessions per-user" For \s-1AJAX\s0 applications, we've found it handy to give each user multiple sessions. In the chat-ajax-push demo each user gets a session for sending messages, and a session for receiving them. The receiving session uses a long-running request (aka \s-1COMET\s0) and watches the globally shared chat message log. When a new message is put into the log, it pushes to all of the ajax listeners. .SS "Lexical storage and callback links" .IX Subsection "Lexical storage and callback links" Don't forget about those pretty little lexicals you have at your disposal. Taking a hint from the Seaside folks, instead of regular links you could have callbacks that trigger a anonymous subs. Your code could look like: .PP .Vb 10 \& use Continuity; \& use strict; \& my @callbacks; \& my $callback_count; \& Continuity\->new\->loop; \& sub gen_link { \& my ($text, $code) = @_; \& $callbacks[$callback_count++] = $code; \& return qq{$text}; \& } \& sub process_links { \& my $request = shift; \& my $cb = $request\->param(\*(Aqcb\*(Aq); \& if(exists $callbacks[$cb]) { \& $callbacks[$cb]\->($request); \& delete $callbacks[$cb]; \& } \& } \& sub main { \& my $request = shift; \& my $x; \& my $link1 = gen_link(\*(AqThis is a link to stuff\*(Aq => sub { $x = 7 }); \& my $link2 = gen_link(\*(AqThis is another link\*(Aq => sub { $x = 42 }); \& $request\->print($link1, $link2); \& $request\->next; \& process_links($request); \& $request\->print("\e$x is now: $x"); \& } .Ve .SS "Scaling" .IX Subsection "Scaling" To scale a Continuity-based application beyond a single process you need to investigate the keywords \*(L"session affinity\*(R". The Seaside folks have a few articles on various experiments they've done for scaling, see the wiki for links and ideas. Note, however, that premature optimization is evil. We shouldn't even be talking about this. .SH "EXTENDING AND CUSTOMIZING" .IX Header "EXTENDING AND CUSTOMIZING" This library is designed to be extensible but have good defaults. There are two important components which you can extend or replace. .PP The Adapter, such as the default Continuity::Adapt::HttpDaemon, actually makes the \s-1HTTP\s0 connections with the client web browser. If you want to use FastCGI or even a non-HTTP protocol, then you will use or create an Adapter. .PP The Mapper, such as the default Continuity::Mapper, identifies incoming requests from The Adapter and maps them to instances of your program. In other words, Mappers keep track of sessions, figuring out which requests belong to which session. The default mapper can identify sessions based on any combination of cookie, \s-1IP\s0 address, and \s-1URL\s0 path. Override The Mapper to create alternative session identification and management. .SH "METHODS" .IX Header "METHODS" The main instance of a continuity server really only has two methods, \f(CW\*(C`new\*(C'\fR and \f(CW\*(C`loop\*(C'\fR. These are used at the top of your program to do setup and start the server. Please look at Continuity::Request for documentation on the \&\f(CW$request\fR object that is passed to each session in your application. .ie n .SS "$server = Continuity\->new(...)" .el .SS "\f(CW$server\fP = Continuity\->new(...)" .IX Subsection "$server = Continuity->new(...)" The \f(CW\*(C`Continuity\*(C'\fR object wires together an Adapter and a mapper. Creating the \f(CW\*(C`Continuity\*(C'\fR object gives you the defaults wired together, or if user-supplied instances are provided, it wires those together. .PP Arguments: .IP "\(bu" 4 \&\f(CW\*(C`callback\*(C'\fR \*(-- coderef of the main application to run persistantly for each unique visitor \*(-- defaults to \f(CW\*(C`\e&::main\*(C'\fR .IP "\(bu" 4 \&\f(CW\*(C`adapter\*(C'\fR \*(-- defaults to an instance of \f(CW\*(C`Continuity::Adapt::HttpDaemon\*(C'\fR .IP "\(bu" 4 \&\f(CW\*(C`mapper\*(C'\fR \*(-- defaults to an instance of \f(CW\*(C`Continuity::Mapper\*(C'\fR .IP "\(bu" 4 \&\f(CW\*(C`docroot\*(C'\fR \*(-- defaults to \f(CW\*(C`.\*(C'\fR .IP "\(bu" 4 \&\f(CW\*(C`staticp\*(C'\fR \*(-- defaults to \f(CW\*(C`sub { $_[0]\->url =~ m/\e.(jpg|jpeg|gif|png|css|ico|js)$/ }\*(C'\fR, used to indicate whether any request is for static content .IP "\(bu" 4 \&\f(CW\*(C`debug_level\*(C'\fR \*(-- Set level of debugging. 0 for nothing, 1 for warnings and system messages, 2 for request status info. Default is 1 .IP "\(bu" 4 \&\f(CW\*(C`debug_callback\*(C'\fR \*(-- Callback for debug messages. Default is print. .PP Arguments passed to the default adapter: .IP "\(bu" 4 \&\f(CW\*(C`port\*(C'\fR \*(-- the port on which to listen .IP "\(bu" 4 \&\f(CW\*(C`no_content_type\*(C'\fR \*(-- defaults to 0, set to 1 to disable the \f(CW\*(C`Content\-Type: text/html\*(C'\fR header and similar headers .PP Arguments passed to the default mapper: .IP "\(bu" 4 \&\f(CW\*(C`cookie_session\*(C'\fR \*(-- set to name of cookie or undef for no cookies (defaults to 'cid') .IP "\(bu" 4 \&\f(CW\*(C`query_session\*(C'\fR \*(-- set to the name of a query variable for session tracking (defaults to undef) .IP "\(bu" 4 \&\f(CW\*(C`assign_session_id\*(C'\fR \*(-- coderef of routine to custom generate session id numbers (defaults to a simple random string generator) .IP "\(bu" 4 \&\f(CW\*(C`cookie_life\*(C'\fR \*(-- lifespan of the cookie, as in CGI::set_cookie (defaults to \*(L"+2d\*(R") .IP "\(bu" 4 \&\f(CW\*(C`ip_session\*(C'\fR \*(-- set to true to enable ip-addresses for session tracking (defaults to false) .IP "\(bu" 4 \&\f(CW\*(C`path_session\*(C'\fR \*(-- set to true to use \s-1URL\s0 path for session tracking (defaults to false) .IP "\(bu" 4 \&\f(CW\*(C`implicit_first_next\*(C'\fR \*(-- set to false to get an empty first request to the main callback (defaults to true) .ie n .SS "$server\->\fIloop()\fP" .el .SS "\f(CW$server\fP\->\fIloop()\fP" .IX Subsection "$server->loop()" Calls Coro::Event::loop and sets up session reaping. This never returns! .SH "SEE ALSO" .IX Header "SEE ALSO" See the Wiki for development information, more waxing philosophic, and links to similar technologies such as . .PP Website/Wiki: .PP Continuity::Request, Continuity::RequestCallbacks, Continuity::Mapper, Continuity::Adapt::HttpDaemon, Coro .PP AnyEvent::DBI and Coro::Mysql for concurrent database access. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 3 \& Brock Wilcox \- http://thelackthereof.org/ \& Scott Walters \- http://slowass.net/ \& Special thanks to Marc Lehmann for creating (and maintaining) Coro .Ve .SH "COPYRIGHT" .IX Header "COPYRIGHT" .Vb 3 \& Copyright (c) 2004\-2011 Brock Wilcox . All \& rights reserved. This program is free software; you can redistribute it \& and/or modify it under the same terms as Perl itself. .Ve