File Coverage

blib/lib/Test/HTTP/MockServer/Once.pm
Criterion Covered Total %
statement 56 71 78.8
branch 11 22 50.0
condition 3 5 60.0
subroutine 12 15 80.0
pod 6 6 100.0
total 88 119 73.9


line stmt bran cond sub pod time code
1             package Test::HTTP::MockServer::Once;
2 2     2   1437 use strict;
  2         4  
  2         54  
3 2     2   9 use warnings;
  2         3  
  2         51  
4 2     2   821 use HTTP::Parser;
  2         3628  
  2         62  
5 2     2   14 use HTTP::Response;
  2         4  
  2         30  
6 2     2   9 use IO::Handle;
  2         3  
  2         85  
7 2     2   1141 use Socket;
  2         6458  
  2         876  
8 2     2   495 use Storable qw(freeze);
  2         2615  
  2         1516  
9              
10             our $VERSION = '0.0.2';
11              
12             sub new {
13 3     3 1 831 my ($class, %params) = @_;
14 3   33     27 $class = ref $class || $class;
15             return bless {
16             port => $params{port} || undef
17 3   100     52 }, $class;
18             }
19              
20             sub bind_mock_server {
21 10     10 1 15 my $self = shift;
22 10 100       33 if (!$self->{socket}) {
23 3 50       954 my $proto = getprotobyname("tcp")
24             or die $!;
25 3 50       144 socket my $s, PF_INET, SOCK_STREAM, $proto
26             or die $!;
27 3 50       37 setsockopt($s,SOL_SOCKET,SO_REUSEADDR,1) or die "setsockopt: $!";
28 3         8 my $host_s = '127.0.0.1';
29 3         19 my $host = inet_aton($host_s);
30 3 100       14 if(defined($self->{port}))
31             {
32 1         8 my $addr = sockaddr_in($self->{port}, $host);
33 1 50       30 bind($s,$addr) or die $!;
34 1 50       12 listen($s, 10)
35             or die $!;
36             }
37             else
38             {
39 2         3 my $random_port;
40 2         4 while (1) {
41 2         51 $random_port = int(rand(5000))+10000;
42 2         9 my $addr = sockaddr_in($random_port, $host);
43 2 50       51 bind($s,$addr)
44             or next;
45 2 50       22 listen($s, 10)
46             or die $!;
47 2         5 last;
48             }
49 2         6 $self->{port} = $random_port;
50             }
51 3         10 $self->{host} = $host_s;
52 3         13 $self->{socket} = $s;
53             }
54 10         17 return 1;
55             }
56              
57             sub host {
58 4     4 1 16 my $self = shift;
59 4         21 $self->bind_mock_server;
60 4         13 return $self->{host};
61             }
62              
63             sub port {
64 6     6 1 2940 my $self = shift;
65 6         17 $self->bind_mock_server;
66 6         23 return $self->{port};
67             }
68              
69             sub base_url {
70 4     4 1 106692 my $self = shift;
71 4         15 my $host = $self->host;
72 4         16 my $port = $self->port;
73 4         43 return "http://$host:$port";
74             }
75              
76             my $request_handle = sub {
77             my $self = shift;
78             my $rp = shift;
79             my $request = shift;
80             my $response;
81             eval {
82             $response = HTTP::Response->new(200, 'OK');
83             $response->header('Content-type' => 'text/plain');
84             $rp->($request, $response);
85             };
86             if ($@) {
87             my $err = $@;
88             return HTTP::Response->new(
89             500, 'Internal Server Error',
90             HTTP::Headers->new("Content-type" => "text/plain"),
91             $err
92             );
93             } else {
94             return $response;
95             }
96             };
97              
98             my $client_handle = sub {
99             my $self = shift;
100             my $rp = shift;
101             my $client = shift;
102             my $parser = HTTP::Parser->new(request => 1);
103              
104             my $ret;
105             while (1) {
106             my $buf;
107             # read a byte at a time so we can do a blocking read instead of
108             # having to manage a non-blocking read loop.
109             my $r = read $client, $buf, 1;
110             my $request;
111             my $closed;
112             if (defined $r) {
113             my $p = $parser->add($buf);
114             if ($p == 0) {
115             $request = $parser->object;
116             }
117             $closed = 0;
118             } else {
119             $request = $parser->object;
120             $closed = 1;
121             }
122             if ($request) {
123             my $copy = $request;
124             $ret->{request} = $request;
125             $request = undef;
126             my $response = $request_handle->($self, $rp, $copy);
127             $response->header('Content-length' => length($response->content));
128             $ret->{response} = $response;
129             my $strout = "HTTP/1.1 ".($response->as_string("\015\012"));
130             $client->print($strout);
131             # we don't support keep-alive
132             $closed = 1;
133             }
134             last if $closed;
135             }
136             return $ret;
137             };
138              
139             sub start_mock_server {
140 0     0 1   my $self = shift;
141 0 0         my $rp = shift or die "No request processor";
142              
143 0           $self->bind_mock_server();
144              
145 0           $DB::signal = 1;
146 0     0     $SIG{INT} = sub { exit 1; };
  0            
147 0     0     $SIG{TERM} = sub { exit 1; };
  0            
148             accept my $client, $self->{socket}
149 0 0         or die "Failed to accept new connections: $!";
150 0           my $interaction;
151 0           eval {
152 0           $interaction = $client_handle->($self, $rp, $client);
153             };
154 0           close $client;
155 0           close $self->{socket};
156 0           return freeze $interaction;
157             }
158              
159             1;
160              
161             __END__