File Coverage

blib/lib/SCGI/Request.pm
Criterion Covered Total %
statement 71 82 86.5
branch 26 48 54.1
condition 10 25 40.0
subroutine 14 15 93.3
pod 6 6 100.0
total 127 176 72.1


line stmt bran cond sub pod time code
1             package SCGI::Request;
2              
3 5     5   22 use strict;
  5         15  
  5         146  
4 5     5   20 use warnings;
  5         5  
  5         114  
5              
6 5     5   20 use SCGI;
  5         7  
  5         132  
7              
8 5     5   4264 use POSIX ':errno_h';
  5         34694  
  5         27  
9              
10             our $VERSION = $SCGI::VERSION;
11              
12             =head1 NAME
13              
14             SCGI::Request
15              
16             =head1 DESCRIPTION
17              
18             This module implements the part of the SCGI protocol that reads the environment. All that remains after this is the content of the request. The protocol and this module guarentee that there will be a CONTENT_LENGTH for the body of the request in the environment.
19              
20             =head1 SYNOPISIS
21              
22             # $request got from SCGI
23             $request->read_env;
24            
25             read $request->connection, my $body, $request->env->{CONTENT_LENGTH};
26              
27             =head2 public methods
28              
29             =over
30              
31             =item read_env
32              
33             Read the environment in a blocking or non-blocking manner, per parameter to Cnew>. Returns true if it has finished.
34              
35             =cut
36              
37             sub read_env {
38 16992     16992 1 71674 my ($this) = @_;
39 16992 100       28930 goto &_blocking_read_env if $this->blocking;
40 16990 50       32795 die 'read_env called when env already read - use env method to access' if $this->{env_read};
41 16990   100     31319 $this->{env_length_buffer} ||= '';
42 16990   50     54036 $this->{env_buffer} ||= '';
43 16990 50       32458 unless ($this->{env_length_read}) {
44 16990         28630 my $bytes_read = sysread $this->connection, my $buffer, 14;
45 16990 50 66     115229 die "read error: $!" unless defined $bytes_read || $! == EAGAIN;
46 16990 100       45470 return unless $bytes_read;
47 18 100 100     281 if ($buffer =~ m{ ^ (\d+) : (.*) $ }osx) {
    100          
    50          
48 9         75 $this->{env_length_buffer} .= $1;
49 9         574 $this->{env_buffer} .= $2;
50 9         36 $this->{env_length_read} = 1;
51             }
52             elsif ($this->{env_length_buffer} ne '' && $buffer =~ m{ ^ : (.*) $ }osx) {
53 3         20 $this->{env_buffer} .= $1;
54 3         10 $this->{env_length_read} = 1;
55             }
56             elsif ($buffer =~ m{ ^ \d+ $ }osx) {
57 6         16 $this->{env_length_buffer} .= $buffer;
58 6         18 return;
59             }
60             else {
61 0         0 die "malformed env length";
62             }
63             }
64 12         58 my $left_to_read = $this->{env_length_buffer} - length($this->{env_buffer});
65 12         26 my $buffer = '';
66 12         64 my $read = sysread $this->connection, $buffer, $left_to_read + 1;
67 12 50 33     51 die "read error: $!" unless defined $read || $! == EAGAIN;
68 12 50       37 return unless $read;
69 12 50       34 if ($read == $left_to_read + 1) {
70 12 50       61 if ((my $comma = substr $buffer, $left_to_read) ne ',') {
71 0         0 die "malformed netstring, expected terminating comma, found \"$comma\"";
72             }
73 12         93 $this->_decode_env($this->{env_buffer} . substr $buffer, 0, $left_to_read);
74 12         55 return 1;
75             }
76             else {
77 0         0 $this->{env_buffer} .= $buffer;
78 0         0 return;
79             }
80             }
81              
82             =item env
83              
84             Gets the environment for this request after it has been read. This will return undef until C or C has been called and returned true.
85              
86             =cut
87              
88             sub env {
89 28     28 1 10618 my ($this) = @_;
90 28         435 $this->{env};
91             }
92              
93             =item connection
94              
95             Returns the open connection to the client.
96              
97             =cut
98              
99             sub connection {
100 17062     17062 1 26383 my ($this) = @_;
101 17062         84787 $this->{connection};
102             }
103              
104             =item close
105              
106             Closes the connection.
107              
108             =cut
109              
110             sub close {
111 14     14 1 27 my ($this) = @_;
112 14 50       48 $this->connection->close if $this->connection;
113 14         17703 $this->{closed} = 1;
114             }
115              
116             =item blocking
117              
118             Returns true if the connection is blocking.
119              
120             =cut
121              
122             sub blocking {
123 16992     16992 1 16356 my ($this) = @_;
124 16992         42669 $this->{blocking};
125             }
126              
127             =item set_blocking
128              
129             If boolean argument is true turns on blocking, otherwise turns it off.
130              
131             =cut
132              
133             sub set_blocking {
134 0     0 1 0 my ($this, $blocking) = @_;
135 0 0 0     0 return if $this->{blocking} && $blocking || ! $this->{blocking} && ! $blocking;
      0        
      0        
136 0 0       0 if ($blocking) {
137 0         0 $this->connection->blocking(1);
138             }
139             else {
140 0         0 $this->connection->flush;
141 0         0 $this->connection->blocking(0);
142             }
143             }
144              
145             =back
146              
147             =head2 private methods
148              
149             =over
150              
151             =item _new
152              
153             Creates a new SCGI::Request. This is used by SCGI in the C method, so if you are considering using this, use that instead.
154              
155             =cut
156              
157             sub _new {
158 14     14   143 my ($class, $connection, $blocking) = @_;
159 14         178 bless {connection => $connection, blocking => $blocking}, $class;
160             }
161              
162             =item _decode_env
163              
164             Takes the encoded environment as a string and sets the env ready for access with C.
165              
166             =cut
167              
168             sub _decode_env {
169 14     14   68 my ($this, $env_string) = @_;
170 14         22 my %env;
171 14         72 pos $env_string = 0;
172 14 50       161 $env_string =~ m{
173             \G CONTENT_LENGTH \0 (\d+) \0
174             }msogcx or die "malformed CONTENT_LENGTH header";
175 14         88 $env{CONTENT_LENGTH} = $1;
176 14         106 while ($env_string =~ m{ ([^\0]+) \0 ([^\0]+) \0 }msogcx) {
177 31 50       96 warn "repeated $1 header in env" if $env{$1};
178 31         314 $env{$1} = $2;
179             }
180 14 50       79 die "malformed header" unless pos $env_string = length $env_string;
181 14 50 33     123 die "missing SCGI header" unless $env{SCGI} && $env{SCGI} eq '1';
182 14         106 $this->_set_env(\%env);
183             }
184              
185             =item _set_env
186              
187             Sets the environment for this request.
188              
189             =cut
190              
191             sub _set_env {
192 14     14   40 my ($this, $env) = @_;
193 14         110 $this->{env} = $env;
194             }
195              
196             =item _blocking_read_env
197              
198             Reads and decodes the environment in one go. Returns true on success, raises an exception on failiure.
199              
200             =cut
201              
202             sub _blocking_read_env {
203 2     2   4 my ($this) = @_;
204 2 50       6 read $this->connection, my $env_length, 14 or die "cannot read env length from connection: $!";
205 2 50       64 my ($length, $rest) = $env_length =~ m{ ^ (\d+) : (.*) $ }osx
206             or die 'malformed env length';
207 2 50       6 read $this->connection, my $env, $length + 1 - length($rest) or die "cannot read env from connection: $!";
208 2 50       15 if ((my $comma = substr $env, $length - length $rest) ne ',') {
209 0         0 die "malformed netstring, expected terminating comma, found \"$comma\"";
210             }
211 2         17 $this->_decode_env($rest . substr $env, 0, $length);
212 2         5 1;
213             }
214              
215             sub DESTROY {
216 14     14   39 my ($this) = @_;
217 14 50       285 $this->close unless $this->{closed};
218             }
219              
220             1;
221              
222             __END__