| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package MHFS::HTTP::Server::Client v0.7.0; |
|
2
|
1
|
|
|
1
|
|
48
|
use 5.014; |
|
|
1
|
|
|
|
|
5
|
|
|
3
|
1
|
|
|
1
|
|
7
|
use strict; use warnings; |
|
|
1
|
|
|
1
|
|
2
|
|
|
|
1
|
|
|
|
|
29
|
|
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
57
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use feature 'say'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
149
|
|
|
5
|
1
|
|
|
1
|
|
8
|
use Time::HiRes qw( usleep clock_gettime CLOCK_REALTIME CLOCK_MONOTONIC); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
9
|
|
|
6
|
1
|
|
|
1
|
|
136
|
use IO::Socket::INET; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
10
|
|
|
7
|
1
|
|
|
1
|
|
809
|
use Errno qw(EINTR EIO :POSIX); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
533
|
|
|
8
|
1
|
|
|
1
|
|
9
|
use Fcntl qw(:seek :mode); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
333
|
|
|
9
|
1
|
|
|
1
|
|
623
|
use File::stat; |
|
|
1
|
|
|
|
|
9060
|
|
|
|
1
|
|
|
|
|
90
|
|
|
10
|
1
|
|
|
1
|
|
35
|
use IO::Poll qw(POLLIN POLLOUT POLLHUP); |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
85
|
|
|
11
|
1
|
|
|
1
|
|
9
|
use Scalar::Util qw(looks_like_number weaken); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
61
|
|
|
12
|
1
|
|
|
1
|
|
6
|
use Data::Dumper; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
54
|
|
|
13
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
56
|
|
|
14
|
1
|
|
|
1
|
|
775
|
use MHFS::HTTP::Server::Client::Request; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
732
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub new { |
|
17
|
0
|
|
|
0
|
0
|
|
my ($class, $sock, $server, $serverhostinfo, $ip) = @_; |
|
18
|
0
|
|
|
|
|
|
$sock->blocking(0); |
|
19
|
0
|
|
|
|
|
|
my %self = ('sock' => $sock, 'server' => $server, 'time' => clock_gettime(CLOCK_MONOTONIC), 'inbuf' => '', 'serverhostname' => $serverhostinfo->{'hostname'}, 'absurl' => $serverhostinfo->{'absurl'}, 'ip' => $ip, 'X-MHFS-PROXY-KEY' => $serverhostinfo->{'X-MHFS-PROXY-KEY'}); |
|
20
|
0
|
|
|
|
|
|
$self{'CONN-ID'} = int($self{'time'} * rand()); # insecure uid |
|
21
|
0
|
|
|
|
|
|
$self{'outheaders'}{'X-MHFS-CONN-ID'} = sprintf("%X", $self{'CONN-ID'}); |
|
22
|
0
|
|
|
|
|
|
bless \%self, $class; |
|
23
|
0
|
|
|
|
|
|
$self{'request'} = MHFS::HTTP::Server::Client::Request->new(\%self); |
|
24
|
0
|
|
|
|
|
|
return \%self; |
|
25
|
|
|
|
|
|
|
} |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# add a connection timeout timer |
|
28
|
|
|
|
|
|
|
sub AddClientCloseTimer { |
|
29
|
0
|
|
|
0
|
0
|
|
my ($self, $timelength, $id, $is_requesttimeout) = @_; |
|
30
|
0
|
|
|
|
|
|
weaken($self); #don't allow this timer to keep the client object alive |
|
31
|
0
|
|
|
|
|
|
my $server = $self->{'server'}; |
|
32
|
0
|
|
|
|
|
|
say "CCT | add timer: $id"; |
|
33
|
|
|
|
|
|
|
$server->{'evp'}->add_timer($timelength, 0, sub { |
|
34
|
0
|
0
|
|
0
|
|
|
if(! defined $self) { |
|
35
|
0
|
|
|
|
|
|
say "CCT | $id self undef"; |
|
36
|
0
|
|
|
|
|
|
return undef; |
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
# Commented out as with connection reuse on, Apache 2.4.10 seems sometimes |
|
39
|
|
|
|
|
|
|
# pass 408 on to the next client. |
|
40
|
|
|
|
|
|
|
#if($is_requesttimeout) { |
|
41
|
|
|
|
|
|
|
# say "CCT | \$timelength ($timelength) exceeded, sending 408"; |
|
42
|
|
|
|
|
|
|
# $self->{request}->Send408; |
|
43
|
|
|
|
|
|
|
# CT_WRITE($self); |
|
44
|
|
|
|
|
|
|
#} |
|
45
|
0
|
|
|
|
|
|
say "CCT | \$timelength ($timelength) exceeded, closing CONN $id"; |
|
46
|
0
|
|
|
|
|
|
say "-------------------------------------------------"; |
|
47
|
0
|
|
|
|
|
|
$server->{'evp'}->remove($self->{'sock'}); |
|
48
|
0
|
|
|
|
|
|
say "poll has " . scalar ( $server->{'evp'}{'poll'}->handles) . " handles"; |
|
49
|
0
|
|
|
|
|
|
return undef; |
|
50
|
0
|
|
|
|
|
|
}, $id); |
|
51
|
0
|
|
|
|
|
|
return $id; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub KillClientCloseTimer { |
|
55
|
0
|
|
|
0
|
0
|
|
my ($self, $id) = @_; |
|
56
|
0
|
|
|
|
|
|
my $server = $self->{'server'}; |
|
57
|
0
|
|
|
|
|
|
say "CCT | removing timer: $id"; |
|
58
|
0
|
|
|
|
|
|
$server->{'evp'}->remove_timer_by_id($id); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub SetEvents { |
|
62
|
0
|
|
|
0
|
0
|
|
my ($self, $events) = @_; |
|
63
|
0
|
|
|
|
|
|
$self->{'server'}{'evp'}->set($self->{'sock'}, $self, $events); |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
use constant { |
|
67
|
1
|
|
|
|
|
3787
|
RECV_SIZE => 65536, |
|
68
|
|
|
|
|
|
|
CT_YIELD => 1, |
|
69
|
|
|
|
|
|
|
CT_DONE => undef, |
|
70
|
|
|
|
|
|
|
#CT_READ => 1, |
|
71
|
|
|
|
|
|
|
#CT_PROCESS = 2, |
|
72
|
|
|
|
|
|
|
#CT_WRITE => 3 |
|
73
|
1
|
|
|
1
|
|
12
|
}; |
|
|
1
|
|
|
|
|
3
|
|
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# The "client_thread" consists of 5 states, CT_READ, CT_PROCESS, CT_WRITE, CT_YIELD, and CT_DONE |
|
76
|
|
|
|
|
|
|
# CT_READ reads input data from the socket |
|
77
|
|
|
|
|
|
|
## on data read transitions to CT_PROCESS |
|
78
|
|
|
|
|
|
|
## on error transitions to CT_DONE |
|
79
|
|
|
|
|
|
|
## otherwise CT_YIELD |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# CT_PROCESS processes the input data |
|
82
|
|
|
|
|
|
|
## on processing done, switches to CT_WRITE or CT_READ to read more data to process |
|
83
|
|
|
|
|
|
|
## on error transitions to CT_DONE |
|
84
|
|
|
|
|
|
|
## otherwise CT_YIELD |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# CT_WRITE outputs data to the socket |
|
87
|
|
|
|
|
|
|
## on all data written transitions to CT_PROCESS unless Connection: close is set. |
|
88
|
|
|
|
|
|
|
## on error transitions to CT_DONE |
|
89
|
|
|
|
|
|
|
## otherwise CT_YIELD |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# CT_YIELD just returns control to the poll loop to wait for IO or allow another client thread to run |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# CT_DONE also returns control to the poll loop, it is called on error or when the client connection should be closed or is closed |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub CT_READ { |
|
96
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
|
97
|
0
|
|
|
|
|
|
my $tempdata; |
|
98
|
0
|
0
|
|
|
|
|
if(!defined($self->{'sock'}->recv($tempdata, RECV_SIZE))) { |
|
99
|
0
|
0
|
0
|
|
|
|
if(! ($!{EAGAIN} || $!{EWOULDBLOCK})) { |
|
100
|
0
|
|
|
|
|
|
print ("CT_READ RECV errno: $!\n"); |
|
101
|
0
|
|
|
|
|
|
return CT_DONE; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
0
|
|
|
|
|
|
say "CT_YIELD: $!"; |
|
104
|
0
|
|
|
|
|
|
return CT_YIELD; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
0
|
0
|
|
|
|
|
if(length($tempdata) == 0) { |
|
107
|
0
|
|
|
|
|
|
say 'Server::Client read 0 bytes, client read closed'; |
|
108
|
0
|
|
|
|
|
|
return CT_DONE; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
0
|
|
|
|
|
|
$self->{'inbuf'} .= $tempdata; |
|
111
|
0
|
|
|
|
|
|
goto &CT_PROCESS; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub CT_PROCESS { |
|
115
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
|
116
|
0
|
|
0
|
|
|
|
$self->{'request'} //= MHFS::HTTP::Server::Client::Request->new($self); |
|
117
|
0
|
0
|
|
|
|
|
if(!defined($self->{'request'}{'on_read_ready'})) { |
|
118
|
0
|
|
|
|
|
|
die("went into CT_PROCESS in bad state"); |
|
119
|
0
|
|
|
|
|
|
return CT_YIELD; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
0
|
|
|
|
|
|
my $res = $self->{'request'}{'on_read_ready'}->($self->{'request'}); |
|
122
|
0
|
0
|
|
|
|
|
if(!$res) { |
|
123
|
0
|
|
|
|
|
|
return $res; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
0
|
0
|
|
|
|
|
if(defined $self->{'request'}{'response'}) { |
|
|
|
0
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
goto &CT_WRITE; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
elsif(defined $self->{'request'}{'on_read_ready'}) { |
|
129
|
0
|
|
|
|
|
|
goto &CT_READ; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
0
|
|
|
|
|
|
return $res; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub CT_WRITE { |
|
135
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
|
136
|
0
|
0
|
|
|
|
|
if(!defined $self->{'request'}{'response'}) { |
|
137
|
0
|
|
|
|
|
|
die("went into CT_WRITE in bad state"); |
|
138
|
0
|
|
|
|
|
|
return CT_YIELD; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
# TODO only TrySendResponse if there is data in buf or to be read |
|
141
|
0
|
|
|
|
|
|
my $tsrRet = $self->TrySendResponse; |
|
142
|
0
|
0
|
|
|
|
|
if(!defined($tsrRet)) { |
|
|
|
0
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
say "-------------------------------------------------"; |
|
144
|
0
|
|
|
|
|
|
return CT_DONE; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
elsif($tsrRet ne '') { |
|
147
|
0
|
0
|
0
|
|
|
|
if($self->{'request'}{'outheaders'}{'Connection'} && ($self->{'request'}{'outheaders'}{'Connection'} eq 'close')) { |
|
148
|
0
|
|
|
|
|
|
say "Connection close header set closing conn"; |
|
149
|
0
|
|
|
|
|
|
say "-------------------------------------------------"; |
|
150
|
0
|
|
|
|
|
|
return CT_DONE; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
0
|
|
|
|
|
|
$self->{'request'} = undef; |
|
153
|
0
|
|
|
|
|
|
goto &CT_PROCESS; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
0
|
|
|
|
|
|
return CT_YIELD; |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub do_on_data { |
|
159
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
|
160
|
0
|
|
|
|
|
|
my $res = $self->{'request'}{'on_read_ready'}->($self->{'request'}); |
|
161
|
0
|
0
|
|
|
|
|
if($res) { |
|
162
|
0
|
0
|
|
|
|
|
if(defined $self->{'request'}{'response'}) { |
|
|
|
0
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
#say "do_on_data: goto onWriteReady"; |
|
164
|
0
|
|
|
|
|
|
goto &onWriteReady; |
|
165
|
|
|
|
|
|
|
#return onWriteReady($self); |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
#else { |
|
168
|
|
|
|
|
|
|
elsif(defined $self->{'request'}{'on_read_ready'}) { |
|
169
|
|
|
|
|
|
|
#say "do_on_data: goto onReadReady inbuf " . length($self->{'inbuf'}); |
|
170
|
0
|
|
|
|
|
|
goto &onReadReady; |
|
171
|
|
|
|
|
|
|
#return onReadReady($self); |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
else { |
|
174
|
0
|
|
|
|
|
|
say "do_on_data: response and on_read_ready not defined, response by timer or poll?"; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
} |
|
177
|
0
|
|
|
|
|
|
return $res; |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub onReadReady { |
|
182
|
0
|
|
|
0
|
0
|
|
goto &CT_READ; |
|
183
|
0
|
|
|
|
|
|
my ($self) = @_; |
|
184
|
0
|
|
|
|
|
|
my $tempdata; |
|
185
|
0
|
0
|
|
|
|
|
if(defined($self->{'sock'}->recv($tempdata, RECV_SIZE))) { |
|
186
|
0
|
0
|
|
|
|
|
if(length($tempdata) == 0) { |
|
187
|
0
|
|
|
|
|
|
say 'Server::Client read 0 bytes, client read closed'; |
|
188
|
0
|
|
|
|
|
|
return undef; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
0
|
|
|
|
|
|
$self->{'inbuf'} .= $tempdata; |
|
191
|
0
|
|
|
|
|
|
goto &do_on_data; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
0
|
0
|
|
|
|
|
if(! $!{EAGAIN}) { |
|
194
|
0
|
|
|
|
|
|
print ("MHFS::HTTP::Server::Client onReadReady RECV errno: $!\n"); |
|
195
|
0
|
|
|
|
|
|
return undef; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
0
|
|
|
|
|
|
return ''; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub onWriteReady { |
|
201
|
0
|
|
|
0
|
0
|
|
goto &CT_WRITE; |
|
202
|
0
|
|
|
|
|
|
my ($client) = @_; |
|
203
|
|
|
|
|
|
|
# send the response |
|
204
|
0
|
0
|
|
|
|
|
if(defined $client->{'request'}{'response'}) { |
|
205
|
|
|
|
|
|
|
# TODO only TrySendResponse if there is data in buf or to be read |
|
206
|
0
|
|
|
|
|
|
my $tsrRet = $client->TrySendResponse; |
|
207
|
0
|
0
|
|
|
|
|
if(!defined($tsrRet)) { |
|
|
|
0
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
say "-------------------------------------------------"; |
|
209
|
0
|
|
|
|
|
|
return undef; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
elsif($tsrRet ne '') { |
|
212
|
0
|
0
|
0
|
|
|
|
if($client->{'request'}{'outheaders'}{'Connection'} && ($client->{'request'}{'outheaders'}{'Connection'} eq 'close')) { |
|
213
|
0
|
|
|
|
|
|
say "Connection close header set closing conn"; |
|
214
|
0
|
|
|
|
|
|
say "-------------------------------------------------"; |
|
215
|
0
|
|
|
|
|
|
return undef; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
0
|
|
|
|
|
|
$client->{'request'} = MHFS::HTTP::Server::Client::Request->new($client); |
|
218
|
|
|
|
|
|
|
# handle possible existing read data |
|
219
|
0
|
|
|
|
|
|
goto &do_on_data; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
else { |
|
223
|
0
|
|
|
|
|
|
say "response not defined, probably set later by a timer or poll"; |
|
224
|
|
|
|
|
|
|
} |
|
225
|
0
|
|
|
|
|
|
return 1; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub _TSRReturnPrint { |
|
229
|
0
|
|
|
0
|
|
|
my ($sentthiscall) = @_; |
|
230
|
0
|
0
|
|
|
|
|
if($sentthiscall > 0) { |
|
231
|
0
|
|
|
|
|
|
say "wrote $sentthiscall bytes"; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub TrySendResponse { |
|
236
|
0
|
|
|
0
|
0
|
|
my ($client) = @_; |
|
237
|
0
|
|
|
|
|
|
my $csock = $client->{'sock'}; |
|
238
|
0
|
|
|
|
|
|
my $dataitem = $client->{'request'}{'response'}; |
|
239
|
0
|
0
|
|
|
|
|
defined($dataitem->{'buf'}) or die("dataitem must always have a buf"); |
|
240
|
0
|
|
|
|
|
|
my $sentthiscall = 0; |
|
241
|
|
|
|
|
|
|
do { |
|
242
|
|
|
|
|
|
|
# Try to send the buf if set |
|
243
|
0
|
0
|
|
|
|
|
if(length($dataitem->{'buf'})) { |
|
244
|
0
|
|
|
|
|
|
my $sret = TrySendItem($csock, \$dataitem->{'buf'}); |
|
245
|
|
|
|
|
|
|
# critical conn error |
|
246
|
0
|
0
|
|
|
|
|
if(! defined($sret)) { |
|
247
|
0
|
|
|
|
|
|
_TSRReturnPrint($sentthiscall); |
|
248
|
0
|
|
|
|
|
|
return undef; |
|
249
|
|
|
|
|
|
|
} |
|
250
|
0
|
0
|
|
|
|
|
if($sret) { |
|
251
|
0
|
|
|
|
|
|
$sentthiscall += $sret; |
|
252
|
|
|
|
|
|
|
# if we sent data, kill the send timer |
|
253
|
0
|
0
|
|
|
|
|
if(defined $client->{'sendresponsetimerid'}) { |
|
254
|
0
|
|
|
|
|
|
$client->KillClientCloseTimer($client->{'sendresponsetimerid'}); |
|
255
|
0
|
|
|
|
|
|
$client->{'sendresponsetimerid'} = undef; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
# not all data sent, add timer |
|
259
|
0
|
0
|
|
|
|
|
if(length($dataitem->{'buf'}) > 0) { |
|
260
|
0
|
|
0
|
|
|
|
$client->{'sendresponsetimerid'} //= $client->AddClientCloseTimer($client->{'server'}{'settings'}{'sendresponsetimeout'}, $client->{'CONN-ID'}); |
|
261
|
0
|
|
|
|
|
|
_TSRReturnPrint($sentthiscall); |
|
262
|
0
|
|
|
|
|
|
return ''; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
#we sent the full buf |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# read more data |
|
269
|
0
|
|
|
|
|
|
my $newdata; |
|
270
|
0
|
0
|
|
|
|
|
if(defined $dataitem->{'fh'}) { |
|
|
|
0
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
|
my $FH = $dataitem->{'fh'}; |
|
272
|
0
|
|
|
|
|
|
my $req_length = $dataitem->{'get_current_length'}->(); |
|
273
|
0
|
|
|
|
|
|
my $filepos = $dataitem->{'fh_pos'}; |
|
274
|
|
|
|
|
|
|
# TODO, remove this assert |
|
275
|
0
|
0
|
|
|
|
|
if($filepos != tell($FH)) { |
|
276
|
0
|
|
|
|
|
|
die('tell mismatch'); |
|
277
|
|
|
|
|
|
|
} |
|
278
|
0
|
0
|
0
|
|
|
|
if($req_length && ($filepos >= $req_length)) { |
|
279
|
0
|
0
|
|
|
|
|
if($filepos > $req_length) { |
|
280
|
0
|
|
|
|
|
|
say "Reading too much tell: $filepos req_length: $req_length"; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
0
|
|
|
|
|
|
say "file read done"; |
|
283
|
0
|
|
|
|
|
|
close($FH); |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
else { |
|
286
|
0
|
|
|
|
|
|
my $readamt = 24000; |
|
287
|
0
|
0
|
|
|
|
|
if($req_length) { |
|
288
|
0
|
|
|
|
|
|
my $tmpsend = $req_length - $filepos; |
|
289
|
0
|
0
|
|
|
|
|
$readamt = $tmpsend if($tmpsend < $readamt); |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
# this is blocking, it shouldn't block for long but it could if it's a pipe especially |
|
292
|
0
|
|
|
|
|
|
my $bytesRead = read($FH, $newdata, $readamt); |
|
293
|
0
|
0
|
|
|
|
|
if(! defined($bytesRead)) { |
|
|
|
0
|
|
|
|
|
|
|
294
|
0
|
|
|
|
|
|
$newdata = undef; |
|
295
|
0
|
|
|
|
|
|
say "READ ERROR: $!"; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
elsif($bytesRead == 0) { |
|
298
|
|
|
|
|
|
|
# read EOF, better remove the error |
|
299
|
0
|
0
|
|
|
|
|
if(! $req_length) { |
|
300
|
0
|
|
|
|
|
|
say '$req_length not set and read 0 bytes, treating as EOF'; |
|
301
|
0
|
|
|
|
|
|
$newdata = undef; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
else { |
|
304
|
0
|
|
|
|
|
|
say 'FH EOF ' .$filepos; |
|
305
|
0
|
|
|
|
|
|
seek($FH, 0, 1); |
|
306
|
0
|
|
|
|
|
|
_TSRReturnPrint($sentthiscall); |
|
307
|
0
|
|
|
|
|
|
return ''; |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
else { |
|
311
|
0
|
|
|
|
|
|
$dataitem->{'fh_pos'} += $bytesRead; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
elsif(defined $dataitem->{'cb'}) { |
|
316
|
0
|
|
|
|
|
|
$newdata = $dataitem->{'cb'}->($dataitem); |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
my $encode_chunked = $dataitem->{'is_chunked'}; |
|
320
|
|
|
|
|
|
|
# if we got to here and there's no data, fetching newdata is done |
|
321
|
0
|
0
|
|
|
|
|
if(! $newdata) { |
|
322
|
0
|
|
|
|
|
|
$dataitem->{'fh'} = undef; |
|
323
|
0
|
|
|
|
|
|
$dataitem->{'cb'} = undef; |
|
324
|
0
|
|
|
|
|
|
$dataitem->{'is_chunked'} = undef; |
|
325
|
0
|
|
|
|
|
|
$newdata = ''; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# encode chunked encoding if needed |
|
329
|
0
|
0
|
|
|
|
|
if($encode_chunked) { |
|
330
|
0
|
|
|
|
|
|
my $sizeline = sprintf "%X\r\n", length($newdata); |
|
331
|
0
|
|
|
|
|
|
$newdata = $sizeline.$newdata."\r\n"; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# add the new data to the dataitem buffer |
|
335
|
0
|
|
|
|
|
|
$dataitem->{'buf'} .= $newdata; |
|
336
|
|
|
|
|
|
|
|
|
337
|
0
|
|
|
|
|
|
} while(length($dataitem->{'buf'})); |
|
338
|
0
|
|
|
|
|
|
$client->{'request'}{'response'} = undef; |
|
339
|
|
|
|
|
|
|
|
|
340
|
0
|
|
|
|
|
|
_TSRReturnPrint($sentthiscall); |
|
341
|
0
|
|
|
|
|
|
say "DONE Sending Data"; |
|
342
|
0
|
|
|
|
|
|
return 'RequestDone'; # not undef because keep-alive |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub TrySendItem { |
|
346
|
0
|
|
|
0
|
0
|
|
my ($csock, $dataref) = @_; |
|
347
|
0
|
|
|
|
|
|
my $sret = send($csock, $$dataref, 0); |
|
348
|
0
|
0
|
|
|
|
|
if(! defined($sret)) { |
|
|
|
0
|
|
|
|
|
|
|
349
|
0
|
0
|
|
|
|
|
if($!{EAGAIN}) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
#say "SEND EAGAIN\n"; |
|
351
|
0
|
|
|
|
|
|
return 0; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
elsif($!{ECONNRESET}) { |
|
354
|
0
|
|
|
|
|
|
print "ECONNRESET\n"; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
elsif($!{EPIPE}) { |
|
357
|
0
|
|
|
|
|
|
print "EPIPE\n"; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
else { |
|
360
|
0
|
|
|
|
|
|
print "send errno $!\n"; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
0
|
|
|
|
|
|
return undef; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
elsif($sret) { |
|
365
|
0
|
|
|
|
|
|
substr($$dataref, 0, $sret, ''); |
|
366
|
|
|
|
|
|
|
} |
|
367
|
0
|
|
|
|
|
|
return $sret; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub onHangUp { |
|
371
|
0
|
|
|
0
|
0
|
|
my ($client) = @_; |
|
372
|
0
|
|
|
|
|
|
return undef; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub DESTROY { |
|
376
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
377
|
0
|
|
|
|
|
|
say "$$ MHFS::HTTP::Server::Client destructor: "; |
|
378
|
0
|
|
|
|
|
|
say "$$ ".'X-MHFS-CONN-ID: ' . $self->{'outheaders'}{'X-MHFS-CONN-ID'}; |
|
379
|
0
|
0
|
|
|
|
|
if($self->{'sock'}) { |
|
380
|
|
|
|
|
|
|
#shutdown($self->{'sock'}, 2); |
|
381
|
0
|
|
|
|
|
|
close($self->{'sock'}); |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
1; |