/
SCGI.pm
111 lines (98 loc) · 3.35 KB
/
SCGI.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
class SCGI::Request {
has $.connection;
has $!env_read is rw;
has $!env_buffer is rw = '';
has $!env_length_buffer is rw = '';
has $!env_length_read is rw;
has %.env is rw;
has $!closed is rw = 0;
#has Bool $.blocking is rw = False;
method read_env {
unless $!env_length_read {
say "read_env entered";
my $buffer = $.connection.recv(14);
say "initial receiving done";
my $bytes_read = ~$buffer.chars;
die "read error $!" unless defined $bytes_read;
return unless $bytes_read;
if $buffer ~~ / ^ (\d+) \: (.*) $ / {
$!env_length_buffer ~= +$0;
$!env_buffer ~= ~$1;
$!env_length_read = 1;
}
elsif $!env_length_buffer ne '' && $buffer ~~ / ^ \: (.*) $ / {
$!env_buffer ~= ~$0;
$!env_length_read = 1;
}
elsif $buffer ~~ / ^ \d+ $ / {
$!env_length_buffer = +$buffer;
return;
}
else {
die "malformed env length";
}
}
my $left_to_read = $!env_length_buffer - $!env_buffer.chars;
my $buffer = $.connection.recv($left_to_read + 1);
my $read = ~$buffer.chars;
die "read error: $!" unless defined $read;
return unless $read;
if $read == $left_to_read + 1 {
if (my $comma = $buffer.substr($left_to_read) ne ',') {
die "malformed netstring, expecting terminating comma, found \"$comma\"";
}
self!decode_env($!env_buffer ~ $buffer.substr(0, $left_to_read));
return 1;
}
else {
$!env_buffer ~= $buffer;
return;
}
}
method close () {
$.connection.close if $.connection;
$!closed = 1;
}
method !decode_env ($env_string) {
my %env = $env_string.split("\0");
die "malformed or missing CONTENT_LENGTH header" unless %env<CONTENT_LENGTH> && %env<CONTENT_LENGTH> ~~ / ^ \d+ $ /;
die "missing SCGI header" unless %env<SCGI> && %env<SCGI> eq '1';
%.env = %env;
}
method read_body {
my $body;
if %.env<CONTENT_LENGTH> {
$body = $.connection.recv(%.env<CONTENT_LENGTH>);
}
return $body;
}
submethod DESTROY {
self.close unless $.closed;
}
}
class SCGI {
#has Bool $.blocking = False;
has Int $!port = 8080;
has Str $!addr = 'localhost';
has IO::Socket $.socket = IO::Socket::INET.socket(2, 1, 6)\
.bind($!addr, $!port)\
.listen();
has $!bodykey = 'Request.Body';
has $!requestkey = 'Request.Object';
has $!scgikey = 'Request.SCGI';
method accept () {
my $connection = self.socket.accept() or return;
#$connecton.blocking(0) unless $.blocking;
SCGI::Request.new( :connection($connection) );
}
method handle (&closure) {
while (my $request = self.accept) {
$request.read_env;
my %env = $request.env;
%env($!requestkey) = $request;
%env($!scgikey) = self;
%env($!bodykey) = $request.read_body;
$request.connection.send($.closure(%env));
}
}
}