File: LWP.pm

package info (click to toggle)
libcoro-perl 6.511-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,316 kB
  • ctags: 647
  • sloc: ansic: 2,454; perl: 2,139; makefile: 3
file content (158 lines) | stat: -rw-r--r-- 4,017 bytes parent folder | download
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
=head1 NAME

Coro::LWP - make LWP non-blocking - as much as possible

=head1 SYNOPSIS

 use Coro::LWP; # afterwards LWP should not block

=head1 ALTERNATIVES

Over the years, a number of less-invasive alternatives have popped up,
which you might find more acceptable than this rather invasive and fragile
module. All of them only support HTTP (and sometimes HTTPS).

=over 4

=item L<AnyEvent::HTTP>

Works fine without Coro. Requires using a very different API than
LWP. Probably the best choice I<iff> you can do with a completely
different event-based API.

=item L<LWP::Protocol::AnyEvent::http>

Makes LWP use L<AnyEvent::HTTP>. Does not make LWP event-based, but allows
Coro threads to schedule unimpeded through its AnyEvent integration.

Lets you use the LWP API normally.

=item L<LWP::Protocol::Coro::http>

Basically the same as above, distinction unclear. :)

=item L<AnyEvent::HTTP::LWP::UserAgent>

A different user agent implementation, not completely transparent to
users, requires Coro.

=back

=head1 DESCRIPTION

This module is an L<AnyEvent> user, you need to make sure that you use and
run a supported event loop.

This module tries to make L<LWP|LWP> non-blocking with respect to other
coroutines as much as possible, and with whatever means it takes.

LWP really tries very hard to be blocking (and relies on a lot of
undocumented functionality in IO::Socket), so this module had to be very
invasive and must be loaded very early to take the proper effect.

Note that the module L<AnyEvent::HTTP> might offer an alternative to the
full L<LWP> that is designed to be non-blocking.

Here is what it currently does (future versions of LWP might require
different tricks):

=over 4

=item It loads Coro::Select, overwriting the perl C<select> builtin I<globally>.

This is necessary because LWP calls select quite often for timeouts and
who-knows-what.

Impact: everybody else uses this (slower) version of select, too. It should be quite
compatible to perls builtin select, though.

=item It overwrites Socket::inet_aton with Coro::Util::inet_aton.

This is necessary because LWP might (and does) try to resolve hostnames
this way.

Impact: some code might not expect coroutine semantics, for example, when
you fork you might prefer the blocking variant because other coroutines
shouldn't actually run.

=item It replaces the base class of Net::HTTP, Net::FTP, Net::NNTP.

This is necessary because LWP does not always use select to see whether
a filehandle can be read/written without blocking, so the base class
C<IO::Socket::INET> needs to be replaced by C<Coro::Socket>.

Impact: Coro::Socket is not at all compatible to IO::Socket::INET. While
it duplicates some undocumented functionality required by LWP, it does not
have all the methods of IO::Socket::INET and might act quite differently
in practise. Also, protocols other than the above mentioned will still block,
at least some of the time.

=back

All this likely makes other libraries than just LWP not block, but that's
just a side effect you cannot rely on.

Increases parallelism is not supported by all libraries, some might cache
data globally.

=cut

package Coro::LWP;

use common::sense;

BEGIN {
   # suppress warnings
   local $^W = 0;
   require Net::Config;
}

# do it as early as possible
use Coro::Select;

# import these so they can grab Socket::inet_aton
use AnyEvent::Util ();
use AnyEvent::DNS ();

use Coro::Util ();
use Coro::Socket ();
use Coro::AnyEvent ();

use Socket ();

use IO::Socket::INET ();

use Net::HTTP ();
use Net::FTP ();
use Net::NNTP ();

our $VERSION = 6.511;

*Socket::inet_aton = \&Coro::Util::inet_aton;

for (@Net::HTTP::ISA, @Net::FTP::ISA, @Net::NTTP::ISA) {
   $_ = Coro::LWP::Socket:: if $_ eq IO::Socket::INET::;
}

package Coro::LWP::Socket;

no warnings;

use base Coro::Socket::;

sub new {
   my $self = shift;

   $self->SUPER::new (@_, partial => 1)
}

1;

=head1 AUTHOR/SUPPORT/CONTACT

   Marc A. Lehmann <schmorp@schmorp.de>
   http://software.schmorp.de/pkg/Coro.html

=cut