Starman-0.4017000755000765000024 014500415430 13271 5ustar00miyagawastaff000000000000README100644000765000024 1014314500415430 14251 0ustar00miyagawastaff000000000000Starman-0.4017NAME Starman - High-performance preforking PSGI/Plack web server SYNOPSIS # Run app.psgi with the default settings > starman # run with Server::Starter > start_server --port 127.0.0.1:80 -- starman --workers 32 myapp.psgi # UNIX domain sockets > starman --listen /tmp/starman.sock Read more options and configurations by running `perldoc starman` (lower-case s). DESCRIPTION Starman is a PSGI perl web server that has unique features such as: High Performance Uses the fast XS/C HTTP header parser Preforking Spawns workers preforked like most high performance UNIX servers do. Starman also reaps dead children and automatically restarts the worker pool. Signals Supports HUP for graceful worker restarts, and TTIN/TTOU to dynamically increase or decrease the number of worker processes, as well as QUIT to gracefully shutdown the worker processes. Superdaemon aware Supports Server::Starter for hot deploy and graceful restarts. Multiple interfaces and UNIX Domain Socket support Able to listen on multiple interfaces including UNIX sockets. Small memory footprint Preloading the applications with --preload-app command line option enables copy-on-write friendly memory management. Also, the minimum memory usage Starman requires for the master process is 7MB and children (workers) is less than 3.0MB. PSGI compatible Can run any PSGI applications and frameworks HTTP/1.1 support Supports chunked requests and responses, keep-alive and pipeline requests. UNIX only This server does not support Win32. PERFORMANCE Here's a simple benchmark using Hello.psgi. -- server: Starman (workers=10) Requests per second: 6849.16 [#/sec] (mean) -- server: Twiggy Requests per second: 3911.78 [#/sec] (mean) -- server: AnyEvent::HTTPD Requests per second: 2738.49 [#/sec] (mean) -- server: HTTP::Server::PSGI Requests per second: 2218.16 [#/sec] (mean) -- server: HTTP::Server::PSGI (workers=10) Requests per second: 2792.99 [#/sec] (mean) -- server: HTTP::Server::Simple Requests per second: 1435.50 [#/sec] (mean) -- server: Corona Requests per second: 2332.00 [#/sec] (mean) -- server: POE Requests per second: 503.59 [#/sec] (mean) This benchmark was processed with ab -c 10 -t 1 -k on MacBook Pro 13" late 2009 model on Mac OS X 10.6.2 with perl 5.10.0. YMMV. NOTES Because Starman runs as a preforking model, it is not recommended to serve the requests directly from the internet, especially when slow requesting clients are taken into consideration. It is suggested to put Starman workers behind the frontend servers such as nginx, and use HTTP proxy with TCP or UNIX sockets. PSGI EXTENSIONS psgix.informational Starman exposes a callback named psgix.informational that can be used for sending an informational response. The callback accepts two arguments, the first argument being the status code and the second being an arrayref of the headers to be sent. Example below sends an 103 Early Hints response before processing the request to build a final response. sub { my $env = shift; $env->{'psgix.informational'}->( 103, [ "Link" => "; rel=preload" ] ); my $rest = ... $resp; } AUTHOR Tatsuhiko Miyagawa Andy Grundman wrote Catalyst::Engine::HTTP::Prefork, which this module is heavily based on. Kazuho Oku wrote Net::Server::SS::PreFork that makes it easy to add Server::Starter support to this software. The psgix.informational callback comes from Starlet by Kazuho Oku. COPYRIGHT Tatsuhiko Miyagawa, 2010- LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. SEE ALSO Plack Catalyst::Engine::HTTP::Prefork Net::Server::PreFork Changes100644000765000024 1646214500415430 14676 0ustar00miyagawastaff000000000000Starman-0.4017Revision history for Perl extension Starman 0.4017 2023-09-13 13:27:02 PDT - Handle EINTR when doing sysread calls (Rob Mueller) #148 - Requires perl 5.14 0.4016 2022-09-13 10:11:34 PDT - Add psgix.informational callback #146 0.4015 2019-05-20 18:43:46 PDT - Fixed a bug incorrectly handling content body of '0' (olsonanl) #133 0.4014 2015-06-03 12:01:00 PDT - Treat ECONNRESET like EPIPE (i.e. ignore), not as a fatal error #114 (Tim Bunce) 0.4013 2015-05-14 15:01:20 PDT - Fixed some bad git merges. 0.4012 2015-05-14 14:59:48 PDT - Add --net_server-* options to pass directly to Net::Server backend (#109) - Updated documentation 0.4011 2014-11-11 08:07:43 PST - Move the app dispatch into a method #107 0.4010 2014-08-22 09:37:22 PDT - Support --read-timeout #103 (slobo) - Handle Expect header case insensitively #101 (oschwald) 0.4009 2014-04-03 14:39:27 PDT - Do not send chunked body for HEAD requests #87 (therigu) - Added --disable-proctitle option to disable the proctitle change #97 0.4008 2013-09-08 21:09:22 PDT - Make response write loop a zero-copy (ap) 0.4007 2013-09-02 17:11:38 PDT - Handle EPIPE and stops writing to the socket #84 (ap) 0.4006 2013-08-16 12:43:19 PDT - Same as 0.4005, non-devel 0.4005 2013-08-13 22:12:11 PDT - Fix SSL implementation bug where body longer than 16K doesn't get written correctly #78 (kazeburo, siracusa) 0.4004 2013-08-12 11:41:13 PDT - Note that SSL support is experimental 0.4003 2013-08-08 14:32:24 PDT - Fix ssl condition for banner 0.4002 2013-08-08 14:20:45 PDT - Fix banner message for host/port broken in 0.4 (siracusa) 0.4001 2013-07-29 23:14:10 PDT - Skip ssl tests if LWP doesn't support HTTPS 0.4000 2013-07-28 23:53:55 PDT - support SSL (aristotle) 0.3014 2013-06-16 01:07:52 PDT - bump Test::TCP dep, not really necessary but avoid 1.27 bug 0.3013 2013-06-12 22:52:54 PDT - Fix writer tests for Plack update 0.3012 2013-06-12 12:31:11 PDT - typo fixes - Fix dependencies for LWP::UserAgent 0.3011 2013-04-24 17:39:31 PDT - Disabled flock serialization when it's unnecessary. This will improve the performance when you have many Starman worker processes on Linux systems (kazeburo) #69 0.3010 2013-04-24 07:04:02 PDT - Switch back to Module::Build::Tiny with fixed #!perl shebang 0.3009 2013-04-23 17:34:57 PDT - Switch to MakeMaker because of shebang bug https://github.com/Leont/module-build-tiny/issues/3 0.3008 2013-04-06 22:04:53 PDT - switch to Module::Build::Tiny with Milla. Might not install bat correctly but we don't support Win32 anyway. 0.3007 2013-03-28 12:55:45 PDT - Accept HTTP requests with LF termination per HTTP spec recommendation (oschwald) #56 - Documentation fix (oalders) 0.3006 Wed Dec 19 09:55:05 JST 2012 - Clear out @ARGV, rather than restoring it, to avoid messing with Net::Server internals 0.3005 Wed Nov 14 19:46:31 PST 2012 - Added a warning in runtime/documentation to NOT use -r/-R with Starman 0.3004 Thu Nov 8 19:40:45 PST 2012 - Added --interval option to the sample start_server command - Makefile.PL fix 0.3003 Thu Sep 27 09:39:56 JST 2012 - Fixed the test hang in some environments, introduced in 0.3002 [RT:79865] 0.3002 Tue Sep 25 15:26:43 JST 2012 - Added a documentation for --signal-on-term for Server::Starter 0.12 (kazuho, ether) - Set REMOTE_PORT PSGI environment variable #50 (dex4er) - Fix a test failure with a directory containing whitespace (clkao) 0.3001 Mon Jun 25 10:57:20 PDT 2012 - Fix SERVER_NAME and SERVER_PORT not exist on UNIX socket mode #24 - Improved documentation - Ensure that chunk buffer contains terminating HTTP newline (Peter Makholm) 0.3000 Mon Feb 20 16:31:44 PST 2012 - This be a 0.3 release 0.29_90 Thu Dec 1 19:40:52 PST 2011 - Changed the way server handles HUP and QUIT signals HUP will just restart all the workers gracefully QUIT will gracefully shutdown workers and the master See `man 1 starman` and look for SIGNALS section. 0.2014 Sun Sep 18 12:43:06 PDT 2011 - Fixed broken PSGI response headers after the output (cho45) 0.2013 Sat Jun 25 11:51:47 PDT 2011 - Relaxed the harakiri tests (audreyt) 0.2012 Wed Jun 22 13:51:59 PDT 2011 - Implemented psgix.harakiri mode (audreyt) - Added --error-log option (Paulo E. Castro) 0.2011 Tue May 24 09:41:52 PDT 2011 - Fix chunked response with 0-length PSGI array elements (chmrr) 0.2010 Mon Mar 28 16:23:23 PDT 2011 - Fixed packaging. No changes. 0.2009 Fri Mar 25 19:15:23 PDT 2011 - Requires Plack 0.9971 to support localizing $0 to fix the FindBin issues #7, #15, #18, #19 - Calls srand() automatically in the child init hook to avoid a fixed random seed #20 - Implemented --keepalive-timeout which defaults to 1 (acme) 0.2008 Mon Feb 14 17:19:20 PST 2011 - Documented that -E is automatically set to 'deployment' RT:61517 (timbunce) - Check the defined-ness of the input buffer to suppress warnings RT:60007 0.2007 Thu Sep 30 14:09:00 PDT 2010 - Fixed a bug where Date header can be duplicate if the app generates one (spleenjack) 0.2006 Fri Jul 2 17:21:22 PDT 2010 - Fixed a bug in chunked response when Content-Length is 0. #8 (chiba) - Documented --pid and --daemonize 0.2005 Fri Jul 2 17:02:16 PDT 2010 - Don't use lib 'lib' - Documentation updates (miyagawa, grantm) 0.2004 Tue Apr 20 21:22:31 JST 2010 - Delay set $0 in master so FindBin works. #7 0.2003 Mon Apr 19 15:19:06 JST 2010 - Upped Plack dependency 0.2002 Sat Apr 17 18:44:24 PDT 2010 - Switch kyoto.jpg to use baybridge.jpg for testing 0.2001 Tue Apr 13 21:45:15 PDT 2010 - Fixed the way to set the default Delayed loader 0.2000 Tue Apr 13 20:22:24 PDT 2010 - INCOMPATIBLE: starman executable by default loads the application with Delayed to be safer. Use --preload-app command line option to preload the application in the master process. See `starman --help` for details. 0.1007 Tue Apr 13 19:45:59 PDT 2010 - Fixed a bug where Content-Length less response are sent in Keep-Alive without chunked, choking HTTP/1.0 clients (patspam) #6 0.1006 Tue Apr 13 00:01:23 CEST 2010 - Fixed 100% CPU loop when an unexpected EOF happens (Graham Barr) 0.1005 Sun Mar 28 14:37:03 PDT 2010 - Implemented starman -v 0.1004 Sat Mar 27 19:10:06 PDT 2010 - Implemented --disable-keepalive for broken frontend proxy such as mod_proxy + mpm_prefork - Documented --backlog 0.1003 Sun Mar 21 21:08:39 PDT 2010 - Fixed SERVER_PORT when used with Server::Starter (Reported by ronsavage) 0.1002 Wed Mar 10 12:10:46 JST 2010 - Officially do not support Win32 0.1001 Sat Feb 27 05:03:18 PST 2010 - Fix documentations - Set 'deployment' PLACK_ENV by default - Do not reopen stdio for possibly faster operations - require Net::Server 0.91 for new() (sekimura) 0.1000 Mon Feb 15 17:56:33 PST 2010 - original version t000755000765000024 014500415430 13455 5ustar00miyagawastaff000000000000Starman-0.4017ssl.t100644000765000024 235214500415430 14605 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::More; use Test::Requires { 'LWP::Protocol::https' => 6 }; use Test::TCP; use LWP::UserAgent; use FindBin '$Bin'; use Starman::Server; my $host = 'localhost'; my $ca_cert = "$Bin/ssl_ca.pem"; my $server_pem = "$Bin/ssl_key.pem"; my ($success, $status, $content); test_tcp( client => sub { my $port = shift; my $ua = LWP::UserAgent->new( timeout => 2, ssl_opts => { verify_hostname => 1, SSL_ca_file => $ca_cert, }, ); my $res = $ua->get("https://$host:$port"); $success = $res->is_success; $status = $res->status_line; $content = $res->decoded_content; }, server => sub { my $port = shift; Starman::Server->new->run( sub { [ 200, [], [$_[0]{'psgi.url_scheme'}] ] }, { host => $host, port => $port, ssl => 1, ssl_key => $server_pem, ssl_cert => $server_pem, }, ); } ); ok $success, 'HTTPS connection succeeded'; diag $status if not $success; is $content, 'https', '... and URL scheme is reported correctly'; done_testing; LICENSE100644000765000024 4400514500415430 14402 0ustar00miyagawastaff000000000000Starman-0.4017This software is copyright (c) 2010- by Tatsuhiko Miyagawa . This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2010- by Tatsuhiko Miyagawa . This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2010- by Tatsuhiko Miyagawa . This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End cpanfile100644000765000024 73414500415430 15042 0ustar00miyagawastaff000000000000Starman-0.4017requires 'Data::Dump'; requires 'HTTP::Date'; requires 'HTTP::Parser::XS'; requires 'HTTP::Status'; requires 'Net::Server', '2.007'; requires 'Plack', '0.9971'; requires 'Test::TCP', '2.00'; requires 'parent'; requires 'perl', '5.008001'; suggests 'Server::Starter'; suggests 'Net::Server::SS::PreFork'; on test => sub { requires 'Test::More'; requires 'Test::Requires'; requires 'LWP::UserAgent'; }; on develop => sub { requires 'LWP::Protocol::https'; }; dist.ini100644000765000024 1214500415430 14747 0ustar00miyagawastaff000000000000Starman-0.4017[@Milla] Build.PL100644000765000024 25514500415430 14630 0ustar00miyagawastaff000000000000Starman-0.4017# This Build.PL for Starman was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015. use strict; use warnings; use 5.008001; use Module::Build::Tiny 0.034; Build_PL(); META.yml100644000765000024 461214500415430 14626 0ustar00miyagawastaff000000000000Starman-0.4017--- abstract: 'High-performance preforking PSGI/Plack web server' author: - 'Tatsuhiko Miyagawa ' build_requires: LWP::UserAgent: '0' Test::More: '0' Test::Requires: '0' configure_requires: Module::Build::Tiny: '0.034' dynamic_config: 0 generated_by: 'Dist::Milla version v1.0.22, Dist::Zilla version 6.025, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Starman no_index: directory: - eg - examples - inc - share - t - xt requires: Data::Dump: '0' HTTP::Date: '0' HTTP::Parser::XS: '0' HTTP::Status: '0' Net::Server: '2.007' Plack: '0.9971' Test::TCP: '2.00' parent: '0' perl: '5.008001' resources: bugtracker: https://github.com/miyagawa/Starman/issues homepage: https://github.com/miyagawa/Starman repository: https://github.com/miyagawa/Starman.git version: '0.4017' x_contributors: - 'Adam Guthrie ' - 'Alex Vandiver ' - 'Andrew Nelson ' - 'Aristotle Pagaltzis ' - 'Audrey Tang ' - 'Chia-liang Kao ' - 'cho45 ' - 'Damyan Ivanov ' - 'David Steinbrunner ' - 'Graham Barr ' - 'Grant McLean ' - 'Gregory Oschwald ' - 'Jeremy Krieg ' - 'John Siracusa ' - 'Leon Brocard ' - 'Masahiro Nagano ' - 'Olaf Alders ' - 'Paulo E. Castro ' - 'Perlover ' - 'Peter Makholm ' - 'Piotr Roszatycki ' - 'podoleanuciprian <52755740+podoleanuciprian@users.noreply.github.com>' - 'Robert Olson ' - 'Robert Rothenberg ' - 'Robert Sedlacek ' - 'Rob Mueller ' - 'Slobodan Mišković ' - 'spleenjack ' - 'Tatsuhiko Miyagawa ' - 'Tim Bunce ' x_generated_by_perl: v5.34.1 x_serialization_backend: 'YAML::Tiny version 1.73' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' x_static_install: 1 MANIFEST100644000765000024 115114500415430 14501 0ustar00miyagawastaff000000000000Starman-0.4017# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.025. Build.PL Changes LICENSE MANIFEST META.json META.yml README cpanfile dist.ini lib/HTTP/Server/PSGI/Net/Server/PreFork.pm lib/Plack/Handler/Starman.pm lib/Starman.pm lib/Starman/Server.pm script/starman t/00_compile.t t/author-pod-syntax.t t/chunked_req.t t/chunked_termination.t t/chunked_zero_length.t t/early-hints.t t/eintr.t t/expect.t t/findbin.psgi t/harakiri.t t/lf_only_request.t t/no_chunked_head.t t/rand.psgi t/release-findbin.t t/release-rand.t t/single_zero.t t/ssl.t t/ssl_ca.pem t/ssl_key.pem t/ssl_largebody.t t/suite.t eintr.t100644000765000024 321414500415430 15123 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Starman::Server; use Plack::Test; use HTTP::Request; use Test::More; use File::Temp qw(tempfile); use Time::HiRes qw(sleep); my $fh = tempfile; $fh->autoflush(1); # When a child handles our request, write it's pid to the temp file { no warnings 'redefine'; my $old_process_request = \&Starman::Server::process_request; *Starman::Server::process_request = sub { seek $fh, 0, 0; print $fh $$; goto &$old_process_request; }; } $Plack::Test::Impl = "Server"; $ENV{PLACK_SERVER} = 'Starman'; my $app = sub { my $env = shift; my $body; my $clen = $env->{CONTENT_LENGTH}; while ($clen > 0) { $env->{'psgi.input'}->read(my $buf, $clen) or last; $clen -= length $buf; $body .= $buf; } return [ 200, [ 'Content-Type', 'text/plain', 'X-Content-Length', $env->{CONTENT_LENGTH} ], [ $body ] ]; }; test_psgi $app, sub { my $cb = shift; my $c = 0; my $req = HTTP::Request->new(POST => "http://localhost/"); $req->content(sub { $c++; # Send some chunked content return "abcde" if $c == 1; # Child should be processing request, get pid seek $fh, 0, 0; sysread $fh, my $pid, 100; # Ensure child is waiting on a sysread sleep 0.1; kill 'HUP', $pid if $pid; # Ensure child received HUP before sending more data sleep 0.1; # Now send it some more content return "abcde" if $c <= 5; return undef; }); my $res = $cb->($req); # We should have got 5 x 5 bytes or 25 bytes total is $res->header('X-Content-Length'), 25; }; done_testing; suite.t100644000765000024 20514500415430 15110 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use warnings; use Test::More; use Plack::Test::Suite; Plack::Test::Suite->run_server_tests('Starman'); done_testing(); META.json100644000765000024 713214500415430 14776 0ustar00miyagawastaff000000000000Starman-0.4017{ "abstract" : "High-performance preforking PSGI/Plack web server", "author" : [ "Tatsuhiko Miyagawa " ], "dynamic_config" : 0, "generated_by" : "Dist::Milla version v1.0.22, Dist::Zilla version 6.025, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Starman", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "requires" : { "Dist::Milla" : "v1.0.22", "LWP::Protocol::https" : "0", "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "Data::Dump" : "0", "HTTP::Date" : "0", "HTTP::Parser::XS" : "0", "HTTP::Status" : "0", "Net::Server" : "2.007", "Plack" : "0.9971", "Test::TCP" : "2.00", "parent" : "0", "perl" : "5.008001" }, "suggests" : { "Net::Server::SS::PreFork" : "0", "Server::Starter" : "0" } }, "test" : { "requires" : { "LWP::UserAgent" : "0", "Test::More" : "0", "Test::Requires" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/miyagawa/Starman/issues" }, "homepage" : "https://github.com/miyagawa/Starman", "repository" : { "type" : "git", "url" : "https://github.com/miyagawa/Starman.git", "web" : "https://github.com/miyagawa/Starman" } }, "version" : "0.4017", "x_contributors" : [ "Adam Guthrie ", "Alex Vandiver ", "Andrew Nelson ", "Aristotle Pagaltzis ", "Audrey Tang ", "Chia-liang Kao ", "cho45 ", "Damyan Ivanov ", "David Steinbrunner ", "Graham Barr ", "Grant McLean ", "Gregory Oschwald ", "Jeremy Krieg ", "John Siracusa ", "Leon Brocard ", "Masahiro Nagano ", "Olaf Alders ", "Paulo E. Castro ", "Perlover ", "Peter Makholm ", "Piotr Roszatycki ", "podoleanuciprian <52755740+podoleanuciprian@users.noreply.github.com>", "Robert Olson ", "Robert Rothenberg ", "Robert Sedlacek ", "Rob Mueller ", "Slobodan Mi\u0161kovi\u0107 ", "spleenjack ", "Tatsuhiko Miyagawa ", "Tim Bunce " ], "x_generated_by_perl" : "v5.34.1", "x_serialization_backend" : "Cpanel::JSON::XS version 4.27", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later", "x_static_install" : 1 } expect.t100644000765000024 224014500415430 15270 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::TCP; use IO::Socket::INET qw/ SHUT_WR /; use HTTP::Request; use HTTP::Response; use Plack::Loader; use Test::More; $ENV{PLACK_SERVER} = 'Starman'; test_tcp( client => sub { my $port = shift; my $socket = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp' ) or die "Failed to connect to server: $!"; my $req_string = join("\r\n", "POST / HTTP/1.1", "Host: localhost", "Expect: 100-CONTINUE", "Content-Length: 0", "", ""); $socket->send($req_string); $socket->shutdown(SHUT_WR); my $data = ""; while ($socket->connected) { my $buf; $socket->recv($buf, 1024); $data .= $buf; } my @lines = split /\r\n/, $data; is $lines[0], "HTTP/1.1 100 Continue"; is $lines[1], ""; is $lines[2], "HTTP/1.1 200 OK"; }, server => sub { my $port = shift; my $server = Plack::Loader->auto(port => $port, host => '127.0.0.1'); $server->run(sub { return [ 200, [ 'Content-Type', 'text/plain' ], ["ok"] ] }); } ); done_testing; rand.psgi100644000765000024 17014500415430 15403 0ustar00miyagawastaff000000000000Starman-0.4017/trand(); # this initializes the random seed sub { return [ 200, ["Content-Type", "text/plain"], [ rand(100) ] ]; }; harakiri.t100644000765000024 173314500415430 15600 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use warnings; use HTTP::Request::Common; use Plack::Test; use Test::More; $Plack::Test::Impl = 'Server'; $ENV{PLACK_SERVER} = 'Starman'; test_psgi app => sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [$$] ]; }, client => sub { my %seen_pid; my $cb = shift; for (1..23) { my $res = $cb->(GET "/"); $seen_pid{$res->content}++; } cmp_ok(keys(%seen_pid), '<=', 5, 'In non-harakiri mode, pid is reused'); }; test_psgi app => sub { my $env = shift; $env->{'psgix.harakiri.commit'} = 1; return [ 200, [ 'Content-Type' => 'text/plain' ], [$$] ]; }, client => sub { my %seen_pid; my $cb = shift; for (1..23) { my $res = $cb->(GET "/"); $seen_pid{$res->content}++; } is keys(%seen_pid), 23, 'In Harakiri mode, each pid only used once'; }; done_testing; ssl_ca.pem100644000765000024 233514500415430 15567 0ustar00miyagawastaff000000000000Starman-0.4017/t-----BEGIN CERTIFICATE----- MIIDazCCAlOgAwIBAgIUIiI/iRVTq/tVgh0aRA5NdV00hWIwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM BENQQU4xEjAQBgNVBAMMCVR3aWdneSBDQTAeFw0xODEwMjkyMDAyMDJaFw0yOTEw MTEyMDAyMDJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ0w CwYDVQQKDARDUEFOMRIwEAYDVQQDDAlUd2lnZ3kgQ0EwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDBFop2+rRjBb5ljDQf9Nf6RLuGblKkQHlTX0IyWjB2 LMOs8hjvQTcGcj1F4K1C6R6enapyo3oHy3VRXwncFcuIkGsblCiQe7N5eDxXDm3S /OnGit7e4wWXT//Jz4UPqb5E2egE2/UD9vNiMgpLlBtcpocs4ftG9zcAfc30Iti+ +0cY9Xj+2PidGNLGTnLVxBHgcMAuVl56Bln++t4zhzMULnmDLZRn7A4Y2hgASwgi /pEOzhDaotUA3UWxHT5ji3zowYFPEaeCrLaVIhLB03t1kyKI37gyzdQLvfb8zgSV dQfTuUJg3xALQ5SwkJ3YkkB88kzx1a5WmznRU+B0hX8PAgMBAAGjUzBRMB0GA1Ud DgQWBBR6irOqu7DaNMbdFDxkwJbO/ghaoTAfBgNVHSMEGDAWgBR6irOqu7DaNMbd FDxkwJbO/ghaoTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBD 3G4sUS6Oves6Ov/l1qPri02dTF5KZIvnn7PX4vTGq022/q1tRd5tL9uwttSutb6i 5/iqL9JMz1/ExJk4LN5y3Zl7jf5a8wg8Tm/7Wu8gfbSn6ChgbfWcwcrje5ImgsHN evwgQxm2WgXkiqvpnvOramCYSwEyhEBX89hJWaBkuSKuDa/rPpjQrce3DbzzQT3C LOiHppZk5rOyOLLGoZYwWbS2+MfQKz6dQmSrMWwwk5CamPGCDvj1Cwmh/hMY2dNq lzSeT7YFThqDv0XhQxTFhkvXOK8xrQuGLvCFIYGZYbNAd2gCguY5lBELJFJHfBFD 3hn7L7wx4M++rOqlCLPm -----END CERTIFICATE----- ssl_key.pem100644000765000024 554014500415430 15775 0ustar00miyagawastaff000000000000Starman-0.4017/t-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDqNpja2Nj0nyhL LIxILjRoLt7Z8rp96/G7pxgoQ5USJr5y2gx7FfKnalF+UEJdZgjNcIkWWm8aybBI tKXcmFH0axWMGZXsQdNdw2tzWzj8dkiWGpdyMLMBFLlihofyXApHl7oeg+kFCVpA 3ov11Z6p10vVKTd7Do+AAcfsVv/RUJWPS4a+ETQMRzk9xYO6GKt80f0RRkv/VAu1 yvmEfaZAtKhUFrqbKQmISdIjkDbjoWgXc0Lg7NCcf1p7mNBh6UjxCqxo0M++EtPD 7adO+5DsI7LuoLF5Gsj1DBRITkJvGNBROz0S982XROebyDo5NOcIIjipns1A2wUb QrPD83ifAgMBAAECggEAEHxZVAYxdz6AspHnKel+rNqnuwWX1Oc3s7K2Q5dGNene O+1XNY687sXQCKkTksls0zEZpSYmbedGbkew6Q8bra2f7aWbZO7ZNt9bf9oq77JP t6kVgeclomzYA7Ree1O5MYLfiehVDZKz9J71kQ9BRMkvwEhPbG0A8sytSthw10XH UTcr+WBs2MhHTzPHkNon/lea8543aq+ccHwx69E0GKnixIE+wVPYBgwf1ZSdOiHh iPEknoYgzLUbq2etcWfGf9OzEulPsLYvHf/FsU85DmHr78LuBaJJHnw/gD4G/uCk U9iW0K9pnB8dQVdjIB4Qms1yPkdnzSlZYjKZpDhAWQKBgQD/Co0iZpizUkwB4WjJ yboM7CmW/lxoQQ0OgIwFP2Bqy5GK42//MT50z14V7LMkod1ZjYF7DGuWzhZ8mzAH OqUiYVj3vd61J6to4aZ3YGYy9Wn2DQE0YCv0tNQ2Hy5H5X0my55kVhBKGuWuz8gV bcLzGcCgxTh+g4QVRCrnbgWybQKBgQDrGABPVYGXd9Lr6nsYW2FKrxdtuEfJKppH 2Ai2co7WBPtpcm0rQrQQpkQlVr+dLQEs9couZ2o7l10GeIC2cmUQjT3IDqZDirOR DfWD5FJO8kmCL1Lqx8gbaJk1GDmas838LtKVvWQjyD/YmSGU3gaHgCompDYFrSBp Av2dAhTPuwKBgDgM1pWf3KFUTdX/9uqaBeR/JmjDwAU9fqQYRi2FDyiJUpQmhd+c r/A1/qRs37YGSMI8oh8rzJ1Y74I1DOoZzl7u7AGOXdqMPFSReuczbWJ3ovDrTL94 /1AJSbYXRjGQ0t9q0oWUkIHoxV+U1JE5DSYmG7p5fX/4YVfXImK4QFJFAoGBAI3G zLELbPrOrWnO3+theHwUublWkBw2UBKRqzd0QMW6/W+rLUEQmg0MaD5oYWhFctcN Z5+yiDNl5hj35Q+iT2a69w5GH2jOJdqNo4ml0SmBHmmfZo7marx0yShm8p5kXw39 osvhCpo1sqNKPiFMAH4JAeKJ13Z24PvTbOUaEzjvAoGAMMlBaOHyhOUtcNwjg12C IpSZiStcQFOPgOb+4lO6MKyQaU4yp8Y6yXZGuBfZDdYKxXc7g9pGPEu7wYE8JX4a xcixJrkvbq8kMbbS2GcStrDohPAgmptdhHGR00+jGN1EnK40a0nKrFii0RwvucxW FYizcklNMPmFOHuJQopwG+g= -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIDTjCCAjagAwIBAgIUIiI/iRVTq/tVgh0aRA5NdV00hWQwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM BENQQU4xEjAQBgNVBAMMCVR3aWdneSBDQTAeFw0xODEwMjkyMDE3MDhaFw0yOTEw MTEyMDE3MDhaMFsxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ0w CwYDVQQHDARQZXJsMRQwEgYDVQQKDAtUd2lnZ3k6OlRMUzESMBAGA1UEAwwJbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6jaY2tjY9J8o SyyMSC40aC7e2fK6fevxu6cYKEOVEia+ctoMexXyp2pRflBCXWYIzXCJFlpvGsmw SLSl3JhR9GsVjBmV7EHTXcNrc1s4/HZIlhqXcjCzARS5YoaH8lwKR5e6HoPpBQla QN6L9dWeqddL1Sk3ew6PgAHH7Fb/0VCVj0uGvhE0DEc5PcWDuhirfNH9EUZL/1QL tcr5hH2mQLSoVBa6mykJiEnSI5A246FoF3NC4OzQnH9ae5jQYelI8QqsaNDPvhLT w+2nTvuQ7COy7qCxeRrI9QwUSE5CbxjQUTs9EvfNl0Tnm8g6OTTnCCI4qZ7NQNsF G0Kzw/N4nwIDAQABoyAwHjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAN BgkqhkiG9w0BAQsFAAOCAQEAVOEsMwoWZozYM102nwwRDveaqsLmQUIq6CfAQ1tC 6WtxtV3ZpydfRJauwNEa1tPtWn7RrqHttZwwG5YD8TvxqJvX18nJXQz8GUef2L65 ZxPoiZOd+pL7eB9P7Hscyp2q76JWJDd0j86QMoL8P9MqZ6J1z4UgoeaDVX1YDRe9 WBtgI+sy4O+v1uPHNv1yT3mG4cTrc8PKFh5G9KmqMAAqha9HslaEWHwz7mXJxOS0 PegBGFjjIw/S6XV5kf7ZLbBnZDRKah9BPxL/K8lmZnBReAJaNMVkE/1KA/5aNp4J 3m9c4DXL+svrQim8nFG3TizjbPkmuRnts5+jwSe5IB3bvQ== -----END CERTIFICATE----- lib000755000765000024 014500415430 13760 5ustar00miyagawastaff000000000000Starman-0.4017Starman.pm100644000765000024 1004714500415430 16105 0ustar00miyagawastaff000000000000Starman-0.4017/libpackage Starman; use strict; use 5.008_001; our $VERSION = '0.4017'; 1; __END__ =encoding utf-8 =for stopwords =head1 NAME Starman - High-performance preforking PSGI/Plack web server =head1 SYNOPSIS # Run app.psgi with the default settings > starman # run with Server::Starter > start_server --port 127.0.0.1:80 -- starman --workers 32 myapp.psgi # UNIX domain sockets > starman --listen /tmp/starman.sock Read more options and configurations by running `perldoc starman` (lower-case s). =head1 DESCRIPTION Starman is a PSGI perl web server that has unique features such as: =over 4 =item High Performance Uses the fast XS/C HTTP header parser =item Preforking Spawns workers preforked like most high performance UNIX servers do. Starman also reaps dead children and automatically restarts the worker pool. =item Signals Supports C for graceful worker restarts, and C/C to dynamically increase or decrease the number of worker processes, as well as C to gracefully shutdown the worker processes. =item Superdaemon aware Supports L for hot deploy and graceful restarts. =item Multiple interfaces and UNIX Domain Socket support Able to listen on multiple interfaces including UNIX sockets. =item Small memory footprint Preloading the applications with C<--preload-app> command line option enables copy-on-write friendly memory management. Also, the minimum memory usage Starman requires for the master process is 7MB and children (workers) is less than 3.0MB. =item PSGI compatible Can run any PSGI applications and frameworks =item HTTP/1.1 support Supports chunked requests and responses, keep-alive and pipeline requests. =item UNIX only This server does not support Win32. =back =head1 PERFORMANCE Here's a simple benchmark using C. -- server: Starman (workers=10) Requests per second: 6849.16 [#/sec] (mean) -- server: Twiggy Requests per second: 3911.78 [#/sec] (mean) -- server: AnyEvent::HTTPD Requests per second: 2738.49 [#/sec] (mean) -- server: HTTP::Server::PSGI Requests per second: 2218.16 [#/sec] (mean) -- server: HTTP::Server::PSGI (workers=10) Requests per second: 2792.99 [#/sec] (mean) -- server: HTTP::Server::Simple Requests per second: 1435.50 [#/sec] (mean) -- server: Corona Requests per second: 2332.00 [#/sec] (mean) -- server: POE Requests per second: 503.59 [#/sec] (mean) This benchmark was processed with C on MacBook Pro 13" late 2009 model on Mac OS X 10.6.2 with perl 5.10.0. YMMV. =head1 NOTES Because Starman runs as a preforking model, it is not recommended to serve the requests directly from the internet, especially when slow requesting clients are taken into consideration. It is suggested to put Starman workers behind the frontend servers such as nginx, and use HTTP proxy with TCP or UNIX sockets. =head1 PSGI EXTENSIONS =head2 psgix.informational Starman exposes a callback named C that can be used for sending an informational response. The callback accepts two arguments, the first argument being the status code and the second being an arrayref of the headers to be sent. Example below sends an 103 Early Hints response before processing the request to build a final response. sub { my $env = shift; $env->{'psgix.informational'}->( 103, [ "Link" => "; rel=preload" ] ); my $rest = ... $resp; } =head1 AUTHOR Tatsuhiko Miyagawa Emiyagawa@bulknews.netE Andy Grundman wrote L, which this module is heavily based on. Kazuho Oku wrote L that makes it easy to add L support to this software. The C callback comes from L by Kazuho Oku. =head1 COPYRIGHT Tatsuhiko Miyagawa, 2010- =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L L L =cut script000755000765000024 014500415430 14516 5ustar00miyagawastaff000000000000Starman-0.4017starman100755000765000024 2211514500415430 16272 0ustar00miyagawastaff000000000000Starman-0.4017/script#!perl use strict; use Plack::Runner; sub version { require Starman; print "Starman $Starman::VERSION\n"; } my $preload_app; require Getopt::Long; Getopt::Long::Configure("no_ignore_case", "no_auto_abbrev", "pass_through"); Getopt::Long::GetOptions( "preload-app" => \$preload_app, ); my @args = (server => 'Starman', env => 'deployment', version_cb => \&version); if (!$preload_app) { push @args, 'loader' => 'Delayed'; } my @argv = @ARGV; my $runner = Plack::Runner->new(@args); $runner->parse_options(@argv); if ($runner->{loader} eq 'Restarter') { warn <set_options(argv => \@argv); $runner->run; __END__ =head1 NAME starman - Starman launcher =head1 SYNOPSIS starman --listen :5001 --listen /tmp/starman.sock starman --workers 32 --port 8080 =head1 OPTIONS =over 4 =item -l, --listen --listen HOST:PORT --listen :PORT --listen UNIX_SOCKET --listen HOST:PORT:ssl Specifies the TCP address, ports and UNIX domain sockets to bind to wait for requests. You can repeat as many times as you want and mix TCP and UNIX domain sockets. For TCP sockets you can append C<:ssl> after the port to specify that connections on that port should use SSL. Note that the SSL support is experimental and hasn't been widely tested. Defaults to any IP address and port 5000. =item --host --host 127.0.0.1 Specifies the address to bind. This option is for a compatibility with L and you're recommended to use C<--listen> instead. =item --port --port 8080 Specifies the port to bind. This option is for a compatibility with L and you're recommended to use C<--listen> instead. =item -S, --socket -S /tmp/starman.sock Specifies the path to UNIX domain socket to bind. This option is for a compatibility with L and you're recommended to use C<--listen> instead. =item --workers Specifies the number of worker pool. Defaults to 5. Starman by default sets up other spare server configuration based on this workers value, making sure there are B C worker processes running. So even if there're no idle workers, Starman won't spawn off spare processes since that's mostly what you want to do by fine tuning the memory usage etc. in the production environment. =item --backlog Specifies the number of backlog (listen queue size) of listener sockets. Defaults to 1024. On production systems, setting a very low value can allow failover on frontend proxy (like nginx) to happen more quickly, if you have multiple Starman clusters. If you're doing simple benchmarks and getting connection errors, increasing this parameter can help avoid them. You should also consider increasing C. Note that this is not recommended for real production system if you have another cluster to failover (see above). =item --max-requests Number of the requests to process per one worker process. Defaults to 1000. =item --preload-app This option lets Starman preload the specified PSGI application in the master parent process before preforking children. This allows memory savings with copy-on-write memory management. When not set (default), forked children loads the application in the initialization hook. Enabling this option can cause bad things happen when resources like sockets or database connections are opened at load time by the master process and shared by multiple children. Since Starman 0.2000, this option defaults to false, and you should explicitly set this option to preload the application in the master process. Alternatively, you can use -M command line option (plackup's common option) to preload the I rather than the itself. starman -MCatalyst -MDBIx::Class myapp.psgi will load the modules in the master process for memory savings with CoW, but the actual loading of C is done per children, allowing resource managements such as database connection safer. If you enable this option, sending C signal to the master process I pick up any code changes you make. See L for details. =item --disable-keepalive Disable Keep-alive persistent connections. It is an useful workaround if you run Starman behind a broken frontend proxy that tries to pool connections more than a number of backend workers (i.e. Apache mpm_prefork + mod_proxy). =item --keepalive-timeout The number of seconds Starman will wait for a subsequent request before closing the connection if Keep-alive persistent connections are enabled. Setting this to a high value may cause performance problems in heavily loaded servers. The higher the timeout, the more backend workers will be kept occupied waiting on connections with idle clients. Defaults to 1. =item --read-timeout The number of seconds Starman will wait for a request on a new connection before closing it. Setting this to a high value may cause performance problems in heavily loaded servers. The higher the timeout, the more backend workers will be kept occupied waiting on connections with idle clients. You may need this if your proxy / load balancer likes to keep a pool of open connections while waiting for clients (eg. Amazon ELB). Defaults to 5. =item --user To listen on a low-numbered (E1024) port, it will be necessary to start the server as root. Use the C<--user> option to specify a userid or username that the server process should switch to after binding to the port. Defaults to the current userid. =item --group Specify the group id or group name that the server should switch to after binding to the port. This option is usually used with C<--user>. Defaults to the current group id. =item --pid Specify the pid file path. Use it with C<-D|--daemonize> option, described in C. =item --error-log Specify the pathname of a file where the error log should be written. This enables you to still have access to the errors when using C<--daemonize>. =item --ssl-cert Specify the path to SSL certificate file. =item --ssl-key Specify the path to SSL key file. =item --enable-ssl Enable SSL on I TCP sockets. This is an experimental feature. =item --disable-proctitle Disable the behavior to set proctitle to "starman (master)" and "starman (worker)" respectively on master and workers. =back Starman passes through other options given to L, the common backend that L uses, so the most options explained in C such as C<--access-log> or C<--daemonize> works fine in starman too. Setting the environment variable C to 1 makes the Starman server running in the debug mode. =cut =head1 SIGNALS =over 4 =item HUP Sending C signal to the master process will restart all the workers gracefully (meaning the currently running requests will shut down once the request is complete), and by default, the workers will pick up the code changes you make by reloading the application. If you enable C<--preload-app> option, however, the code will be only loaded in the startup process and will not pick up the code changes you made. If you want to preload the app I do graceful restarts by reloading the code changes, you're recommended to use L, configured to send C signal when superdaemon received C, i.e: start_server --interval 5 --port 8080 --signal-on-hup=QUIT -- \ starman --preload-app myapp.psgi You will then send the HUP signal to C process to gracefully reload the starman cluster (master and workers). With Server::Starter 0.12 or later, you should also be able to set C<--signal-on-term> to QUIT so that you can safely shutdown Starman first and then stop the C daemon process as well. =item TTIN, TTOU Sending C signal to the master process will dynamically increase the number of workers, and C signal will decrease it. =item INT, TERM Sending C or C signal to the master process will kill all the workers immediately and shut down the server. =item QUIT Sending C signal to the master process will gracefully shutdown the workers (meaning the currently running requests will shut down once the request is complete). =back =head1 RELOADING THE APPLICATION You're recommended to use signals (see above) to reload the application, and are strongly discouraged to use C<-r> or C<-R> (reloading flag) from plackup. These options will make a separate directory watcher process, and makes your life difficult if you want to combine with other process daemon tools such as L. =head1 DIFFERENCES WITH PLACKUP C executable is basically the equivalent of using C with C server handler i.e. C, except that C delay loads the application with the Delayed loader by default, which can be disabled with C<--preload-app>. C command also automatically sets the environment (C<-E>) to the value of I. You're recommended to use C unless there's a reason to stick to C for compatibility. =head1 SEE ALSO L =cut 00_compile.t100644000765000024 10314500415430 15703 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::More tests => 1; BEGIN { use_ok 'Starman' } findbin.psgi100644000765000024 20014500415430 16062 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use FindBin; sub { my $env = shift; return [ 200, [ "Content-Type", "text/plain" ], [ $FindBin::Bin ] ]; }; chunked_req.t100644000765000024 202014500415430 16264 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Plack::Test; use File::ShareDir; use HTTP::Request; use Test::More; use Digest::MD5; $Plack::Test::Impl = "Server"; $ENV{PLACK_SERVER} = 'Starman'; my $file = File::ShareDir::dist_dir('Plack') . "/baybridge.jpg"; open my $fh, "<", $file or die $!; my $md5 = Digest::MD5->new; $md5->addfile($fh); my $hex = $md5->hexdigest; my $app = sub { my $env = shift; my $body; my $clen = $env->{CONTENT_LENGTH}; while ($clen > 0) { $env->{'psgi.input'}->read(my $buf, $clen) or last; $clen -= length $buf; $body .= $buf; } return [ 200, [ 'Content-Type', 'text/plain', 'X-Content-Length', $env->{CONTENT_LENGTH} ], [ $body ] ]; }; test_psgi $app, sub { my $cb = shift; open my $fh, "<:raw", $file; local $/ = \1024; my $req = HTTP::Request->new(POST => "http://localhost/"); $req->content(sub { scalar <$fh> }); my $res = $cb->($req); is $res->header('X-Content-Length'), -s $file; is Digest::MD5::md5_hex($res->content), $hex; }; done_testing; early-hints.t100644000765000024 253214500415430 16243 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::TCP; use IO::Socket::INET qw/ SHUT_WR /; use HTTP::Request; use HTTP::Response; use Plack::Loader; use Test::More; $ENV{PLACK_SERVER} = 'Starman'; test_tcp( client => sub { my $port = shift; my $socket = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp' ) or die "Failed to connect to server: $!"; my $req_string = join("\r\n", "GET / HTTP/1.1", "Host: localhost", "", ""); $socket->send($req_string); $socket->shutdown(SHUT_WR); my $data = ""; while ($socket->connected) { my $buf; $socket->recv($buf, 1024); $data .= $buf; } my @lines = split /\r\n/, $data; is $lines[0], "HTTP/1.1 103 Early Hints"; is $lines[1], "Link: ; rel=preload"; is $lines[2], ""; is $lines[3], "HTTP/1.1 200 OK"; }, server => sub { my $port = shift; my $server = Plack::Loader->auto(port => $port, host => '127.0.0.1'); $server->run(sub { my $env = shift; $env->{'psgix.informational'}->( 103, [ "Link" => "; rel=preload" ] ); return [ 200, [ 'Content-Type', 'text/plain' ], ["ok"] ] }); } ); done_testing; single_zero.t100644000765000024 120514500415430 16320 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Plack::Test; use Plack::Request; use HTTP::Request; use Test::More; $Plack::Test::Impl = "Server"; $ENV{PLACK_SERVER} = 'Starman'; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); is $req->content, "0"; return sub { my $response = shift; my $writer = $response->([ 200, [ 'Content-Type', 'text/plain' ]]); $writer->write("ok"); $writer->close; } }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new(POST => "http://localhost/"); $req->content('0'); my $res = $cb->($req); is $res->content, "ok"; }; done_testing; release-rand.t100644000765000024 131314500415430 16342 0ustar00miyagawastaff000000000000Starman-0.4017/t BEGIN { unless ($ENV{RELEASE_TESTING}) { print qq{1..0 # SKIP these tests are for release candidate testing\n}; exit } } use Test::TCP; use LWP::UserAgent; use FindBin; use Test::More; for (1..2) { # preload, non-preload my @preload = $_ == 1 ? ("--preload-app") : (); my $s = Test::TCP->new( code => sub { my $port = shift; exec $^X, "script/starman", @preload, "--port", $port, "--max-requests=1", "--workers=1", "t/rand.psgi"; }, ); my $ua = LWP::UserAgent->new; my @res; for (1..2) { push @res, $ua->get("http://localhost:" . $s->port); } isnt $res[0]->content, $res[1]->content; undef $s; } done_testing; ssl_largebody.t100644000765000024 237314500415430 16640 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::More; use Test::Requires { 'LWP::Protocol::https' => 6 }; use Test::TCP; use LWP::UserAgent; use FindBin '$Bin'; use Starman::Server; # https://github.com/miyagawa/Starman/issues/78 my $host = 'localhost'; my $ca_cert = "$Bin/ssl_ca.pem"; my $server_pem = "$Bin/ssl_key.pem"; my $body = 'x'x32*1024; # > 16KB my ($success, $status, $content); test_tcp( client => sub { my $port = shift; my $ua = LWP::UserAgent->new( timeout => 2, ssl_opts => { verify_hostname => 1, SSL_ca_file => $ca_cert, }, ); my $res = $ua->get("https://$host:$port"); $success = $res->is_success; $status = $res->status_line; $content = $res->decoded_content; }, server => sub { my $port = shift; Starman::Server->new->run( sub { [ 200, [], [$body] ] }, { host => $host, port => $port, ssl => 1, ssl_key => $server_pem, ssl_cert => $server_pem, }, ); } ); ok $success, 'HTTPS connection succeeded'; diag $status if not $success; is $content, $body; done_testing; lf_only_request.t100644000765000024 142114500415430 17212 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use warnings; use Plack::Test; use HTTP::Request; use Test::More; { package Starman::Server; # override so we can mangle the HTTP request use subs 'sysread'; *Starman::Server::sysread = sub { my $read = CORE::sysread( $_[0], $_[1], $_[2] ); $_[1] =~ s/\r\n/\n/g; return $read; }; } $Plack::Test::Impl = "Server"; $ENV{PLACK_SERVER} = 'Starman'; my $app = sub { my $env = shift; return sub { my $response = shift; my $writer = $response->( [ 200, [ 'Content-Type', 'text/plain' ] ] ); $writer->close; } }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new( GET => "http://localhost/" ); my $res = $cb->($req); is $res->code, 200; }; done_testing; no_chunked_head.t100644000765000024 206214500415430 17100 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::TCP; use IO::Socket::INET; use HTTP::Request; use HTTP::Response; use Plack::Loader; use Test::More; $ENV{PLACK_SERVER} = 'Starman'; test_tcp( client => sub { my $port = shift; my $socket = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp' ) or die "Failed to connect to server: $!"; my $request = HTTP::Request->new( HEAD => '/', [ Host => 'localhost' ] ); $request->protocol('HTTP/1.1'); $socket->send($request->as_string("\r\n")); $socket->shutdown(1); my $data; while ($socket->connected) { my $buf; $socket->recv($buf, 1024); $data .= $buf; } my $res = HTTP::Response->parse($data); is $res->content, ''; }, server => sub { my $port = shift; my $server = Plack::Loader->auto(port => $port, host => '127.0.0.1'); $server->run(sub { return [ 200, [], [] ] }); } ); done_testing; release-findbin.t100644000765000024 105114500415430 17026 0ustar00miyagawastaff000000000000Starman-0.4017/t BEGIN { unless ($ENV{RELEASE_TESTING}) { print qq{1..0 # SKIP these tests are for release candidate testing\n}; exit } } use Test::TCP; use LWP::UserAgent; use FindBin; use Test::More; my $s = Test::TCP->new( code => sub { my $port = shift; exec $^X, "script/starman", "--port", $port, "--max-requests=1", "--workers=1", "t/findbin.psgi"; }, ); my $ua = LWP::UserAgent->new(timeout => 3); for (1..2) { my $res = $ua->get("http://localhost:" . $s->port); is $res->content, $FindBin::Bin; } done_testing; Starman000755000765000024 014500415430 15365 5ustar00miyagawastaff000000000000Starman-0.4017/libServer.pm100644000765000024 4442214500415430 17357 0ustar00miyagawastaff000000000000Starman-0.4017/lib/Starmanpackage Starman::Server; use strict; use base 'Net::Server::PreFork'; use Data::Dump qw(dump); use Socket qw(IPPROTO_TCP TCP_NODELAY); use IO::Socket qw(:crlf); use HTTP::Parser::XS qw(parse_http_request); use HTTP::Status qw(status_message); use HTTP::Date qw(time2str); use POSIX qw(EINTR EPIPE ECONNRESET); use Symbol; use Plack::Util; use Plack::TempBuffer; use constant DEBUG => $ENV{STARMAN_DEBUG} || 0; use constant CHUNKSIZE => 64 * 1024; my $null_io = do { open my $io, "<", \""; $io }; use Net::Server::SIG qw(register_sig); # Override Net::Server's HUP handling - just restart all the workers and that's about it sub sig_hup { my $self = shift; $self->hup_children; } sub run { my($self, $app, $options) = @_; $self->{app} = $app; $self->{options} = $options; my %extra = (); if ($options->{net_server_args}) { %extra = %{ $options->{net_server_args} }; } if ( $options->{pid} ) { $extra{pid_file} = $options->{pid}; } if ( $options->{daemonize} ) { $extra{setsid} = $extra{background} = 1; } if ( $options->{error_log} ) { $extra{log_file} = $options->{error_log}; } if ( DEBUG ) { $extra{log_level} = 4; } if ( $options->{ssl_cert} ) { $extra{SSL_cert_file} = $options->{ssl_cert}; } if ( $options->{ssl_key} ) { $extra{SSL_key_file} = $options->{ssl_key}; } if (! exists $options->{keepalive}) { $options->{keepalive} = 1; } if (! exists $options->{keepalive_timeout}) { $options->{keepalive_timeout} = 1; } if (! exists $options->{read_timeout}) { $options->{read_timeout} = 5; } if (! exists $options->{proctitle}) { $options->{proctitle} = 1; } my @port; for my $listen (@{$options->{listen} || [ "$options->{host}:$options->{port}" ]}) { my %listen; if ($listen =~ /:/) { my($h, $p, $opt) = split /:/, $listen, 3; $listen{host} = $h if $h; $listen{port} = $p; $listen{proto} = 'ssl' if defined $opt && lc $opt eq 'ssl'; } else { %listen = ( host => 'localhost', port => $listen, proto => 'unix', ); } push @port, \%listen; } my $workers = $options->{workers} || 5; local @ARGV = (); $self->SUPER::run( port => \@port, host => '*', # default host proto => $options->{ssl} ? 'ssl' : 'tcp', # default proto serialize => ( $^O =~ m!(linux|darwin|bsd|cygwin)$! ) ? 'none' : 'flock', min_servers => $options->{min_servers} || $workers, min_spare_servers => $options->{min_spare_servers} || $workers - 1, max_spare_servers => $options->{max_spare_servers} || $workers - 1, max_servers => $options->{max_servers} || $workers, max_requests => $options->{max_requests} || 1000, user => $options->{user} || $>, group => $options->{group} || $), listen => $options->{backlog} || 1024, check_for_waiting => 1, no_client_stdout => 1, %extra ); } sub pre_loop_hook { my $self = shift; my $port = $self->{server}->{port}->[0]; my $proto = $port->{proto} eq 'ssl' ? 'https' : $port->{proto} eq 'unix' ? 'unix' : 'http'; $self->{options}{server_ready}->({ host => $port->{host}, port => $port->{port}, proto => $proto, server_software => 'Starman', }) if $self->{options}{server_ready}; register_sig( TTIN => sub { $self->{server}->{$_}++ for qw( min_servers max_servers ) }, TTOU => sub { $self->{server}->{$_}-- for qw( min_servers max_servers ) }, QUIT => sub { $self->server_close(1) }, ); } sub server_close { my($self, $quit) = @_; if ($quit) { $self->log(2, $self->log_time . " Received QUIT. Running a graceful shutdown\n"); $self->{server}->{$_} = 0 for qw( min_servers max_servers ); $self->hup_children; while (1) { Net::Server::SIG::check_sigs(); $self->coordinate_children; last if !keys %{$self->{server}{children}}; sleep 1; } $self->log(2, $self->log_time . " Worker processes cleaned up\n"); } $self->SUPER::server_close(); } sub run_parent { my $self = shift; $0 = "starman master " . join(" ", @{$self->{options}{argv} || []}) if $self->{options}{proctitle}; no warnings 'redefine'; local *Net::Server::PreFork::register_sig = sub { my %args = @_; delete $args{QUIT}; Net::Server::SIG::register_sig(%args); }; $self->SUPER::run_parent(@_); } # The below methods run in the child process sub child_init_hook { my $self = shift; srand(); if ($self->{options}->{psgi_app_builder}) { DEBUG && warn "[$$] Initializing the PSGI app\n"; $self->{app} = $self->{options}->{psgi_app_builder}->(); } $0 = "starman worker " . join(" ", @{$self->{options}{argv} || []}) if $self->{options}{proctitle}; } sub post_accept_hook { my $self = shift; $self->{client} = { headerbuf => '', inputbuf => '', keepalive => 1, }; } sub dispatch_request { my ($self, $env) = @_; # Run PSGI apps my $res = Plack::Util::run_app($self->{app}, $env); if (ref $res eq 'CODE') { $res->(sub { $self->_finalize_response($env, $_[0]) }); } else { $self->_finalize_response($env, $res); } } sub process_request { my $self = shift; my $conn = $self->{server}->{client}; if ($conn->NS_proto eq 'TCP') { setsockopt($conn, IPPROTO_TCP, TCP_NODELAY, 1) or die $!; } while ( $self->{client}->{keepalive} ) { last if !$conn->connected; # Read until we see all headers last if !$self->_read_headers; my $env = { REMOTE_ADDR => $self->{server}->{peeraddr}, REMOTE_HOST => $self->{server}->{peerhost} || $self->{server}->{peeraddr}, REMOTE_PORT => $self->{server}->{peerport} || 0, SERVER_NAME => $self->{server}->{sockaddr} || 0, # XXX: needs to be resolved? SERVER_PORT => $self->{server}->{sockport} || 0, SCRIPT_NAME => '', 'psgi.version' => [ 1, 1 ], 'psgi.errors' => *STDERR, 'psgi.url_scheme' => ($conn->NS_proto eq 'SSL' ? 'https' : 'http'), 'psgi.nonblocking' => Plack::Util::FALSE, 'psgi.streaming' => Plack::Util::TRUE, 'psgi.run_once' => Plack::Util::FALSE, 'psgi.multithread' => Plack::Util::FALSE, 'psgi.multiprocess' => Plack::Util::TRUE, 'psgix.io' => $conn, 'psgix.input.buffered' => Plack::Util::TRUE, 'psgix.harakiri' => Plack::Util::TRUE, 'psgix.informational' => sub { _write_informational($conn, @_) }, }; # Parse headers my $reqlen = parse_http_request(delete $self->{client}->{headerbuf}, $env); if ( $reqlen == -1 ) { # Bad request DEBUG && warn "[$$] Bad request\n"; $self->_http_error(400, { SERVER_PROTOCOL => "HTTP/1.0" }); last; } # Initialize PSGI environment # Determine whether we will keep the connection open after the request my $connection = delete $env->{HTTP_CONNECTION}; my $proto = $env->{SERVER_PROTOCOL}; if ( $proto && $proto eq 'HTTP/1.0' ) { if ( $connection && $connection =~ /^keep-alive$/i ) { # Keep-alive only with explicit header in HTTP/1.0 $self->{client}->{keepalive} = 1; } else { $self->{client}->{keepalive} = 0; } } elsif ( $proto && $proto eq 'HTTP/1.1' ) { if ( $connection && $connection =~ /^close$/i ) { $self->{client}->{keepalive} = 0; } else { # Keep-alive assumed in HTTP/1.1 $self->{client}->{keepalive} = 1; } # Do we need to send 100 Continue? if ( $env->{HTTP_EXPECT} ) { if ( lc $env->{HTTP_EXPECT} eq '100-continue' ) { _syswrite($conn, \('HTTP/1.1 100 Continue' . $CRLF . $CRLF)); DEBUG && warn "[$$] Sent 100 Continue response\n"; } else { DEBUG && warn "[$$] Invalid Expect header, returning 417\n"; $self->_http_error( 417, $env ); last; } } unless ($env->{HTTP_HOST}) { # No host, bad request DEBUG && warn "[$$] Bad request, HTTP/1.1 without Host header\n"; $self->_http_error( 400, $env ); last; } } unless ($self->{options}->{keepalive}) { DEBUG && warn "[$$] keep-alive is disabled. Closing the connection after this request\n"; $self->{client}->{keepalive} = 0; } $self->_prepare_env($env); $self->dispatch_request($env); DEBUG && warn "[$$] Request done\n"; if ( $self->{client}->{keepalive} ) { # If we still have data in the input buffer it may be a pipelined request if ( $self->{client}->{inputbuf} ne '' ) { if ( $self->{client}->{inputbuf} =~ /^(?:GET|HEAD)/ ) { if ( DEBUG ) { warn "Pipelined GET/HEAD request in input buffer: " . dump( $self->{client}->{inputbuf} ) . "\n"; } # Continue processing the input buffer next; } else { # Input buffer just has junk, clear it if ( DEBUG ) { warn "Clearing junk from input buffer: " . dump( $self->{client}->{inputbuf} ) . "\n"; } $self->{client}->{inputbuf} = ''; } } DEBUG && warn "[$$] Waiting on previous connection for keep-alive request...\n"; my $sel = IO::Select->new($conn); last unless $sel->can_read($self->{options}->{keepalive_timeout}); } } DEBUG && warn "[$$] Closing connection\n"; } sub _read_headers { my $self = shift; eval { local $SIG{ALRM} = sub { die "Timed out\n"; }; alarm( $self->{options}->{read_timeout} ); while (1) { # Do we have a full header in the buffer? # This is before sysread so we don't read if we have a pipelined request # waiting in the buffer last if $self->{client}->{inputbuf} ne '' && $self->{client}->{inputbuf} =~ /$CR?$LF$CR?$LF/s; # If not, read some data my $read = _sysread($self->{server}->{client}, my $buf, CHUNKSIZE); if ( !defined $read || $read == 0 ) { die "Read error: $!\n"; } if ( DEBUG ) { warn "[$$] Read $read bytes: " . dump($buf) . "\n"; } $self->{client}->{inputbuf} .= $buf; } }; alarm(0); if ( $@ ) { if ( $@ =~ /Timed out/ ) { DEBUG && warn "[$$] Client connection timed out\n"; return; } if ( $@ =~ /Read error/ ) { DEBUG && warn "[$$] Read error: $!\n"; return; } } # Pull out the complete header into a new buffer $self->{client}->{headerbuf} = $self->{client}->{inputbuf}; # Save any left-over data, possibly body data or pipelined requests $self->{client}->{inputbuf} =~ s/.*?$CR?$LF$CR?$LF//s; return 1; } sub _http_error { my ( $self, $code, $env ) = @_; my $status = $code || 500; my $msg = status_message($status); my $res = [ $status, [ 'Content-Type' => 'text/plain', 'Content-Length' => length($msg) ], [ $msg ], ]; $self->{client}->{keepalive} = 0; $self->_finalize_response($env, $res); } sub _prepare_env { my($self, $env) = @_; my $get_chunk = sub { if ($self->{client}->{inputbuf} ne '') { my $chunk = delete $self->{client}->{inputbuf}; return ($chunk, length $chunk); } my $read = _sysread($self->{server}->{client}, my($chunk), CHUNKSIZE); return ($chunk, $read); }; my $chunked = do { no warnings; lc delete $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' }; if (my $cl = $env->{CONTENT_LENGTH}) { my $buf = Plack::TempBuffer->new($cl); while ($cl > 0) { my($chunk, $read) = $get_chunk->(); if ( !defined $read || $read == 0 ) { die "Read error: $!\n"; } $cl -= $read; $buf->print($chunk); } $env->{'psgi.input'} = $buf->rewind; } elsif ($chunked) { my $buf = Plack::TempBuffer->new; my $chunk_buffer = ''; my $length; DECHUNK: while (1) { my($chunk, $read) = $get_chunk->(); $chunk_buffer .= $chunk; while ( $chunk_buffer =~ s/^(([0-9a-fA-F]+).*\015\012)// ) { my $trailer = $1; my $chunk_len = hex $2; if ($chunk_len == 0) { last DECHUNK; } elsif (length $chunk_buffer < $chunk_len + 2) { $chunk_buffer = $trailer . $chunk_buffer; last; } $buf->print(substr $chunk_buffer, 0, $chunk_len, ''); $chunk_buffer =~ s/^\015\012//; $length += $chunk_len; } last unless $read && $read > 0; } $env->{CONTENT_LENGTH} = $length; $env->{'psgi.input'} = $buf->rewind; } else { $env->{'psgi.input'} = $null_io; } } sub _finalize_response { my($self, $env, $res) = @_; if ($env->{'psgix.harakiri.commit'}) { $self->{client}->{keepalive} = 0; $self->{client}->{harakiri} = 1; } my $protocol = $env->{SERVER_PROTOCOL}; my $status = $res->[0]; my $message = status_message($status); my(@headers, %headers); push @headers, "$protocol $status $message"; # Switch on Transfer-Encoding: chunked if we don't know Content-Length. my $chunked; my $headers = $res->[1]; for (my $i = 0; $i < @$headers; $i += 2) { my $k = $headers->[$i]; my $v = $headers->[$i + 1]; next if $k eq 'Connection'; push @headers, "$k: $v"; $headers{lc $k} = $v; } if ( $protocol eq 'HTTP/1.1' ) { if ( !exists $headers{'content-length'} ) { if ( $status !~ /^1\d\d|[23]04$/ && $env->{REQUEST_METHOD} ne 'HEAD' ) { DEBUG && warn "[$$] Using chunked transfer-encoding to send unknown length body\n"; push @headers, 'Transfer-Encoding: chunked'; $chunked = 1; } } elsif ( my $te = $headers{'transfer-encoding'} ) { if ( $te eq 'chunked' ) { DEBUG && warn "[$$] Chunked transfer-encoding set for response\n"; $chunked = 1; } } } else { if ( !exists $headers{'content-length'} ) { DEBUG && warn "[$$] Disabling keep-alive after sending unknown length body on $protocol\n"; $self->{client}->{keepalive} = 0; } } if ( ! $headers{date} ) { push @headers, "Date: " . time2str( time() ); } # Should we keep the connection open? if ( $self->{client}->{keepalive} ) { push @headers, 'Connection: keep-alive'; } else { push @headers, 'Connection: close'; } my $conn = $self->{server}->{client}; # Buffer the headers so they are sent with the first write() call # This reduces the number of TCP packets we are sending _syswrite($conn, \(join( $CRLF, @headers, '' ) . $CRLF)); if (defined $res->[2]) { Plack::Util::foreach($res->[2], sub { my $buffer = $_[0]; if ($chunked) { my $len = length $buffer; return unless $len; $buffer = sprintf( "%x", $len ) . $CRLF . $buffer . $CRLF; } _syswrite($conn, \$buffer); }); _syswrite($conn, \"0$CRLF$CRLF") if $chunked; } else { return Plack::Util::inline_object write => sub { my $buffer = $_[0]; if ($chunked) { my $len = length $buffer; return unless $len; $buffer = sprintf( "%x", $len ) . $CRLF . $buffer . $CRLF; } _syswrite($conn, \$buffer); }, close => sub { _syswrite($conn, \"0$CRLF$CRLF") if $chunked; }; } } sub _syswrite { my ($conn, $buffer_ref) = @_; my $amount = length $$buffer_ref; my $offset = 0; while ($amount > 0) { my $len = syswrite($conn, $$buffer_ref, $amount, $offset); if (not defined $len) { return if $! == EPIPE; return if $! == ECONNRESET; redo if $! == EINTR; die "write error: $!"; } $amount -= $len; $offset += $len; DEBUG && warn "[$$] Wrote $len byte", ($len == 1 ? '' : 's'), "\n"; } } sub _sysread { while (1) { my $len = sysread $_[0], $_[1], $_[2]; return $len if defined $len or $! != EINTR; } } sub _write_informational { my ($conn, $code, $headers) = @_; my $message = HTTP::Status::status_message($code); my @lines = "HTTP/1.1 $code $message"; for (my $i = 0; $i < @$headers; $i += 2) { my $k = $headers->[$i]; my $v = $headers->[$i + 1]; push @lines, "$k: $v" ; } _syswrite($conn, \join($CRLF, @lines, $CRLF)); DEBUG && warn "[$$] Sent $code $message response\n"; } sub post_client_connection_hook { my $self = shift; if ($self->{client}->{harakiri}) { exit; } } 1; author-pod-syntax.t100644000765000024 45414500415430 17373 0ustar00miyagawastaff000000000000Starman-0.4017/t#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); chunked_termination.t100644000765000024 216414500415430 20037 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Test::More; { package Starman::Server; # Override the sysread method enabling it to read a stream of packages # from an arrayref instead of an file handle: use subs 'sysread'; *Starman::Server::sysread = sub { if (ref $_[0] eq "ARRAY") { die "EWOULDBLOCK\n" unless @{ $_[0] }; $_[1] = shift @{ $_[0] }; return length $_[1]; } return CORE::sysread($_[0], $_[1], $_[2]); }; } use Starman::Server; my $server = { server => { client => [ "3\015\012foo\015\012", # Full chunk "3\015\012bar", # Chunk missing terminating HTTP newline "\015\012", # ... and then the termination "0\015\012", # Empty chunk to mark end of stream ], } }; my $env = { HTTP_TRANSFER_ENCODING => 'chunked', }; my $blocked; eval { Starman::Server::_prepare_env( $server, $env ); 1; } or do { $blocked = 1 if $@ =~ /^EWOULDBLOCK$/; }; ok( !$blocked, "Reading chunked encoding does not block on well-placed package borders" ); done_testing; chunked_zero_length.t100644000765000024 114014500415430 20017 0ustar00miyagawastaff000000000000Starman-0.4017/tuse strict; use Plack::Test; use HTTP::Request; use Test::More; $Plack::Test::Impl = "Server"; $ENV{PLACK_SERVER} = 'Starman'; my $app = sub { my $env = shift; return sub { my $response = shift; my $writer = $response->([ 200, [ 'Content-Type', 'text/plain' ]]); $writer->write("Content"); $writer->write(""); $writer->write("Again"); $writer->close; } }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new(GET => "http://localhost/"); my $res = $cb->($req); is $res->content, "ContentAgain"; }; done_testing; Handler000755000765000024 014500415430 16367 5ustar00miyagawastaff000000000000Starman-0.4017/lib/PlackStarman.pm100644000765000024 175414500415430 20501 0ustar00miyagawastaff000000000000Starman-0.4017/lib/Plack/Handlerpackage Plack::Handler::Starman; use strict; use Starman::Server; sub new { my $class = shift; bless { @_ }, $class; } sub run { my($self, $app) = @_; if ($ENV{SERVER_STARTER_PORT}) { require Net::Server::SS::PreFork; @Starman::Server::ISA = qw(Net::Server::SS::PreFork); # Yikes. } my %nsa; while (my($key, $value) = each %$self) { $key =~ s/^net_server_// or next; $nsa{$key} = $value; } $self->{net_server_args} = \%nsa if %nsa; Starman::Server->new->run($app, {%$self}); } 1; __END__ =head1 NAME Plack::Handler::Starman - Plack adapter for Starman =head1 SYNOPSIS plackup -s Starman =head1 DESCRIPTION This handler exists for the C compatibility. Essentially, C is equivalent to C, because the C executable delay loads the application by default. See L for more details. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L =cut Server000755000765000024 014500415430 20543 5ustar00miyagawastaff000000000000Starman-0.4017/lib/HTTP/Server/PSGI/NetPreFork.pm100644000765000024 12614500415430 22570 0ustar00miyagawastaff000000000000Starman-0.4017/lib/HTTP/Server/PSGI/Net/Serverpackage HTTP::Server::PSGI::Net::Server::PreFork; use parent qw(Starman::Server); 1;