2 # This class is the internal subclass that does various Async connects and
3 # retreivals of info. Typical uses (and specific support) include http get and
6 # This merely starts up a Msg handler (and no DXChannel) ($conn in other words)
7 # does the GET, parses out the result and the data and then (assuming a positive
8 # result and that the originating callsign is still online) punts out the data
11 # It isn't designed to be very clever.
13 # Copyright (c) 2013 - Dirk Koopman G1TLH
23 use vars qw(@ISA $deftimeout);
36 my $conn = $pkg->SUPER::new($handler);
37 $conn->{caller} = ref $call ? $call->call : $call;
40 $outstanding{$conn} = $conn;
47 my ($conn, $ua, $tx) = @_;
49 # no point in going on if there is no-one wanting the output anymore
50 my $dxchan = DXChannel::get($conn->{caller});
56 my @lines = split qr{\r?\n}, $tx->res->body;
58 foreach my $msg(@lines) {
59 dbg("AsyncMsg: $conn->{_asstate} $msg") if isdbg('async');
61 if (my $filter = $conn->{_asfilter}) {
63 # this will crash if the command has been redefined and the filter is a
64 # function defined there whilst the request is in flight,
65 # but this isn't exactly likely in a production environment.
66 $filter->($conn, $msg, $dxchan);
68 my $prefix = $conn->{prefix} || '';
69 $dxchan->send("$prefix$msg");
76 # This does a http get on a path on a host and
77 # returns the result (through an optional filter)
79 # expects to be called something like from a cmd.pl file:
81 # AsyncMsg->get($self, <host>, <port>, <path>, [<key=>value>...]
83 # Standard key => value pairs are:
85 # filter => CODE ref (e.g. sub { ... })
86 # prefix => <string> prefix output with this string
88 # Anything else is taken and sent as (extra) http header stuff e.g:
90 # 'User-Agent' => qq{DXSpider;$main::version;$main::build;$^O}
91 # 'Content-Type' => q{text/xml; charset=utf-8}
92 # 'Content-Length' => $lth
94 # Host: is always set to the name of the host (unless overridden)
95 # User-Agent: is set to default above (unless overridden)
107 my $conn = $pkg->new($call);
108 $conn->{_asargs} = [@_];
109 $conn->{_asstate} = 'waitreply';
110 $conn->{_asfilter} = delete $args{filter} if exists $args{filter};
111 $conn->{prefix} = delete $args{prefix} if exists $args{prefix};
112 $conn->{prefix} ||= '';
113 $conn->{on_disconnect} = delete $args{on_disc} || delete $args{on_disconnect};
114 $conn->{path} = $path;
115 $conn->{host} = $conn->{peerhost} = $host;
116 $conn->{port} = $conn->{peerport} = delete $args{port} || 80;
117 $conn->{sort} = 'outgoing';
118 $conn->{_assort} = $sort;
119 $conn->{csort} = 'http';
121 my $data = delete $args{data};
123 my $ua = Mojo::UserAgent->new;
126 $s .= ":$port" unless $conn->{port} == 80;
128 dbg("AsyncMsg: $sort $s") if isdbg('async');
130 my $tx = $ua->build_tx($sort => $s);
131 $ua->on(error => sub { $conn->_error(@_); });
132 # $tx->on(error => sub { $conn->_error(@_); });
133 # $tx->on(finish => sub { $conn->disconnect; });
135 $ua->on(start => sub {
137 while (my ($k, $v) = each %args) {
138 dbg("AsyncMsg: attaching header $k: $v") if isdbg('async');
139 $tx->req->headers->header($k => $v);
142 dbg("AsyncMsg: body ='$data'") if isdbg('async');
143 $tx->req->body($data);
148 $ua->start($tx => sub { $conn->handle_getpost(@_) });
162 my $dxchan = DXChannel::get($conn->{caller});
163 $dxchan->send($msg) if $dxchan;
168 my ($conn, $e, $err);
169 dbg("Async: $conn->host:$conn->port path $conn->{path} error $err") if isdbg('chan');
170 $conn->_dxchan_send("$conn->{prefix}$msg");
177 _getpost($pkg, "GET", @_);
183 _getpost($pkg, "POST", @_);
186 # do a raw connection
188 # Async->raw($self, <host>, <port>, [handler => CODE ref], [prefix => <string>]);
190 # With no handler defined, everything sent by the connection will be sent to
193 # One can send stuff out on the connection by doing a standard "$conn->send_later(...)"
194 # inside the (custom) handler.
205 my $handler = delete $args{handler} || \&handle_raw;
207 my $conn = $pkg->new($call, $handler);
208 $conn->{prefix} = delete $args{prefix} if exists $args{prefix};
209 $conn->{prefix} ||= '';
210 $conn->{on_disconnect} = delete $args{on_disc} || delete $args{on_disconnect};
211 $r = $conn->connect($host, $port, on_connect => &_on_raw_connect);
212 return $r ? $conn : undef;
218 # Just outputs everything
225 # no point in going on if there is no-one wanting the output anymore
226 my $dxchan = DXChannel::get($conn->{caller});
233 $dxchan->send("$conn->{prefix}$msg");
241 dbg("AsyncMsg: Connected $conn->{cnum} to $conn->{host}:$conn->{port}") if isdbg('async');
248 dbg("AsyncMsg: ***Connect $conn->{cnum} Failed to $conn->{host}:$conn->{port} $!") if isdbg('async');
258 my $r = $conn->SUPER::connect($host, $port, @_);
267 if (my $ondisc = $conn->{on_disconnect}) {
268 my $dxchan = DXChannel::get($conn->{caller});
271 $ondisc->($conn, $dxchan);
274 delete $conn->{mojo};
275 delete $outstanding{$conn};
276 $conn->SUPER::disconnect;
282 delete $outstanding{$conn};
283 $conn->SUPER::DESTROY;