1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6use Test::More;
7
8use IO::Async::Test;
9use IO::Async::Loop;
10
11use Net::Async::HTTP;
12
13my $CRLF = "\x0d\x0a"; # because \r\n isn't portable
14
15my $loop = IO::Async::Loop->new();
16testing_loop( $loop );
17
18my $http = Net::Async::HTTP->new(
19   user_agent => "", # Don't put one in request headers
20);
21
22$loop->add( $http );
23
24my $peersock;
25no warnings 'redefine';
26local *IO::Async::Handle::connect = sub {
27   my $self = shift;
28   my %args = @_;
29   $args{host} eq "localhost" or die "Cannot fake connect - expected host 'localhost'";
30   $args{service} eq "5000"   or die "Cannot fake connect - expected service '5000'";
31
32   ( my $selfsock, $peersock ) = IO::Async::OS->socketpair() or die "Cannot create socket pair - $!";
33   $self->set_handle( $selfsock );
34
35   return Future->done( $self );
36};
37
38# Without on_error
39{
40   my $f1 = $http->GET( "http://localhost:5000/1" )
41      ->on_done( sub { die "Oopsie" } );
42
43   my $f2 = $http->GET( "http://localhost:5000/2" );
44
45   wait_for { defined $peersock };
46
47   my $request_stream = "";
48
49   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
50   pass( "First request is made" );
51
52   $request_stream =~ s/^.*$CRLF$CRLF//s;
53
54   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
55                        "Content-Length: 0$CRLF" .
56                        $CRLF );
57
58   my $e = eval { $loop->loop_once(0) for 1 .. 5; 1 } ? undef : $@;
59   like( $e, qr/^Oopsie at \Q$0\E line \d+/,
60      'Oopsie exception caught at loop toplevel' );
61
62   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
63   pass( "Second request is made after first one dies at ->done" );
64
65   $request_stream =~ s/^.*$CRLF$CRLF//s;
66
67   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
68                        "Content-Length: 0$CRLF" .
69                        $CRLF );
70
71   wait_for_future( $f2 );
72   ok( !$f2->failure, '$f2 completes successfully' );
73}
74
75# With on_error
76{
77   my $error;
78   $http->configure(
79      on_error => sub { ( undef, $error ) = @_; },
80   );
81
82   my $f1 = $http->GET( "http://localhost:5000/1" )
83      ->on_done( sub { die "Oopsie" } );
84
85   my $f2 = $http->GET( "http://localhost:5000/2" );
86
87   wait_for { defined $peersock };
88
89   my $request_stream = "";
90
91   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
92   pass( "First request is made" );
93
94   $request_stream =~ s/^.*$CRLF$CRLF//s;
95
96   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
97                        "Content-Length: 0$CRLF" .
98                        $CRLF );
99
100   my $e = eval { $loop->loop_once(0) for 1 .. 5; 1 } ? undef : $@;
101   ok( !defined $e, 'Loop toplevel does not catch exception' ) or
102      diag( "Caught exception was: $e" );
103
104   like( $error, qr/^Oopsie at \Q$0\E line \d+/,
105      'Oopsie exception caught by on_error handler' );
106
107   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
108   pass( "Second request is made after first one dies at ->done" );
109
110   $request_stream =~ s/^.*$CRLF$CRLF//s;
111
112   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
113                        "Content-Length: 0$CRLF" .
114                        $CRLF );
115
116   wait_for_future( $f2 );
117   ok( !$f2->failure, '$f2 completes successfully' );
118}
119
120done_testing;
121