1 |
|
---|
2 | =encoding utf8
|
---|
3 |
|
---|
4 | =head1 NAME
|
---|
5 |
|
---|
6 | Mojolicious::Guides::Cookbook - Cooking with Mojolicious
|
---|
7 |
|
---|
8 | =head1 OVERVIEW
|
---|
9 |
|
---|
10 | This document contains many fun recipes for cooking with L<Mojolicious>.
|
---|
11 |
|
---|
12 | =head1 CONCEPTS
|
---|
13 |
|
---|
14 | Essentials every L<Mojolicious> developer should know.
|
---|
15 |
|
---|
16 | =head2 Blocking and non-blocking operations
|
---|
17 |
|
---|
18 | A I<blocking> operation is a subroutine that blocks the execution of the
|
---|
19 | calling subroutine until the subroutine is finished.
|
---|
20 |
|
---|
21 | sub foo {
|
---|
22 | my $result = blocking_subroutine();
|
---|
23 | ...
|
---|
24 | }
|
---|
25 |
|
---|
26 | A I<non-blocking> operation on the other hand lets the calling subroutine
|
---|
27 | continue execution even though the subroutine is not yet finished. Instead of
|
---|
28 | waiting, the calling subroutine passes along a callback to be executed once the
|
---|
29 | subroutine is finished, this is called continuation-passing style.
|
---|
30 |
|
---|
31 | sub foo {
|
---|
32 | non_blocking_subroutine(sub {
|
---|
33 | my $result = shift;
|
---|
34 | ...
|
---|
35 | });
|
---|
36 | ...
|
---|
37 | }
|
---|
38 |
|
---|
39 | While L<Mojolicious> has been designed from the ground up for non-blocking I/O
|
---|
40 | and event loops, it is not possible to magically make Perl code non-blocking.
|
---|
41 | You have to use specialized non-blocking code available through modules like
|
---|
42 | L<Mojo::IOLoop> and L<Mojo::UserAgent>, or third-party event loops. You can wrap
|
---|
43 | your blocking code in L<subprocesses|Mojo::IOLoop/"subprocess"> though to
|
---|
44 | prevent it from interfering with your non-blocking code.
|
---|
45 |
|
---|
46 | =head2 Event loops
|
---|
47 |
|
---|
48 | An event loop is basically a loop that continually tests for external events
|
---|
49 | and executes the appropriate callbacks to handle them, it is often the main
|
---|
50 | loop in a program. Non-blocking tests for readability/writability of file
|
---|
51 | descriptors and timers are commonly used events for highly scalable network
|
---|
52 | servers, because they allow a single process to handle thousands of client
|
---|
53 | connections concurrently.
|
---|
54 |
|
---|
55 | while (1) {
|
---|
56 | my @readable = test_fds_for_readability();
|
---|
57 | handle_readable_fds(@readable);
|
---|
58 |
|
---|
59 | my @writable = test_fds_for_writability();
|
---|
60 | handle_writable_fds(@writable);
|
---|
61 |
|
---|
62 | my @expired = test_timers();
|
---|
63 | handle_timers(@expired);
|
---|
64 | }
|
---|
65 |
|
---|
66 | In L<Mojolicious> this event loop is L<Mojo::IOLoop>.
|
---|
67 |
|
---|
68 | =head2 Reverse proxy
|
---|
69 |
|
---|
70 | A reverse proxy architecture is a deployment technique used in many production
|
---|
71 | environments, where a I<reverse proxy> server is put in front of your
|
---|
72 | application to act as the endpoint accessible by external clients. It can
|
---|
73 | provide a lot of benefits, like terminating SSL connections from the outside,
|
---|
74 | limiting the number of concurrent open sockets towards the Mojolicious
|
---|
75 | application (or even using Unix sockets), balancing load across multiple
|
---|
76 | instances, or supporting several applications through the same IP/port.
|
---|
77 |
|
---|
78 | ..........................................
|
---|
79 | : :
|
---|
80 | +--------+ : +-----------+ +---------------+ :
|
---|
81 | | |-------->| | | | :
|
---|
82 | | client | : | reverse |----->| Mojolicious | :
|
---|
83 | | |<--------| proxy | | application | :
|
---|
84 | +--------+ : | |<-----| | :
|
---|
85 | : +-----------+ +---------------+ :
|
---|
86 | : :
|
---|
87 | .. system boundary (e.g. same host) ......
|
---|
88 |
|
---|
89 | This setup introduces some problems, though: the application will receive
|
---|
90 | requests from the reverse proxy instead of the original client; the
|
---|
91 | address/hostname where your application lives internally will be different from
|
---|
92 | the one visible from the outside; and if terminating SSL, the reverse proxy
|
---|
93 | exposes services via HTTPS while using HTTP towards the Mojolicious application.
|
---|
94 |
|
---|
95 | As an example, compare a sample request from the client and what the Mojolicious
|
---|
96 | application receives:
|
---|
97 |
|
---|
98 | client reverse proxy Mojolicious app
|
---|
99 | __|__ _______________|______________ ____|____
|
---|
100 | / \ / \ / \
|
---|
101 | 1.2.3.4 --HTTPS--> api.example.com 10.20.30.39 --HTTP--> 10.20.30.40
|
---|
102 |
|
---|
103 | GET /foo/1 HTTP/1.1 | GET /foo/1 HTTP/1.1
|
---|
104 | Host: api.example.com | Host: 10.20.30.40
|
---|
105 | User-Agent: Firefox | User-Agent: ShinyProxy/1.2
|
---|
106 | ... | ...
|
---|
107 |
|
---|
108 | However, now the client address is no longer available (which might be useful
|
---|
109 | for analytics, or Geo-IP) and URLs generated via
|
---|
110 | L<Mojolicious::Controller/"url_for"> will look like this:
|
---|
111 |
|
---|
112 | http://10.20.30.40/bar/2
|
---|
113 |
|
---|
114 | instead of something meaningful for the client, like this:
|
---|
115 |
|
---|
116 | https://api.example.com/bar/2
|
---|
117 |
|
---|
118 | To solve these problems, you can configure your reverse proxy to send the
|
---|
119 | missing data (see L</Nginx> and L</"Apache/mod_proxy">) and tell your
|
---|
120 | application about it by setting the environment variable C<MOJO_REVERSE_PROXY>.
|
---|
121 | For finer control, L</Rewriting> includes examples of how the changes could be
|
---|
122 | implemented manually.
|
---|
123 |
|
---|
124 | =head1 DEPLOYMENT
|
---|
125 |
|
---|
126 | Getting L<Mojolicious> and L<Mojolicious::Lite> applications running on
|
---|
127 | different platforms. Note that many real-time web features are based on the
|
---|
128 | L<Mojo::IOLoop> event loop, and therefore require one of the built-in web
|
---|
129 | servers to be able to use them to their full potential.
|
---|
130 |
|
---|
131 | =head2 Built-in web server
|
---|
132 |
|
---|
133 | L<Mojolicious> contains a very portable non-blocking I/O HTTP and WebSocket
|
---|
134 | server with L<Mojo::Server::Daemon>. It is usually used during development and
|
---|
135 | in the construction of more advanced web servers, but is solid and fast enough
|
---|
136 | for small to mid sized applications.
|
---|
137 |
|
---|
138 | $ ./script/my_app daemon
|
---|
139 | Server available at http://127.0.0.1:3000
|
---|
140 |
|
---|
141 | It is available to every application through the command
|
---|
142 | L<Mojolicious::Command::daemon>, which has many configuration options and is
|
---|
143 | known to work on every platform Perl works on with its single-process
|
---|
144 | architecture.
|
---|
145 |
|
---|
146 | $ ./script/my_app daemon -h
|
---|
147 | ...List of available options...
|
---|
148 |
|
---|
149 | Another huge advantage is that it supports TLS and WebSockets out of the box, a
|
---|
150 | development certificate for testing purposes is built right in, so it just
|
---|
151 | works, but you can specify all listen locations supported by
|
---|
152 | L<Mojo::Server::Daemon/"listen">.
|
---|
153 |
|
---|
154 | $ ./script/my_app daemon -l https://[::]:3000
|
---|
155 | Server available at https://[::]:3000
|
---|
156 |
|
---|
157 | To manage the web server with systemd, you can use a unit configuration file
|
---|
158 | like this.
|
---|
159 |
|
---|
160 | [Unit]
|
---|
161 | Description=My Mojolicious application
|
---|
162 | After=network.target
|
---|
163 |
|
---|
164 | [Service]
|
---|
165 | Type=simple
|
---|
166 | ExecStart=/home/sri/myapp/script/my_app daemon -m production -l http://*:8080
|
---|
167 |
|
---|
168 | [Install]
|
---|
169 | WantedBy=multi-user.target
|
---|
170 |
|
---|
171 | =head2 Pre-forking
|
---|
172 |
|
---|
173 | On UNIX platforms you can also add pre-forking to the built-in web server and
|
---|
174 | switch to a multi-process architecture with L<Mojolicious::Command::prefork>, to
|
---|
175 | take advantage of multiple CPU cores and copy-on-write memory management.
|
---|
176 |
|
---|
177 | $ ./script/my_app prefork
|
---|
178 | Server available at http://127.0.0.1:3000
|
---|
179 |
|
---|
180 | Since all built-in web servers are based on the L<Mojo::IOLoop> event loop,
|
---|
181 | they scale best with non-blocking operations. But if your application for some
|
---|
182 | reason needs to perform many blocking operations, you can improve performance
|
---|
183 | by increasing the number of worker processes and decreasing the number of
|
---|
184 | concurrent connections each worker is allowed to handle (often as low as C<1>).
|
---|
185 |
|
---|
186 | $ ./script/my_app prefork -m production -w 10 -c 1
|
---|
187 | Server available at http://127.0.0.1:3000
|
---|
188 |
|
---|
189 | During startup your application is preloaded in the manager process, which does
|
---|
190 | not run an event loop, so you can use L<Mojo::IOLoop/"next_tick"> to run code
|
---|
191 | whenever a new worker process has been forked and its event loop gets started.
|
---|
192 |
|
---|
193 | use Mojolicious::Lite;
|
---|
194 |
|
---|
195 | Mojo::IOLoop->next_tick(sub {
|
---|
196 | app->log->info("Worker $$ star...ALL GLORY TO THE HYPNOTOAD!");
|
---|
197 | });
|
---|
198 |
|
---|
199 | get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
|
---|
200 |
|
---|
201 | app->start;
|
---|
202 |
|
---|
203 | And to manage the pre-forking web server with systemd, you can use a unit
|
---|
204 | configuration file like this.
|
---|
205 |
|
---|
206 | [Unit]
|
---|
207 | Description=My Mojolicious application
|
---|
208 | After=network.target
|
---|
209 |
|
---|
210 | [Service]
|
---|
211 | Type=simple
|
---|
212 | ExecStart=/home/sri/myapp/script/my_app prefork -m production -l http://*:8080
|
---|
213 |
|
---|
214 | [Install]
|
---|
215 | WantedBy=multi-user.target
|
---|
216 |
|
---|
217 | =head2 Morbo
|
---|
218 |
|
---|
219 | After reading the L<Mojolicious::Guides::Tutorial>, you should already be
|
---|
220 | familiar with L<Mojo::Server::Morbo>.
|
---|
221 |
|
---|
222 | Mojo::Server::Morbo
|
---|
223 | +- Mojo::Server::Daemon
|
---|
224 |
|
---|
225 | It is basically a restarter that forks a new L<Mojo::Server::Daemon> web server
|
---|
226 | whenever a file in your project changes, and should therefore only be used
|
---|
227 | during development. To start applications with it you can use the L<morbo>
|
---|
228 | script.
|
---|
229 |
|
---|
230 | $ morbo ./script/my_app
|
---|
231 | Server available at http://127.0.0.1:3000
|
---|
232 |
|
---|
233 | =head2 Hypnotoad
|
---|
234 |
|
---|
235 | For bigger applications L<Mojolicious> contains the UNIX optimized pre-forking
|
---|
236 | web server L<Hypnotoad|Mojo::Server::Hypnotoad>, which can take advantage of
|
---|
237 | multiple CPU cores and copy-on-write memory management to scale up to thousands
|
---|
238 | of concurrent client connections.
|
---|
239 |
|
---|
240 | Mojo::Server::Hypnotoad
|
---|
241 | |- Mojo::Server::Daemon [1]
|
---|
242 | |- Mojo::Server::Daemon [2]
|
---|
243 | |- Mojo::Server::Daemon [3]
|
---|
244 | +- Mojo::Server::Daemon [4]
|
---|
245 |
|
---|
246 | It is based on the L<Mojo::Server::Prefork> web server, which adds pre-forking
|
---|
247 | to L<Mojo::Server::Daemon>, but optimized specifically for production
|
---|
248 | environments out of the box. To start applications with it you can use the
|
---|
249 | L<hypnotoad> script, which listens on port C<8080>, automatically daemonizes
|
---|
250 | the server process and defaults to C<production> mode for L<Mojolicious> and
|
---|
251 | L<Mojolicious::Lite> applications.
|
---|
252 |
|
---|
253 | $ hypnotoad ./script/my_app
|
---|
254 |
|
---|
255 | Many configuration settings can be tweaked right from within your application
|
---|
256 | with L<Mojolicious/"config">, for a full list see
|
---|
257 | L<Mojo::Server::Hypnotoad/"SETTINGS">.
|
---|
258 |
|
---|
259 | use Mojolicious::Lite;
|
---|
260 |
|
---|
261 | app->config(hypnotoad => {listen => ['http://*:80']});
|
---|
262 |
|
---|
263 | get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
|
---|
264 |
|
---|
265 | app->start;
|
---|
266 |
|
---|
267 | Or just add a C<hypnotoad> section to your L<Mojolicious::Plugin::Config> or
|
---|
268 | L<Mojolicious::Plugin::JSONConfig> configuration file.
|
---|
269 |
|
---|
270 | # myapp.conf
|
---|
271 | {
|
---|
272 | hypnotoad => {
|
---|
273 | listen => ['https://*:443?cert=/etc/server.crt&key=/etc/server.key'],
|
---|
274 | workers => 10
|
---|
275 | }
|
---|
276 | };
|
---|
277 |
|
---|
278 | But one of its biggest advantages is the support for effortless zero downtime
|
---|
279 | software upgrades (hot deployment). That means you can upgrade L<Mojolicious>,
|
---|
280 | Perl or even system libraries at runtime without ever stopping the server or
|
---|
281 | losing a single incoming connection, just by running the command above again.
|
---|
282 |
|
---|
283 | $ hypnotoad ./script/my_app
|
---|
284 | Starting hot deployment for Hypnotoad server 31841.
|
---|
285 |
|
---|
286 | You might also want to enable proxy support if you're using
|
---|
287 | L<Hypnotoad|Mojo::Server::Hypnotoad> behind a reverse proxy. This allows
|
---|
288 | L<Mojolicious> to automatically pick up the C<X-Forwarded-For> and
|
---|
289 | C<X-Forwarded-Proto> headers.
|
---|
290 |
|
---|
291 | # myapp.conf
|
---|
292 | {hypnotoad => {proxy => 1}};
|
---|
293 |
|
---|
294 | To manage L<Hypnotoad|Mojo::Server::Hypnotoad> with systemd, you can use a unit
|
---|
295 | configuration file like this.
|
---|
296 |
|
---|
297 | [Unit]
|
---|
298 | Description=My Mojolicious application
|
---|
299 | After=network.target
|
---|
300 |
|
---|
301 | [Service]
|
---|
302 | Type=forking
|
---|
303 | PIDFile=/home/sri/myapp/script/hypnotoad.pid
|
---|
304 | ExecStart=/path/to/hypnotoad /home/sri/myapp/script/my_app
|
---|
305 | ExecReload=/path/to/hypnotoad /home/sri/myapp/script/my_app
|
---|
306 | KillMode=process
|
---|
307 |
|
---|
308 | [Install]
|
---|
309 | WantedBy=multi-user.target
|
---|
310 |
|
---|
311 | =head2 Zero downtime software upgrades
|
---|
312 |
|
---|
313 | L<Hypnotoad|Mojo::Server::Hypnotoad> makes zero downtime software upgrades (hot
|
---|
314 | deployment) very simple, as you can see above, but on modern operating systems
|
---|
315 | that support the C<SO_REUSEPORT> socket option, there is also another method
|
---|
316 | available that works with all built-in web servers.
|
---|
317 |
|
---|
318 | $ ./script/my_app prefork -P /tmp/first.pid -l http://*:8080?reuse=1
|
---|
319 | Server available at http://127.0.0.1:8080
|
---|
320 |
|
---|
321 | All you have to do, is to start a second web server listening to the same port,
|
---|
322 | and stop the first web server gracefully afterwards.
|
---|
323 |
|
---|
324 | $ ./script/my_app prefork -P /tmp/second.pid -l http://*:8080?reuse=1
|
---|
325 | Server available at http://127.0.0.1:8080
|
---|
326 | $ kill -s TERM `cat /tmp/first.pid`
|
---|
327 |
|
---|
328 | Just remember that both web servers need to be started with the C<reuse>
|
---|
329 | parameter.
|
---|
330 |
|
---|
331 | =head2 Nginx
|
---|
332 |
|
---|
333 | One of the most popular setups these days is
|
---|
334 | L<Hypnotoad|Mojo::Server::Hypnotoad> behind an L<Nginx|http://nginx.org> reverse
|
---|
335 | proxy, which even supports WebSockets in newer versions.
|
---|
336 |
|
---|
337 | upstream myapp {
|
---|
338 | server 127.0.0.1:8080;
|
---|
339 | }
|
---|
340 | server {
|
---|
341 | listen 80;
|
---|
342 | server_name localhost;
|
---|
343 | location / {
|
---|
344 | proxy_pass http://myapp;
|
---|
345 | proxy_http_version 1.1;
|
---|
346 | proxy_set_header Upgrade $http_upgrade;
|
---|
347 | proxy_set_header Connection "upgrade";
|
---|
348 | proxy_set_header Host $host;
|
---|
349 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
---|
350 | proxy_set_header X-Forwarded-Proto $scheme;
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | =head2 Apache/mod_proxy
|
---|
355 |
|
---|
356 | Another good reverse proxy is L<Apache|http://httpd.apache.org> with
|
---|
357 | C<mod_proxy>, the configuration looks quite similar to the Nginx one above. And
|
---|
358 | if you need WebSocket support, newer versions come with C<mod_proxy_wstunnel>.
|
---|
359 |
|
---|
360 | <VirtualHost *:80>
|
---|
361 | ServerName localhost
|
---|
362 | <Proxy *>
|
---|
363 | Require all granted
|
---|
364 | </Proxy>
|
---|
365 | ProxyRequests Off
|
---|
366 | ProxyPreserveHost On
|
---|
367 | ProxyPass /echo ws://localhost:8080/echo
|
---|
368 | ProxyPass / http://localhost:8080/ keepalive=On
|
---|
369 | ProxyPassReverse / http://localhost:8080/
|
---|
370 | RequestHeader set X-Forwarded-Proto "http"
|
---|
371 | </VirtualHost>
|
---|
372 |
|
---|
373 | =head2 Apache/CGI
|
---|
374 |
|
---|
375 | C<CGI> is supported out of the box and your L<Mojolicious> application will
|
---|
376 | automatically detect that it is executed as a C<CGI> script. Its use in
|
---|
377 | production environments is discouraged though, because as a result of how
|
---|
378 | C<CGI> works, it is very slow and many web servers are making it exceptionally
|
---|
379 | hard to configure properly. Additionally, many real-time web features, such as
|
---|
380 | WebSockets, are not available.
|
---|
381 |
|
---|
382 | ScriptAlias / /home/sri/my_app/script/my_app/
|
---|
383 |
|
---|
384 | =head2 PSGI/Plack
|
---|
385 |
|
---|
386 | L<PSGI> is an interface between Perl web frameworks and web servers, and
|
---|
387 | L<Plack> is a Perl module and toolkit that contains L<PSGI> middleware, helpers
|
---|
388 | and adapters to web servers. L<PSGI> and L<Plack> are inspired by Python's WSGI
|
---|
389 | and Ruby's Rack. L<Mojolicious> applications are ridiculously simple to deploy
|
---|
390 | with L<Plack>, but be aware that many real-time web features, such as
|
---|
391 | WebSockets, are not available.
|
---|
392 |
|
---|
393 | $ plackup ./script/my_app
|
---|
394 |
|
---|
395 | L<Plack> provides many server and protocol adapters for you to choose from,
|
---|
396 | such as C<FCGI>, C<uWSGI> and C<mod_perl>.
|
---|
397 |
|
---|
398 | $ plackup ./script/my_app -s FCGI -l /tmp/myapp.sock
|
---|
399 |
|
---|
400 | The C<MOJO_REVERSE_PROXY> environment variable can be used to enable proxy
|
---|
401 | support, this allows L<Mojolicious> to automatically pick up the
|
---|
402 | C<X-Forwarded-For> and C<X-Forwarded-Proto> headers.
|
---|
403 |
|
---|
404 | $ MOJO_REVERSE_PROXY=1 plackup ./script/my_app
|
---|
405 |
|
---|
406 | If an older server adapter is unable to correctly detect the application home
|
---|
407 | directory, you can simply use the C<MOJO_HOME> environment variable.
|
---|
408 |
|
---|
409 | $ MOJO_HOME=/home/sri/my_app plackup ./script/my_app
|
---|
410 |
|
---|
411 | There is no need for a C<.psgi> file, just point the server adapter at your
|
---|
412 | application script, it will automatically act like one if it detects the
|
---|
413 | presence of a C<PLACK_ENV> environment variable.
|
---|
414 |
|
---|
415 | =head2 Plack middleware
|
---|
416 |
|
---|
417 | Wrapper scripts like C<myapp.fcgi> are a great way to separate deployment and
|
---|
418 | application logic.
|
---|
419 |
|
---|
420 | #!/usr/bin/env plackup -s FCGI
|
---|
421 | use Plack::Builder;
|
---|
422 |
|
---|
423 | builder {
|
---|
424 | enable 'Deflater';
|
---|
425 | require './script/my_app';
|
---|
426 | };
|
---|
427 |
|
---|
428 | L<Mojo::Server::PSGI> can be used directly to load and customize applications
|
---|
429 | in the wrapper script.
|
---|
430 |
|
---|
431 | #!/usr/bin/env plackup -s FCGI
|
---|
432 | use Mojo::Server::PSGI;
|
---|
433 | use Plack::Builder;
|
---|
434 |
|
---|
435 | builder {
|
---|
436 | enable 'Deflater';
|
---|
437 | my $server = Mojo::Server::PSGI->new;
|
---|
438 | $server->load_app('./script/my_app');
|
---|
439 | $server->app->config(foo => 'bar');
|
---|
440 | $server->to_psgi_app;
|
---|
441 | };
|
---|
442 |
|
---|
443 | But you could even use middleware right in your application.
|
---|
444 |
|
---|
445 | use Mojolicious::Lite;
|
---|
446 | use Plack::Builder;
|
---|
447 |
|
---|
448 | get '/welcome' => sub {
|
---|
449 | my $c = shift;
|
---|
450 | $c->render(text => 'Hello Mojo!');
|
---|
451 | };
|
---|
452 |
|
---|
453 | builder {
|
---|
454 | enable 'Deflater';
|
---|
455 | app->start;
|
---|
456 | };
|
---|
457 |
|
---|
458 | =head2 Rewriting
|
---|
459 |
|
---|
460 | Sometimes you might have to deploy your application in a blackbox environment
|
---|
461 | where you can't just change the server configuration or behind a reverse proxy
|
---|
462 | that passes along additional information with C<X-Forwarded-*> headers. In such
|
---|
463 | cases you can use the hook L<Mojolicious/"before_dispatch"> to rewrite incoming
|
---|
464 | requests.
|
---|
465 |
|
---|
466 | # Change scheme if "X-Forwarded-HTTPS" header is set
|
---|
467 | $app->hook(before_dispatch => sub {
|
---|
468 | my $c = shift;
|
---|
469 | $c->req->url->base->scheme('https')
|
---|
470 | if $c->req->headers->header('X-Forwarded-HTTPS');
|
---|
471 | });
|
---|
472 |
|
---|
473 | Since reverse proxies generally don't pass along information about path
|
---|
474 | prefixes your application might be deployed under, rewriting the base path of
|
---|
475 | incoming requests is also quite common. This allows
|
---|
476 | L<Mojolicious::Controller/"url_for"> for example, to generate portable URLs
|
---|
477 | based on the current environment.
|
---|
478 |
|
---|
479 | # Move first part and slash from path to base path in production mode
|
---|
480 | $app->hook(before_dispatch => sub {
|
---|
481 | my $c = shift;
|
---|
482 | push @{$c->req->url->base->path->trailing_slash(1)},
|
---|
483 | shift @{$c->req->url->path->leading_slash(0)};
|
---|
484 | }) if $app->mode eq 'production';
|
---|
485 |
|
---|
486 | L<Mojo::URL> objects are very easy to manipulate, just make sure that the URL
|
---|
487 | (C<foo/bar?baz=yada>), which represents the routing destination, is always
|
---|
488 | relative to the base URL (C<http://example.com/myapp/>), which represents the
|
---|
489 | deployment location of your application.
|
---|
490 |
|
---|
491 | =head2 Application embedding
|
---|
492 |
|
---|
493 | From time to time you might want to reuse parts of L<Mojolicious> applications
|
---|
494 | like configuration files, database connection or helpers for other scripts,
|
---|
495 | with this little L<Mojo::Server> based mock server you can just embed them.
|
---|
496 |
|
---|
497 | use Mojo::Server;
|
---|
498 |
|
---|
499 | # Load application with mock server
|
---|
500 | my $server = Mojo::Server->new;
|
---|
501 | my $app = $server->load_app('./myapp.pl');
|
---|
502 |
|
---|
503 | # Access fully initialized application
|
---|
504 | say for @{$app->static->paths};
|
---|
505 | say $app->config->{secret_identity};
|
---|
506 | say $app->dumper({just => 'a helper test'});
|
---|
507 | say $app->build_controller->render_to_string(template => 'foo');
|
---|
508 |
|
---|
509 | The plugin L<Mojolicious::Plugin::Mount> uses this functionality to allow you
|
---|
510 | to combine multiple applications into one and deploy them together.
|
---|
511 |
|
---|
512 | use Mojolicious::Lite;
|
---|
513 |
|
---|
514 | app->config(hypnotoad => {listen => ['http://*:80']});
|
---|
515 |
|
---|
516 | plugin Mount => {'test1.example.com' => '/home/sri/myapp1.pl'};
|
---|
517 | plugin Mount => {'test2.example.com' => '/home/sri/myapp2.pl'};
|
---|
518 |
|
---|
519 | app->start;
|
---|
520 |
|
---|
521 | =head2 Web server embedding
|
---|
522 |
|
---|
523 | You can also use L<Mojo::IOLoop/"one_tick"> to embed the built-in web server
|
---|
524 | L<Mojo::Server::Daemon> into alien environments like foreign event loops that
|
---|
525 | for some reason can't just be integrated with a new reactor backend.
|
---|
526 |
|
---|
527 | use Mojolicious::Lite;
|
---|
528 | use Mojo::IOLoop;
|
---|
529 | use Mojo::Server::Daemon;
|
---|
530 |
|
---|
531 | # Normal action
|
---|
532 | get '/' => {text => 'Hello World!'};
|
---|
533 |
|
---|
534 | # Connect application with web server and start accepting connections
|
---|
535 | my $daemon
|
---|
536 | = Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
|
---|
537 | $daemon->start;
|
---|
538 |
|
---|
539 | # Call "one_tick" repeatedly from the alien environment
|
---|
540 | Mojo::IOLoop->one_tick while 1;
|
---|
541 |
|
---|
542 | =head1 REAL-TIME WEB
|
---|
543 |
|
---|
544 | The real-time web is a collection of technologies that include Comet (long
|
---|
545 | polling), EventSource and WebSockets, which allow content to be pushed to
|
---|
546 | consumers with long-lived connections as soon as it is generated, instead of
|
---|
547 | relying on the more traditional pull model. All built-in web servers use
|
---|
548 | non-blocking I/O and are based on the L<Mojo::IOLoop> event loop, which
|
---|
549 | provides many very powerful features that allow real-time web applications to
|
---|
550 | scale up to thousands of concurrent client connections.
|
---|
551 |
|
---|
552 | =head2 Backend web services
|
---|
553 |
|
---|
554 | Since L<Mojo::UserAgent> is also based on the L<Mojo::IOLoop> event loop, it
|
---|
555 | won't block the built-in web servers when used non-blocking, even for high
|
---|
556 | latency backend web services.
|
---|
557 |
|
---|
558 | use Mojolicious::Lite;
|
---|
559 |
|
---|
560 | # Search MetaCPAN for "mojolicious"
|
---|
561 | get '/' => sub {
|
---|
562 | my $c = shift;
|
---|
563 | $c->ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
|
---|
564 | my ($ua, $tx) = @_;
|
---|
565 | $c->render('metacpan', hits => $tx->result->json->{hits}{hits});
|
---|
566 | });
|
---|
567 | };
|
---|
568 |
|
---|
569 | app->start;
|
---|
570 | __DATA__
|
---|
571 |
|
---|
572 | @@ metacpan.html.ep
|
---|
573 | <!DOCTYPE html>
|
---|
574 | <html>
|
---|
575 | <head><title>MetaCPAN results for "mojolicious"</title></head>
|
---|
576 | <body>
|
---|
577 | % for my $hit (@$hits) {
|
---|
578 | <p><%= $hit->{_source}{release} %></p>
|
---|
579 | % }
|
---|
580 | </body>
|
---|
581 | </html>
|
---|
582 |
|
---|
583 | The callback passed to L<Mojo::UserAgent/"get"> will be executed once the
|
---|
584 | request to the backend web service has been finished, this is called
|
---|
585 | continuation-passing style.
|
---|
586 |
|
---|
587 | =head2 Synchronizing non-blocking operations
|
---|
588 |
|
---|
589 | Multiple non-blocking operations, such as concurrent requests, can be easily
|
---|
590 | synchronized with promises and L<Mojo::Promise/"all">. You create
|
---|
591 | L<Mojo::Promise> objects manually or use methods like L<Mojo::UserAgent/"get_p">
|
---|
592 | that create them for you.
|
---|
593 |
|
---|
594 | use Mojolicious::Lite;
|
---|
595 | use Mojo::Promise;
|
---|
596 | use Mojo::URL;
|
---|
597 |
|
---|
598 | # Search MetaCPAN for "mojo" and "minion"
|
---|
599 | get '/' => sub {
|
---|
600 | my $c = shift;
|
---|
601 |
|
---|
602 | # Create two promises
|
---|
603 | my $url = Mojo::URL->new('fastapi.metacpan.org/v1/module/_search');
|
---|
604 | my $mojo = $c->ua->get_p($url->clone->query({q => 'mojo'}));
|
---|
605 | my $minion = $c->ua->get_p($url->clone->query({q => 'minion'}));
|
---|
606 |
|
---|
607 | # Render a response once both promises have been resolved
|
---|
608 | Mojo::Promise->all($mojo, $minion)->then(sub {
|
---|
609 | my ($mojo, $minion) = @_;
|
---|
610 | $c->render(json => {
|
---|
611 | mojo => $mojo->[0]->result->json('/hits/hits/0/_source/release'),
|
---|
612 | minion => $minion->[0]->result->json('/hits/hits/0/_source/release')
|
---|
613 | });
|
---|
614 | })->catch(sub {
|
---|
615 | my $err = shift;
|
---|
616 | $c->reply->exception($err);
|
---|
617 | })->wait;
|
---|
618 | };
|
---|
619 |
|
---|
620 | app->start;
|
---|
621 |
|
---|
622 | To create promises manually you just wrap your continuation-passing style APIs
|
---|
623 | in functions that return promises. Here's an example for how
|
---|
624 | L<Mojo::UserAgent/"get_p"> works internally.
|
---|
625 |
|
---|
626 | use Mojo::UserAgent;
|
---|
627 | use Mojo::Promise;
|
---|
628 |
|
---|
629 | # Wrap a user agent method with a promise
|
---|
630 | my $ua = Mojo::UserAgent->new;
|
---|
631 | sub get_p {
|
---|
632 | my $promise = Mojo::Promise->new;
|
---|
633 | $ua->get(@_ => sub {
|
---|
634 | my ($ua, $tx) = @_;
|
---|
635 | my $err = $tx->error;
|
---|
636 | $promise->resolve($tx) if !$err || $err->{code};
|
---|
637 | $promise->reject($err->{message});
|
---|
638 | });
|
---|
639 | return $promise;
|
---|
640 | }
|
---|
641 |
|
---|
642 | # Use our new promise generating function
|
---|
643 | get_p('https://mojolicious.org')->then(sub {
|
---|
644 | my $tx = shift;
|
---|
645 | say $tx->result->dom->at('title')->text;
|
---|
646 | })->wait;
|
---|
647 |
|
---|
648 | Promises have three states, they start out as C<pending> and you call
|
---|
649 | L<Mojo::Promise/"resolve"> to transition them to C<fulfilled>, or
|
---|
650 | L<Mojo::Promise/"reject"> to transition them to C<rejected>.
|
---|
651 |
|
---|
652 | =head2 Timers
|
---|
653 |
|
---|
654 | Timers, another primary feature of the event loop, are created with
|
---|
655 | L<Mojo::IOLoop/"timer"> and can, for example, be used to delay rendering of a
|
---|
656 | response, and unlike C<sleep>, won't block any other requests that might be
|
---|
657 | processed concurrently.
|
---|
658 |
|
---|
659 | use Mojolicious::Lite;
|
---|
660 | use Mojo::IOLoop;
|
---|
661 |
|
---|
662 | # Wait 3 seconds before rendering a response
|
---|
663 | get '/' => sub {
|
---|
664 | my $c = shift;
|
---|
665 | Mojo::IOLoop->timer(3 => sub {
|
---|
666 | $c->render(text => 'Delayed by 3 seconds!');
|
---|
667 | });
|
---|
668 | };
|
---|
669 |
|
---|
670 | app->start;
|
---|
671 |
|
---|
672 | Recurring timers created with L<Mojo::IOLoop/"recurring"> are slightly more
|
---|
673 | powerful, but need to be stopped manually, or they would just keep getting
|
---|
674 | emitted.
|
---|
675 |
|
---|
676 | use Mojolicious::Lite;
|
---|
677 | use Mojo::IOLoop;
|
---|
678 |
|
---|
679 | # Count to 5 in 1 second steps
|
---|
680 | get '/' => sub {
|
---|
681 | my $c = shift;
|
---|
682 |
|
---|
683 | # Start recurring timer
|
---|
684 | my $i = 1;
|
---|
685 | my $id = Mojo::IOLoop->recurring(1 => sub {
|
---|
686 | $c->write_chunk($i);
|
---|
687 | $c->finish if $i++ == 5;
|
---|
688 | });
|
---|
689 |
|
---|
690 | # Stop recurring timer
|
---|
691 | $c->on(finish => sub { Mojo::IOLoop->remove($id) });
|
---|
692 | };
|
---|
693 |
|
---|
694 | app->start;
|
---|
695 |
|
---|
696 | Timers are not tied to a specific request or connection, and can even be
|
---|
697 | created at startup time.
|
---|
698 |
|
---|
699 | use Mojolicious::Lite;
|
---|
700 | use Mojo::IOLoop;
|
---|
701 |
|
---|
702 | # Check title in the background every 10 seconds
|
---|
703 | my $title = 'Got no title yet.';
|
---|
704 | Mojo::IOLoop->recurring(10 => sub {
|
---|
705 | app->ua->get('https://mojolicious.org' => sub {
|
---|
706 | my ($ua, $tx) = @_;
|
---|
707 | $title = $tx->result->dom->at('title')->text;
|
---|
708 | });
|
---|
709 | });
|
---|
710 |
|
---|
711 | # Show current title
|
---|
712 | get '/' => sub {
|
---|
713 | my $c = shift;
|
---|
714 | $c->render(json => {title => $title});
|
---|
715 | };
|
---|
716 |
|
---|
717 | app->start;
|
---|
718 |
|
---|
719 | Just remember that all these non-blocking operations are processed
|
---|
720 | cooperatively, so your callbacks shouldn't block for too long.
|
---|
721 |
|
---|
722 | =head2 Subprocesses
|
---|
723 |
|
---|
724 | You can also use subprocesses, created with L<Mojo::IOLoop/"subprocess">, to
|
---|
725 | perform computationally expensive operations without blocking the event loop.
|
---|
726 |
|
---|
727 | use Mojolicious::Lite;
|
---|
728 | use Mojo::IOLoop;
|
---|
729 |
|
---|
730 | # Operation that would block the event loop for 5 seconds
|
---|
731 | get '/' => sub {
|
---|
732 | my $c = shift;
|
---|
733 | Mojo::IOLoop->subprocess(
|
---|
734 | sub {
|
---|
735 | my $subprocess = shift;
|
---|
736 | sleep 5;
|
---|
737 | return 'â¥', 'Mojolicious';
|
---|
738 | },
|
---|
739 | sub {
|
---|
740 | my ($subprocess, $err, @results) = @_;
|
---|
741 | $c->reply->exception($err) and return if $err;
|
---|
742 | $c->render(text => "I $results[0] $results[1]!");
|
---|
743 | }
|
---|
744 | );
|
---|
745 | };
|
---|
746 |
|
---|
747 | app->start;
|
---|
748 |
|
---|
749 | The first callback will be executed in a child process, without blocking the
|
---|
750 | event loop of the parent process. The results of the first callback will then be
|
---|
751 | shared between both processes, and the second callback executed in the parent
|
---|
752 | process.
|
---|
753 |
|
---|
754 | =head2 Exceptions in non-blocking operations
|
---|
755 |
|
---|
756 | Since timers and other non-blocking operations are running solely in the event
|
---|
757 | loop, outside of the application, exceptions that get thrown in callbacks can't
|
---|
758 | get caught and handled automatically. But you can handle them manually by
|
---|
759 | subscribing to the event L<Mojo::Reactor/"error"> or catching them inside the
|
---|
760 | callback.
|
---|
761 |
|
---|
762 | use Mojolicious::Lite;
|
---|
763 | use Mojo::IOLoop;
|
---|
764 |
|
---|
765 | # Forward error messages to the application log
|
---|
766 | Mojo::IOLoop->singleton->reactor->on(error => sub {
|
---|
767 | my ($reactor, $err) = @_;
|
---|
768 | app->log->error($err);
|
---|
769 | });
|
---|
770 |
|
---|
771 | # Exception only gets logged (and connection times out)
|
---|
772 | get '/connection_times_out' => sub {
|
---|
773 | my $c = shift;
|
---|
774 | Mojo::IOLoop->timer(2 => sub {
|
---|
775 | die 'This request will not be getting a response';
|
---|
776 | });
|
---|
777 | };
|
---|
778 |
|
---|
779 | # Exception gets caught and handled
|
---|
780 | get '/catch_exception' => sub {
|
---|
781 | my $c = shift;
|
---|
782 | Mojo::IOLoop->timer(2 => sub {
|
---|
783 | eval { die 'This request will be getting a response' };
|
---|
784 | $c->reply->exception($@) if $@;
|
---|
785 | });
|
---|
786 | };
|
---|
787 |
|
---|
788 | app->start;
|
---|
789 |
|
---|
790 | A default subscriber that turns all errors into warnings will usually be added
|
---|
791 | by L<Mojo::IOLoop> as a fallback.
|
---|
792 |
|
---|
793 | Mojo::IOLoop->singleton->reactor->unsubscribe('error');
|
---|
794 |
|
---|
795 | During development or for applications where crashing is simply preferable, you
|
---|
796 | can also make every exception that gets thrown in a callback fatal by removing
|
---|
797 | all of its subscribers.
|
---|
798 |
|
---|
799 | =head2 WebSocket web service
|
---|
800 |
|
---|
801 | The WebSocket protocol offers full bi-directional low-latency communication
|
---|
802 | channels between clients and servers. Receive messages just by subscribing to
|
---|
803 | events such as L<Mojo::Transaction::WebSocket/"message"> with
|
---|
804 | L<Mojolicious::Controller/"on"> and return them with
|
---|
805 | L<Mojolicious::Controller/"send">.
|
---|
806 |
|
---|
807 | use Mojolicious::Lite;
|
---|
808 |
|
---|
809 | # Template with browser-side code
|
---|
810 | get '/' => 'index';
|
---|
811 |
|
---|
812 | # WebSocket echo service
|
---|
813 | websocket '/echo' => sub {
|
---|
814 | my $c = shift;
|
---|
815 |
|
---|
816 | # Opened
|
---|
817 | $c->app->log->debug('WebSocket opened');
|
---|
818 |
|
---|
819 | # Increase inactivity timeout for connection a bit
|
---|
820 | $c->inactivity_timeout(300);
|
---|
821 |
|
---|
822 | # Incoming message
|
---|
823 | $c->on(message => sub {
|
---|
824 | my ($c, $msg) = @_;
|
---|
825 | $c->send("echo: $msg");
|
---|
826 | });
|
---|
827 |
|
---|
828 | # Closed
|
---|
829 | $c->on(finish => sub {
|
---|
830 | my ($c, $code, $reason) = @_;
|
---|
831 | $c->app->log->debug("WebSocket closed with status $code");
|
---|
832 | });
|
---|
833 | };
|
---|
834 |
|
---|
835 | app->start;
|
---|
836 | __DATA__
|
---|
837 |
|
---|
838 | @@ index.html.ep
|
---|
839 | <!DOCTYPE html>
|
---|
840 | <html>
|
---|
841 | <head><title>Echo</title></head>
|
---|
842 | <body>
|
---|
843 | <script>
|
---|
844 | var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
|
---|
845 |
|
---|
846 | // Incoming messages
|
---|
847 | ws.onmessage = function (event) {
|
---|
848 | document.body.innerHTML += event.data + '<br/>';
|
---|
849 | };
|
---|
850 |
|
---|
851 | // Outgoing messages
|
---|
852 | ws.onopen = function (event) {
|
---|
853 | window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
|
---|
854 | };
|
---|
855 | </script>
|
---|
856 | </body>
|
---|
857 | </html>
|
---|
858 |
|
---|
859 | The event L<Mojo::Transaction::WebSocket/"finish"> will be emitted right after
|
---|
860 | the WebSocket connection has been closed.
|
---|
861 |
|
---|
862 | $c->tx->with_compression;
|
---|
863 |
|
---|
864 | You can activate C<permessage-deflate> compression with
|
---|
865 | L<Mojo::Transaction::WebSocket/"with_compression">, this can result in much
|
---|
866 | better performance, but also increases memory usage by up to 300KiB per
|
---|
867 | connection.
|
---|
868 |
|
---|
869 | my $proto = $c->tx->with_protocols('v2.proto', 'v1.proto');
|
---|
870 |
|
---|
871 | You can also use L<Mojo::Transaction::WebSocket/"with_protocols"> to negotiate a
|
---|
872 | subprotocol.
|
---|
873 |
|
---|
874 | =head2 EventSource web service
|
---|
875 |
|
---|
876 | EventSource is a special form of long polling where you can use
|
---|
877 | L<Mojolicious::Controller/"write"> to directly send DOM events from servers to
|
---|
878 | clients. It is uni-directional, that means you will have to use Ajax requests
|
---|
879 | for sending data from clients to servers, the advantage however is low
|
---|
880 | infrastructure requirements, since it reuses the HTTP protocol for transport.
|
---|
881 |
|
---|
882 | use Mojolicious::Lite;
|
---|
883 |
|
---|
884 | # Template with browser-side code
|
---|
885 | get '/' => 'index';
|
---|
886 |
|
---|
887 | # EventSource for log messages
|
---|
888 | get '/events' => sub {
|
---|
889 | my $c = shift;
|
---|
890 |
|
---|
891 | # Increase inactivity timeout for connection a bit
|
---|
892 | $c->inactivity_timeout(300);
|
---|
893 |
|
---|
894 | # Change content type and finalize response headers
|
---|
895 | $c->res->headers->content_type('text/event-stream');
|
---|
896 | $c->write;
|
---|
897 |
|
---|
898 | # Subscribe to "message" event and forward "log" events to browser
|
---|
899 | my $cb = $c->app->log->on(message => sub {
|
---|
900 | my ($log, $level, @lines) = @_;
|
---|
901 | $c->write("event:log\ndata: [$level] @lines\n\n");
|
---|
902 | });
|
---|
903 |
|
---|
904 | # Unsubscribe from "message" event again once we are done
|
---|
905 | $c->on(finish => sub {
|
---|
906 | my $c = shift;
|
---|
907 | $c->app->log->unsubscribe(message => $cb);
|
---|
908 | });
|
---|
909 | };
|
---|
910 |
|
---|
911 | app->start;
|
---|
912 | __DATA__
|
---|
913 |
|
---|
914 | @@ index.html.ep
|
---|
915 | <!DOCTYPE html>
|
---|
916 | <html>
|
---|
917 | <head><title>LiveLog</title></head>
|
---|
918 | <body>
|
---|
919 | <script>
|
---|
920 | var events = new EventSource('<%= url_for 'events' %>');
|
---|
921 |
|
---|
922 | // Subscribe to "log" event
|
---|
923 | events.addEventListener('log', function (event) {
|
---|
924 | document.body.innerHTML += event.data + '<br/>';
|
---|
925 | }, false);
|
---|
926 | </script>
|
---|
927 | </body>
|
---|
928 | </html>
|
---|
929 |
|
---|
930 | The event L<Mojo::Log/"message"> will be emitted for every new log message and
|
---|
931 | the event L<Mojo::Transaction/"finish"> right after the transaction has been
|
---|
932 | finished.
|
---|
933 |
|
---|
934 | =head2 Streaming multipart uploads
|
---|
935 |
|
---|
936 | L<Mojolicious> contains a very sophisticated event system based on
|
---|
937 | L<Mojo::EventEmitter>, with ready-to-use events on almost all layers, and which
|
---|
938 | can be combined to solve some of the hardest problems in web development.
|
---|
939 |
|
---|
940 | use Mojolicious::Lite;
|
---|
941 | use Scalar::Util 'weaken';
|
---|
942 |
|
---|
943 | # Intercept multipart uploads and log each chunk received
|
---|
944 | hook after_build_tx => sub {
|
---|
945 | my $tx = shift;
|
---|
946 |
|
---|
947 | # Subscribe to "upgrade" event to identify multipart uploads
|
---|
948 | weaken $tx;
|
---|
949 | $tx->req->content->on(upgrade => sub {
|
---|
950 | my ($single, $multi) = @_;
|
---|
951 | return unless $tx->req->url->path->contains('/upload');
|
---|
952 |
|
---|
953 | # Subscribe to "part" event to find the right one
|
---|
954 | $multi->on(part => sub {
|
---|
955 | my ($multi, $single) = @_;
|
---|
956 |
|
---|
957 | # Subscribe to "body" event of part to make sure we have all headers
|
---|
958 | $single->on(body => sub {
|
---|
959 | my $single = shift;
|
---|
960 |
|
---|
961 | # Make sure we have the right part and replace "read" event
|
---|
962 | return unless $single->headers->content_disposition =~ /example/;
|
---|
963 | $single->unsubscribe('read')->on(read => sub {
|
---|
964 | my ($single, $bytes) = @_;
|
---|
965 |
|
---|
966 | # Log size of every chunk we receive
|
---|
967 | app->log->debug(length($bytes) . ' bytes uploaded');
|
---|
968 | });
|
---|
969 | });
|
---|
970 | });
|
---|
971 | });
|
---|
972 | };
|
---|
973 |
|
---|
974 | # Upload form in DATA section
|
---|
975 | get '/' => 'index';
|
---|
976 |
|
---|
977 | # Streaming multipart upload
|
---|
978 | post '/upload' => {text => 'Upload was successful.'};
|
---|
979 |
|
---|
980 | app->start;
|
---|
981 | __DATA__
|
---|
982 |
|
---|
983 | @@ index.html.ep
|
---|
984 | <!DOCTYPE html>
|
---|
985 | <html>
|
---|
986 | <head><title>Streaming multipart upload</title></head>
|
---|
987 | <body>
|
---|
988 | %= form_for upload => (enctype => 'multipart/form-data') => begin
|
---|
989 | %= file_field 'example'
|
---|
990 | %= submit_button 'Upload'
|
---|
991 | % end
|
---|
992 | </body>
|
---|
993 | </html>
|
---|
994 |
|
---|
995 | =head2 More event loops
|
---|
996 |
|
---|
997 | Internally, the L<Mojo::IOLoop> event loop can use multiple reactor backends,
|
---|
998 | L<EV> for example, will be automatically used if possible. Which in turn allows
|
---|
999 | other event loops like L<AnyEvent> to just work.
|
---|
1000 |
|
---|
1001 | use Mojolicious::Lite;
|
---|
1002 | use EV;
|
---|
1003 | use AnyEvent;
|
---|
1004 |
|
---|
1005 | # Wait 3 seconds before rendering a response
|
---|
1006 | get '/' => sub {
|
---|
1007 | my $c = shift;
|
---|
1008 | my $w;
|
---|
1009 | $w = AE::timer 3, 0, sub {
|
---|
1010 | $c->render(text => 'Delayed by 3 seconds!');
|
---|
1011 | undef $w;
|
---|
1012 | };
|
---|
1013 | };
|
---|
1014 |
|
---|
1015 | app->start;
|
---|
1016 |
|
---|
1017 | Who actually controls the event loop backend is not important.
|
---|
1018 |
|
---|
1019 | use Mojo::UserAgent;
|
---|
1020 | use EV;
|
---|
1021 | use AnyEvent;
|
---|
1022 |
|
---|
1023 | # Search MetaCPAN for "mojolicious"
|
---|
1024 | my $cv = AE::cv;
|
---|
1025 | my $ua = Mojo::UserAgent->new;
|
---|
1026 | $ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
|
---|
1027 | my ($ua, $tx) = @_;
|
---|
1028 | $cv->send($tx->result->json('/hits/hits/0/_source/release'));
|
---|
1029 | });
|
---|
1030 | say $cv->recv;
|
---|
1031 |
|
---|
1032 | You could, for example, just embed the built-in web server into an L<AnyEvent>
|
---|
1033 | application.
|
---|
1034 |
|
---|
1035 | use Mojolicious::Lite;
|
---|
1036 | use Mojo::Server::Daemon;
|
---|
1037 | use EV;
|
---|
1038 | use AnyEvent;
|
---|
1039 |
|
---|
1040 | # Normal action
|
---|
1041 | get '/' => {text => 'Hello World!'};
|
---|
1042 |
|
---|
1043 | # Connect application with web server and start accepting connections
|
---|
1044 | my $daemon
|
---|
1045 | = Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
|
---|
1046 | $daemon->start;
|
---|
1047 |
|
---|
1048 | # Let AnyEvent take control
|
---|
1049 | AE::cv->recv;
|
---|
1050 |
|
---|
1051 | =head1 USER AGENT
|
---|
1052 |
|
---|
1053 | When we say L<Mojolicious> is a web framework we actually mean it, with
|
---|
1054 | L<Mojo::UserAgent> there's a full featured HTTP and WebSocket user agent built
|
---|
1055 | right in.
|
---|
1056 |
|
---|
1057 | =head2 REST web services
|
---|
1058 |
|
---|
1059 | Requests can be performed very comfortably with methods like
|
---|
1060 | L<Mojo::UserAgent/"get">, and always result in a L<Mojo::Transaction::HTTP>
|
---|
1061 | object, which has many useful attributes and methods. You can check for
|
---|
1062 | connection errors with L<Mojo::Transaction/"result">, or access HTTP request and
|
---|
1063 | response information directly through L<Mojo::Transaction/"req"> and
|
---|
1064 | L<Mojo::Transaction/"res">.
|
---|
1065 |
|
---|
1066 | use Mojo::UserAgent;
|
---|
1067 |
|
---|
1068 | # Request a resource and make sure there were no connection errors
|
---|
1069 | my $ua = Mojo::UserAgent->new;
|
---|
1070 | my $tx = $ua->get('mojolicious.org/perldoc/Mojo' => {Accept => 'text/plain'});
|
---|
1071 | my $res = $tx->result;
|
---|
1072 |
|
---|
1073 | # Decide what to do with its representation
|
---|
1074 | if ($res->is_success) { say $res->body }
|
---|
1075 | elsif ($res->is_error) { say $res->message }
|
---|
1076 | elsif ($res->code == 301) { say $res->headers->location }
|
---|
1077 | else { say 'Whatever...' }
|
---|
1078 |
|
---|
1079 | While methods like L<Mojo::Message::Response/"is_success"> and
|
---|
1080 | L<Mojo::Message::Response/"is_error"> serve as building blocks for more
|
---|
1081 | sophisticated REST clients.
|
---|
1082 |
|
---|
1083 | =head2 Web scraping
|
---|
1084 |
|
---|
1085 | Scraping information from websites has never been this much fun before. The
|
---|
1086 | built-in HTML/XML parser L<Mojo::DOM> is accessible through
|
---|
1087 | L<Mojo::Message/"dom"> and supports all CSS selectors that make sense for a
|
---|
1088 | standalone parser, it can be a very powerful tool especially for testing web
|
---|
1089 | application.
|
---|
1090 |
|
---|
1091 | use Mojo::UserAgent;
|
---|
1092 |
|
---|
1093 | # Fetch website
|
---|
1094 | my $ua = Mojo::UserAgent->new;
|
---|
1095 | my $res = $ua->get('mojolicious.org/perldoc')->result;
|
---|
1096 |
|
---|
1097 | # Extract title
|
---|
1098 | say 'Title: ', $res->dom->at('head > title')->text;
|
---|
1099 |
|
---|
1100 | # Extract headings
|
---|
1101 | $res->dom('h1, h2, h3')->each(sub { say 'Heading: ', shift->all_text });
|
---|
1102 |
|
---|
1103 | # Visit all nodes recursively to extract more than just text
|
---|
1104 | for my $n ($res->dom->descendant_nodes->each) {
|
---|
1105 |
|
---|
1106 | # Text or CDATA node
|
---|
1107 | print $n->content if $n->type eq 'text' || $n->type eq 'cdata';
|
---|
1108 |
|
---|
1109 | # Also include alternate text for images
|
---|
1110 | print $n->{alt} if $n->type eq 'tag' && $n->tag eq 'img';
|
---|
1111 | }
|
---|
1112 |
|
---|
1113 | For a full list of available CSS selectors see L<Mojo::DOM::CSS/"SELECTORS">.
|
---|
1114 |
|
---|
1115 | =head2 JSON web services
|
---|
1116 |
|
---|
1117 | Most web services these days are based on the JSON data-interchange format.
|
---|
1118 | That's why L<Mojolicious> comes with the possibly fastest pure-Perl
|
---|
1119 | implementation L<Mojo::JSON> built right in, which is accessible through
|
---|
1120 | L<Mojo::Message/"json">.
|
---|
1121 |
|
---|
1122 | use Mojo::UserAgent;
|
---|
1123 | use Mojo::URL;
|
---|
1124 |
|
---|
1125 | # Fresh user agent
|
---|
1126 | my $ua = Mojo::UserAgent->new;
|
---|
1127 |
|
---|
1128 | # Search MetaCPAN for "mojolicious" and list latest releases
|
---|
1129 | my $url = Mojo::URL->new('http://fastapi.metacpan.org/v1/release/_search');
|
---|
1130 | $url->query({q => 'mojolicious', sort => 'date:desc'});
|
---|
1131 | for my $hit (@{$ua->get($url)->result->json->{hits}{hits}}) {
|
---|
1132 | say "$hit->{_source}{name} ($hit->{_source}{author})";
|
---|
1133 | }
|
---|
1134 |
|
---|
1135 | =head2 Basic authentication
|
---|
1136 |
|
---|
1137 | You can just add username and password to the URL, an C<Authorization> header
|
---|
1138 | will be automatically generated.
|
---|
1139 |
|
---|
1140 | use Mojo::UserAgent;
|
---|
1141 |
|
---|
1142 | my $ua = Mojo::UserAgent->new;
|
---|
1143 | say $ua->get('https://sri:[email protected]/hideout')->result->body;
|
---|
1144 |
|
---|
1145 | =head2 Decorating follow-up requests
|
---|
1146 |
|
---|
1147 | L<Mojo::UserAgent> can automatically follow redirects, the event
|
---|
1148 | L<Mojo::UserAgent/"start"> allows you direct access to each transaction right
|
---|
1149 | after they have been initialized and before a connection gets associated with
|
---|
1150 | them.
|
---|
1151 |
|
---|
1152 | use Mojo::UserAgent;
|
---|
1153 |
|
---|
1154 | # User agent following up to 10 redirects
|
---|
1155 | my $ua = Mojo::UserAgent->new(max_redirects => 10);
|
---|
1156 |
|
---|
1157 | # Add a witty header to every request
|
---|
1158 | $ua->on(start => sub {
|
---|
1159 | my ($ua, $tx) = @_;
|
---|
1160 | $tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
|
---|
1161 | say 'Request: ', $tx->req->url->clone->to_abs;
|
---|
1162 | });
|
---|
1163 |
|
---|
1164 | # Request that will most likely get redirected
|
---|
1165 | say 'Title: ', $ua->get('google.com')->result->dom->at('head > title')->text;
|
---|
1166 |
|
---|
1167 | This even works for proxy C<CONNECT> requests.
|
---|
1168 |
|
---|
1169 | =head2 Content generators
|
---|
1170 |
|
---|
1171 | Content generators can be registered with
|
---|
1172 | L<Mojo::UserAgent::Transactor/"add_generator"> to generate the same type of
|
---|
1173 | content repeatedly for multiple requests.
|
---|
1174 |
|
---|
1175 | use Mojo::UserAgent;
|
---|
1176 | use Mojo::Asset::File;
|
---|
1177 |
|
---|
1178 | # Add "stream" generator
|
---|
1179 | my $ua = Mojo::UserAgent->new;
|
---|
1180 | $ua->transactor->add_generator(stream => sub {
|
---|
1181 | my ($transactor, $tx, $path) = @_;
|
---|
1182 | $tx->req->content->asset(Mojo::Asset::File->new(path => $path));
|
---|
1183 | });
|
---|
1184 |
|
---|
1185 | # Send multiple files streaming via PUT and POST
|
---|
1186 | $ua->put('http://example.com/upload' => stream => '/home/sri/mojo.png');
|
---|
1187 | $ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
|
---|
1188 |
|
---|
1189 | The C<json>, C<form> and C<multipart> content generators are always available.
|
---|
1190 |
|
---|
1191 | use Mojo::UserAgent;
|
---|
1192 |
|
---|
1193 | # Send "application/json" content via PATCH
|
---|
1194 | my $ua = Mojo::UserAgent->new;
|
---|
1195 | my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'});
|
---|
1196 |
|
---|
1197 | # Send query parameters via GET
|
---|
1198 | my $tx2 = $ua->get('search.example.com' => form => {q => 'test'});
|
---|
1199 |
|
---|
1200 | # Send "application/x-www-form-urlencoded" content via POST
|
---|
1201 | my $tx3 = $ua->post('http://search.example.com' => form => {q => 'test'});
|
---|
1202 |
|
---|
1203 | # Send "multipart/form-data" content via PUT
|
---|
1204 | my $tx4 = $ua->put(
|
---|
1205 | 'upload.example.com' => form => {test => {content => 'Hello World!'}});
|
---|
1206 |
|
---|
1207 | # Send custom multipart content via PUT
|
---|
1208 | my $tx5 = $ua->put('api.example.com' => multipart => ['Hello', 'World!']);
|
---|
1209 |
|
---|
1210 | For more information about available content generators see also
|
---|
1211 | L<Mojo::UserAgent::Transactor/"tx">.
|
---|
1212 |
|
---|
1213 | =head2 Large file downloads
|
---|
1214 |
|
---|
1215 | When downloading large files with L<Mojo::UserAgent> you don't have to worry
|
---|
1216 | about memory usage at all, because it will automatically stream everything
|
---|
1217 | above 250KiB into a temporary file, which can then be moved into a permanent
|
---|
1218 | file with L<Mojo::Asset::File/"move_to">.
|
---|
1219 |
|
---|
1220 | use Mojo::UserAgent;
|
---|
1221 |
|
---|
1222 | # Fetch the latest Mojolicious tarball
|
---|
1223 | my $ua = Mojo::UserAgent->new(max_redirects => 5);
|
---|
1224 | my $tx = $ua->get('https://www.github.com/kraih/mojo/tarball/master');
|
---|
1225 | $tx->result->content->asset->move_to('mojo.tar.gz');
|
---|
1226 |
|
---|
1227 | To protect you from excessively large files there is also a limit of 2GiB by
|
---|
1228 | default, which you can tweak with the attribute
|
---|
1229 | L<Mojo::UserAgent/"max_response_size">.
|
---|
1230 |
|
---|
1231 | # Increase limit to 10GiB
|
---|
1232 | $ua->max_response_size(10737418240);
|
---|
1233 |
|
---|
1234 | =head2 Large file upload
|
---|
1235 |
|
---|
1236 | Uploading a large file is even easier.
|
---|
1237 |
|
---|
1238 | use Mojo::UserAgent;
|
---|
1239 |
|
---|
1240 | # Upload file via POST and "multipart/form-data"
|
---|
1241 | my $ua = Mojo::UserAgent->new;
|
---|
1242 | $ua->post('example.com/upload' =>
|
---|
1243 | form => {image => {file => '/home/sri/hello.png'}});
|
---|
1244 |
|
---|
1245 | And once again you don't have to worry about memory usage, all data will be
|
---|
1246 | streamed directly from the file.
|
---|
1247 |
|
---|
1248 | =head2 Streaming response
|
---|
1249 |
|
---|
1250 | Receiving a streaming response can be really tricky in most HTTP clients, but
|
---|
1251 | L<Mojo::UserAgent> makes it actually easy.
|
---|
1252 |
|
---|
1253 | use Mojo::UserAgent;
|
---|
1254 |
|
---|
1255 | # Accept responses of indefinite size
|
---|
1256 | my $ua = Mojo::UserAgent->new(max_response_size => 0);
|
---|
1257 |
|
---|
1258 | # Build a normal transaction
|
---|
1259 | my $tx = $ua->build_tx(GET => 'http://example.com');
|
---|
1260 |
|
---|
1261 | # Replace "read" events to disable default content parser
|
---|
1262 | $tx->res->content->unsubscribe('read')->on(read => sub {
|
---|
1263 | my ($content, $bytes) = @_;
|
---|
1264 | say "Streaming: $bytes";
|
---|
1265 | });
|
---|
1266 |
|
---|
1267 | # Process transaction
|
---|
1268 | $tx = $ua->start($tx);
|
---|
1269 |
|
---|
1270 | The event L<Mojo::Content/"read"> will be emitted for every chunk of data that
|
---|
1271 | is received, even chunked transfer encoding and gzip content encoding will be
|
---|
1272 | handled transparently if necessary.
|
---|
1273 |
|
---|
1274 | =head2 Streaming request
|
---|
1275 |
|
---|
1276 | Sending a streaming request is almost just as easy.
|
---|
1277 |
|
---|
1278 | use Mojo::UserAgent;
|
---|
1279 |
|
---|
1280 | # Build a normal transaction
|
---|
1281 | my $ua = Mojo::UserAgent->new;
|
---|
1282 | my $tx = $ua->build_tx(GET => 'http://example.com');
|
---|
1283 |
|
---|
1284 | # Prepare body
|
---|
1285 | my $body = 'Hello World!';
|
---|
1286 | $tx->req->headers->content_length(length $body);
|
---|
1287 |
|
---|
1288 | # Start writing directly with a drain callback
|
---|
1289 | my $drain;
|
---|
1290 | $drain = sub {
|
---|
1291 | my $content = shift;
|
---|
1292 | my $chunk = substr $body, 0, 1, '';
|
---|
1293 | $drain = undef unless length $body;
|
---|
1294 | $content->write($chunk, $drain);
|
---|
1295 | };
|
---|
1296 | $tx->req->content->$drain;
|
---|
1297 |
|
---|
1298 | # Process transaction
|
---|
1299 | $tx = $ua->start($tx);
|
---|
1300 |
|
---|
1301 | The drain callback passed to L<Mojo::Content/"write"> will be executed whenever
|
---|
1302 | the entire previous chunk of data has actually been written.
|
---|
1303 |
|
---|
1304 | =head2 Non-blocking
|
---|
1305 |
|
---|
1306 | L<Mojo::UserAgent> has been designed from the ground up to be non-blocking, the
|
---|
1307 | whole blocking API is just a simple convenience wrapper. Especially for high
|
---|
1308 | latency tasks like web crawling this can be extremely useful, because you can
|
---|
1309 | keep many concurrent connections active at the same time.
|
---|
1310 |
|
---|
1311 | use Mojo::UserAgent;
|
---|
1312 | use Mojo::IOLoop;
|
---|
1313 |
|
---|
1314 | # Concurrent non-blocking requests
|
---|
1315 | my $ua = Mojo::UserAgent->new;
|
---|
1316 | $ua->get('https://metacpan.org/search?q=mojo' => sub {
|
---|
1317 | my ($ua, $mojo) = @_;
|
---|
1318 | say $mojo->result->dom->at('title')->text;
|
---|
1319 | });
|
---|
1320 | $ua->get('https://metacpan.org/search?q=minion' => sub {
|
---|
1321 | my ($ua, $minion) = @_;
|
---|
1322 | say $minion->result->dom->at('title')->text;
|
---|
1323 | });
|
---|
1324 |
|
---|
1325 | # Start event loop if necessary
|
---|
1326 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
1327 |
|
---|
1328 | But don't try to open too many connections to one server at the same time, it
|
---|
1329 | might get overwhelmed. Better use a queue to process requests in smaller
|
---|
1330 | batches.
|
---|
1331 |
|
---|
1332 | use Mojo::UserAgent;
|
---|
1333 | use Mojo::IOLoop;
|
---|
1334 |
|
---|
1335 | my @urls = (
|
---|
1336 | 'mojolicious.org/perldoc/Mojo/DOM', 'mojolicious.org/perldoc/Mojo',
|
---|
1337 | 'mojolicious.org/perldoc/Mojo/File', 'mojolicious.org/perldoc/Mojo/URL'
|
---|
1338 | );
|
---|
1339 |
|
---|
1340 | # User agent with a custom name, following up to 5 redirects
|
---|
1341 | my $ua = Mojo::UserAgent->new(max_redirects => 5);
|
---|
1342 | $ua->transactor->name('MyParallelCrawler 1.0');
|
---|
1343 |
|
---|
1344 | # Use a delay to keep the event loop running until we are done
|
---|
1345 | my $delay = Mojo::IOLoop->delay;
|
---|
1346 | my $fetch;
|
---|
1347 | $fetch = sub {
|
---|
1348 |
|
---|
1349 | # Stop if there are no more URLs
|
---|
1350 | return unless my $url = shift @urls;
|
---|
1351 |
|
---|
1352 | # Fetch the next title
|
---|
1353 | my $end = $delay->begin;
|
---|
1354 | $ua->get($url => sub {
|
---|
1355 | my ($ua, $tx) = @_;
|
---|
1356 | say "$url: ", $tx->result->dom->at('title')->text;
|
---|
1357 |
|
---|
1358 | # Next request
|
---|
1359 | $fetch->();
|
---|
1360 | $end->();
|
---|
1361 | });
|
---|
1362 | };
|
---|
1363 |
|
---|
1364 | # Process two requests at a time
|
---|
1365 | $fetch->() for 1 .. 2;
|
---|
1366 | $delay->wait;
|
---|
1367 |
|
---|
1368 | It is also strongly recommended to respect every sites C<robots.txt> file as
|
---|
1369 | well as terms of service, and to wait a little before reopening connections to
|
---|
1370 | the same host, or the operators might be forced to block your access.
|
---|
1371 |
|
---|
1372 | =head2 Concurrent blocking requests
|
---|
1373 |
|
---|
1374 | You might have seen L<Mojo::Promise/"wait"> already in some examples
|
---|
1375 | above. It is used to make non-blocking operations portable, allowing them to
|
---|
1376 | work inside an already running event loop or start one on demand.
|
---|
1377 |
|
---|
1378 | use Mojo::UserAgent;
|
---|
1379 | use Mojo::Promise;
|
---|
1380 |
|
---|
1381 | # Synchronize non-blocking requests with promises
|
---|
1382 | my $ua = Mojo::UserAgent->new;
|
---|
1383 | my $mojo = $ua->get_p('https://metacpan.org/search?q=mojo');
|
---|
1384 | my $minion = $ua->get_p('https://metacpan.org/search?q=minion');
|
---|
1385 | Mojo::Promise->all($mojo, $minion)->then(sub {
|
---|
1386 | my ($mojo, $minion) = @_;
|
---|
1387 | say $mojo->[0]->result->dom->at('title')->text;
|
---|
1388 | say $minion->[0]->result->dom->at('title')->text;
|
---|
1389 | })->wait;
|
---|
1390 |
|
---|
1391 | =head2 WebSockets
|
---|
1392 |
|
---|
1393 | WebSockets are not just for the server-side, you can use
|
---|
1394 | L<Mojo::UserAgent/"websocket_p"> to open new connections, which are always
|
---|
1395 | non-blocking. The WebSocket handshake uses HTTP, and is a normal C<GET> request
|
---|
1396 | with a few additional headers. It can even contain cookies, and is followed by
|
---|
1397 | a C<101> response from the server, notifying our user agent that the connection
|
---|
1398 | has been established and it can start using the bi-directional WebSocket
|
---|
1399 | protocol.
|
---|
1400 |
|
---|
1401 | use Mojo::UserAgent;
|
---|
1402 | use Mojo::Promise;
|
---|
1403 |
|
---|
1404 | # Open WebSocket to echo service
|
---|
1405 | my $ua = Mojo::UserAgent->new;
|
---|
1406 | $ua->websocket_p('ws://echo.websocket.org')->then(sub {
|
---|
1407 | my $tx = shift;
|
---|
1408 |
|
---|
1409 | # Prepare a followup promise so we can wait for messages
|
---|
1410 | my $promise = Mojo::Promise->new;
|
---|
1411 |
|
---|
1412 | # Wait for WebSocket to be closed
|
---|
1413 | $tx->on(finish => sub {
|
---|
1414 | my ($tx, $code, $reason) = @_;
|
---|
1415 | say "WebSocket closed with status $code.";
|
---|
1416 | $promise->resolve;
|
---|
1417 | });
|
---|
1418 |
|
---|
1419 | # Close WebSocket after receiving one message
|
---|
1420 | $tx->on(message => sub {
|
---|
1421 | my ($tx, $msg) = @_;
|
---|
1422 | say "WebSocket message: $msg";
|
---|
1423 | $tx->finish;
|
---|
1424 | });
|
---|
1425 |
|
---|
1426 | # Send a message to the server
|
---|
1427 | $tx->send('Hi!');
|
---|
1428 |
|
---|
1429 | # Insert a new promise into the promise chain
|
---|
1430 | return $promise;
|
---|
1431 | })->catch(sub {
|
---|
1432 | my $err = shift;
|
---|
1433 |
|
---|
1434 | # Handle failed WebSocket handshakes and other exceptions
|
---|
1435 | warn "WebSocket error: $err";
|
---|
1436 | })->wait;
|
---|
1437 |
|
---|
1438 | =head2 UNIX domain sockets
|
---|
1439 |
|
---|
1440 | Not just TCP/IP sockets are supported, but also UNIX domain sockets, which can
|
---|
1441 | have significant security and performance benefits when used for inter-process
|
---|
1442 | communication. Instead of C<http://> and C<ws://> you can use the
|
---|
1443 | C<http+unix://> and C<ws+unix://> schemes, and pass along a percent encoded path
|
---|
1444 | (C</> becomes C<%2F>) instead of a hostname.
|
---|
1445 |
|
---|
1446 | use Mojo::UserAgent;
|
---|
1447 | use Mojo::Promise;
|
---|
1448 |
|
---|
1449 | # GET request via UNIX domain socket "/tmp/foo.sock"
|
---|
1450 | my $ua = Mojo::UserAgent->new;
|
---|
1451 | say $ua->get('http+unix://%2Ftmp%2Ffoo.sock/index.html')->result->body;
|
---|
1452 |
|
---|
1453 | # GET request with HOST header via UNIX domain socket "/tmp/bar.sock"
|
---|
1454 | my $tx = $ua->get('http+unix://%2Ftmp%2Fbar.sock' => {Host => 'example.com'});
|
---|
1455 | say $tx->result->body;
|
---|
1456 |
|
---|
1457 | # WebSocket connection via UNIX domain socket "/tmp/baz.sock"
|
---|
1458 | $ua->websocket_p('ws+unix://%2Ftmp%2Fbaz.sock/echo')->then(sub {
|
---|
1459 | my $tx = shift;
|
---|
1460 |
|
---|
1461 | my $promise = Mojo::Promise->new;
|
---|
1462 | $tx->on(finish => sub { $promise->resolve });
|
---|
1463 |
|
---|
1464 | $tx->on(message => sub {
|
---|
1465 | my ($tx, $msg) = @_;
|
---|
1466 | say "WebSocket message: $msg";
|
---|
1467 | $tx->finish;
|
---|
1468 | });
|
---|
1469 | $tx->send('Hi!');
|
---|
1470 |
|
---|
1471 | return $promise;
|
---|
1472 | })->catch(sub {
|
---|
1473 | my $err = shift;
|
---|
1474 | warn "WebSocket error: $err";
|
---|
1475 | })->wait;
|
---|
1476 |
|
---|
1477 | You can set the C<Host> header manually to pass along a hostname.
|
---|
1478 |
|
---|
1479 | =head2 Command line
|
---|
1480 |
|
---|
1481 | Don't you hate checking huge HTML files from the command line? Thanks to the
|
---|
1482 | command L<Mojolicious::Command::get> that is about to change. You can just pick
|
---|
1483 | the parts that actually matter with the CSS selectors from L<Mojo::DOM> and
|
---|
1484 | JSON Pointers from L<Mojo::JSON::Pointer>.
|
---|
1485 |
|
---|
1486 | $ mojo get https://mojolicious.org 'head > title'
|
---|
1487 |
|
---|
1488 | How about a list of all id attributes?
|
---|
1489 |
|
---|
1490 | $ mojo get https://mojolicious.org '*' attr id
|
---|
1491 |
|
---|
1492 | Or the text content of all heading tags?
|
---|
1493 |
|
---|
1494 | $ mojo get https://mojolicious.org 'h1, h2, h3' text
|
---|
1495 |
|
---|
1496 | Maybe just the text of the third heading?
|
---|
1497 |
|
---|
1498 | $ mojo get https://mojolicious.org 'h1, h2, h3' 3 text
|
---|
1499 |
|
---|
1500 | You can also extract all text from nested child elements.
|
---|
1501 |
|
---|
1502 | $ mojo get https://mojolicious.org '#mojobar' all
|
---|
1503 |
|
---|
1504 | The request can be customized as well.
|
---|
1505 |
|
---|
1506 | $ mojo get -M POST -H 'X-Bender: Bite my shiny metal ass!' http://google.com
|
---|
1507 |
|
---|
1508 | Store response data by redirecting C<STDOUT>.
|
---|
1509 |
|
---|
1510 | $ mojo get mojolicious.org > example.html
|
---|
1511 |
|
---|
1512 | Pass request data by redirecting C<STDIN>.
|
---|
1513 |
|
---|
1514 | $ mojo get -M PUT mojolicious.org < example.html
|
---|
1515 |
|
---|
1516 | Or use the output of another program.
|
---|
1517 |
|
---|
1518 | $ echo 'Hello World' | mojo get -M PUT https://mojolicious.org
|
---|
1519 |
|
---|
1520 | Submit forms as C<application/x-www-form-urlencoded> content.
|
---|
1521 |
|
---|
1522 | $ mojo get -M POST -f 'q=Mojo' -f 'size=5' https://metacpan.org/search
|
---|
1523 |
|
---|
1524 | And upload files as C<multipart/form-data> content.
|
---|
1525 |
|
---|
1526 | $ mojo get -M POST -f '[email protected]' mojolicious.org
|
---|
1527 |
|
---|
1528 | You can follow redirects and view the headers for all messages.
|
---|
1529 |
|
---|
1530 | $ mojo get -r -v http://google.com 'head > title'
|
---|
1531 |
|
---|
1532 | Extract just the information you really need from JSON data structures.
|
---|
1533 |
|
---|
1534 | $ mojo get https://fastapi.metacpan.org/v1/author/SRI /name
|
---|
1535 |
|
---|
1536 | This can be an invaluable tool for testing your applications.
|
---|
1537 |
|
---|
1538 | $ ./myapp.pl get /welcome 'head > title'
|
---|
1539 |
|
---|
1540 | =head2 One-liners
|
---|
1541 |
|
---|
1542 | For quick hacks and especially testing, L<ojo> one-liners are also a great
|
---|
1543 | choice.
|
---|
1544 |
|
---|
1545 | $ perl -Mojo -E 'say g("mojolicious.org")->dom->at("title")->text'
|
---|
1546 |
|
---|
1547 | =head1 APPLICATIONS
|
---|
1548 |
|
---|
1549 | Fun L<Mojolicious> application hacks for all occasions.
|
---|
1550 |
|
---|
1551 | =head2 Basic authentication
|
---|
1552 |
|
---|
1553 | Basic authentication data will be automatically extracted from the
|
---|
1554 | C<Authorization> header.
|
---|
1555 |
|
---|
1556 | use Mojolicious::Lite;
|
---|
1557 | use Mojo::Util 'secure_compare';
|
---|
1558 |
|
---|
1559 | get '/' => sub {
|
---|
1560 | my $c = shift;
|
---|
1561 |
|
---|
1562 | # Check for username "Bender" and password "rocks"
|
---|
1563 | return $c->render(text => 'Hello Bender!')
|
---|
1564 | if secure_compare $c->req->url->to_abs->userinfo, 'Bender:rocks';
|
---|
1565 |
|
---|
1566 | # Require authentication
|
---|
1567 | $c->res->headers->www_authenticate('Basic');
|
---|
1568 | $c->render(text => 'Authentication required!', status => 401);
|
---|
1569 | };
|
---|
1570 |
|
---|
1571 | app->start;
|
---|
1572 |
|
---|
1573 | This can be combined with TLS for a secure authentication mechanism.
|
---|
1574 |
|
---|
1575 | $ ./myapp.pl daemon -l 'https://*:3000?cert=./server.crt&key=./server.key'
|
---|
1576 |
|
---|
1577 | =head2 Adding a configuration file
|
---|
1578 |
|
---|
1579 | Adding a configuration file to your application is as easy as adding a file to
|
---|
1580 | its home directory and loading the plugin L<Mojolicious::Plugin::Config>. The
|
---|
1581 | default name is based on the value of L<Mojolicious/"moniker"> (C<myapp>),
|
---|
1582 | appended with a C<.conf> extension (C<myapp.conf>).
|
---|
1583 |
|
---|
1584 | $ mkdir myapp
|
---|
1585 | $ cd myapp
|
---|
1586 | $ touch myapp.pl
|
---|
1587 | $ chmod 744 myapp.pl
|
---|
1588 | $ echo '{name => "my Mojolicious application"};' > myapp.conf
|
---|
1589 |
|
---|
1590 | Configuration files themselves are just Perl scripts that return a hash
|
---|
1591 | reference with configuration settings of your choice. All those settings are
|
---|
1592 | then available through the method L<Mojolicious/"config"> and the helper
|
---|
1593 | L<Mojolicious::Plugin::DefaultHelpers/"config">.
|
---|
1594 |
|
---|
1595 | use Mojolicious::Lite;
|
---|
1596 |
|
---|
1597 | plugin 'Config';
|
---|
1598 |
|
---|
1599 | my $name = app->config('name');
|
---|
1600 | app->log->debug("Welcome to $name");
|
---|
1601 |
|
---|
1602 | get '/' => 'with_config';
|
---|
1603 |
|
---|
1604 | app->start;
|
---|
1605 | __DATA__
|
---|
1606 | @@ with_config.html.ep
|
---|
1607 | <!DOCTYPE html>
|
---|
1608 | <html>
|
---|
1609 | <head><title><%= config 'name' %></title></head>
|
---|
1610 | <body>Welcome to <%= config 'name' %></body>
|
---|
1611 | </html>
|
---|
1612 |
|
---|
1613 | Alternatively you can also use configuration files in the JSON format with
|
---|
1614 | L<Mojolicious::Plugin::JSONConfig>.
|
---|
1615 |
|
---|
1616 | =head2 Adding a plugin to your application
|
---|
1617 |
|
---|
1618 | To organize your code better and to prevent helpers from cluttering your
|
---|
1619 | application, you can use application specific plugins.
|
---|
1620 |
|
---|
1621 | $ mkdir -p lib/MyApp/Plugin
|
---|
1622 | $ touch lib/MyApp/Plugin/MyHelpers.pm
|
---|
1623 |
|
---|
1624 | They work just like normal plugins and are also subclasses of
|
---|
1625 | L<Mojolicious::Plugin>. Nested helpers with a prefix based on the plugin name
|
---|
1626 | are an easy way to avoid conflicts.
|
---|
1627 |
|
---|
1628 | package MyApp::Plugin::MyHelpers;
|
---|
1629 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1630 |
|
---|
1631 | sub register {
|
---|
1632 | my ($self, $app) = @_;
|
---|
1633 | $app->helper('my_helpers.render_with_header' => sub {
|
---|
1634 | my ($c, @args) = @_;
|
---|
1635 | $c->res->headers->header('X-Mojo' => 'I <3 Mojolicious!');
|
---|
1636 | $c->render(@args);
|
---|
1637 | });
|
---|
1638 | }
|
---|
1639 |
|
---|
1640 | 1;
|
---|
1641 |
|
---|
1642 | You can have as many application specific plugins as you like, the only
|
---|
1643 | difference to normal plugins is that you load them using their full class name.
|
---|
1644 |
|
---|
1645 | use Mojolicious::Lite;
|
---|
1646 |
|
---|
1647 | use lib 'lib';
|
---|
1648 |
|
---|
1649 | plugin 'MyApp::Plugin::MyHelpers';
|
---|
1650 |
|
---|
1651 | get '/' => sub {
|
---|
1652 | my $c = shift;
|
---|
1653 | $c->my_helpers->render_with_header(text => 'I ⥠Mojolicious!');
|
---|
1654 | };
|
---|
1655 |
|
---|
1656 | app->start;
|
---|
1657 |
|
---|
1658 | Of course these plugins can contain more than just helpers, take a look at
|
---|
1659 | L<Mojolicious::Plugins/"PLUGINS"> for a few ideas.
|
---|
1660 |
|
---|
1661 | =head2 Adding commands to Mojolicious
|
---|
1662 |
|
---|
1663 | By now you've probably used many of the built-in commands described in
|
---|
1664 | L<Mojolicious::Commands>, but did you know that you can just add new ones and
|
---|
1665 | that they will be picked up automatically by the command line interface if they
|
---|
1666 | are placed in a directory from C<@INC>?
|
---|
1667 |
|
---|
1668 | package Mojolicious::Command::spy;
|
---|
1669 | use Mojo::Base 'Mojolicious::Command';
|
---|
1670 |
|
---|
1671 | has description => 'Spy on application';
|
---|
1672 | has usage => "Usage: APPLICATION spy [TARGET]\n";
|
---|
1673 |
|
---|
1674 | sub run {
|
---|
1675 | my ($self, @args) = @_;
|
---|
1676 |
|
---|
1677 | # Leak secret passphrases
|
---|
1678 | if ($args[0] eq 'secrets') { say for @{$self->app->secrets} }
|
---|
1679 |
|
---|
1680 | # Leak mode
|
---|
1681 | elsif ($args[0] eq 'mode') { say $self->app->mode }
|
---|
1682 | }
|
---|
1683 |
|
---|
1684 | 1;
|
---|
1685 |
|
---|
1686 | Command line arguments are passed right through and there are many useful
|
---|
1687 | attributes and methods in L<Mojolicious::Command> that you can use or overload.
|
---|
1688 |
|
---|
1689 | $ mojo spy secrets
|
---|
1690 | HelloWorld
|
---|
1691 |
|
---|
1692 | $ ./script/myapp spy secrets
|
---|
1693 | secr3t
|
---|
1694 |
|
---|
1695 | And to make your commands application specific, just add a custom namespace to
|
---|
1696 | L<Mojolicious::Commands/"namespaces"> and use a class name like
|
---|
1697 | C<MyApp::Command::spy> instead of C<Mojolicious::Command::spy>.
|
---|
1698 |
|
---|
1699 | # Application
|
---|
1700 | package MyApp;
|
---|
1701 | use Mojo::Base 'Mojolicious';
|
---|
1702 |
|
---|
1703 | sub startup {
|
---|
1704 | my $self = shift;
|
---|
1705 |
|
---|
1706 | # Add another namespace to load commands from
|
---|
1707 | push @{$self->commands->namespaces}, 'MyApp::Command';
|
---|
1708 | }
|
---|
1709 |
|
---|
1710 | 1;
|
---|
1711 |
|
---|
1712 | The options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode> are handled
|
---|
1713 | automatically by L<Mojolicious::Commands> and are shared by all commands.
|
---|
1714 |
|
---|
1715 | $ ./script/myapp spy -m production mode
|
---|
1716 | production
|
---|
1717 |
|
---|
1718 | For a full list of shared options see L<Mojolicious::Commands/"SYNOPSIS">.
|
---|
1719 |
|
---|
1720 | =head2 Running code against your application
|
---|
1721 |
|
---|
1722 | Ever thought about running a quick one-liner against your L<Mojolicious>
|
---|
1723 | application to test something? Thanks to the command
|
---|
1724 | L<Mojolicious::Command::eval> you can do just that, the application object
|
---|
1725 | itself can be accessed via C<app>.
|
---|
1726 |
|
---|
1727 | $ mojo generate lite_app myapp.pl
|
---|
1728 | $ ./myapp.pl eval 'say for @{app->static->paths}'
|
---|
1729 | $ ./myapp.pl eval 'say for sort keys %{app->renderer->helpers}'
|
---|
1730 |
|
---|
1731 | The C<verbose> options will automatically print the return value or returned
|
---|
1732 | data structure to C<STDOUT>.
|
---|
1733 |
|
---|
1734 | $ ./myapp.pl eval -v 'app->static->paths->[0]'
|
---|
1735 | $ ./myapp.pl eval -V 'app->static->paths'
|
---|
1736 |
|
---|
1737 | =head2 Making your application installable
|
---|
1738 |
|
---|
1739 | Ever thought about releasing your L<Mojolicious> application to CPAN? It's
|
---|
1740 | actually much easier than you might think.
|
---|
1741 |
|
---|
1742 | $ mojo generate app MyApp
|
---|
1743 | $ cd my_app
|
---|
1744 | $ mv public lib/MyApp/
|
---|
1745 | $ mv templates lib/MyApp/
|
---|
1746 |
|
---|
1747 | The trick is to move the C<public> and C<templates> directories so they can get
|
---|
1748 | automatically installed with the modules.
|
---|
1749 |
|
---|
1750 | # Application
|
---|
1751 | package MyApp;
|
---|
1752 | use Mojo::Base 'Mojolicious';
|
---|
1753 |
|
---|
1754 | use Mojo::File 'path';
|
---|
1755 | use Mojo::Home;
|
---|
1756 |
|
---|
1757 | # Every CPAN module needs a version
|
---|
1758 | our $VERSION = '1.0';
|
---|
1759 |
|
---|
1760 | sub startup {
|
---|
1761 | my $self = shift;
|
---|
1762 |
|
---|
1763 | # Switch to installable home directory
|
---|
1764 | $self->home(Mojo::Home->new(path(__FILE__)->sibling('MyApp')));
|
---|
1765 |
|
---|
1766 | # Switch to installable "public" directory
|
---|
1767 | $self->static->paths->[0] = $self->home->child('public');
|
---|
1768 |
|
---|
1769 | # Switch to installable "templates" directory
|
---|
1770 | $self->renderer->paths->[0] = $self->home->child('templates');
|
---|
1771 |
|
---|
1772 | $self->plugin('PODRenderer');
|
---|
1773 |
|
---|
1774 | my $r = $self->routes;
|
---|
1775 | $r->get('/welcome')->to('example#welcome');
|
---|
1776 | }
|
---|
1777 |
|
---|
1778 | 1;
|
---|
1779 |
|
---|
1780 | Finally there is just one small change to be made to the application script. The
|
---|
1781 | shebang line becomes the recommended C<#!perl>, which the toolchain can rewrite
|
---|
1782 | to the proper shebang during installation.
|
---|
1783 |
|
---|
1784 | #!perl
|
---|
1785 |
|
---|
1786 | use strict;
|
---|
1787 | use warnings;
|
---|
1788 |
|
---|
1789 | use FindBin;
|
---|
1790 | BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
|
---|
1791 | use Mojolicious::Commands;
|
---|
1792 |
|
---|
1793 | # Start command line interface for application
|
---|
1794 | Mojolicious::Commands->start_app('MyApp');
|
---|
1795 |
|
---|
1796 | That's really everything, now you can package your application like any other
|
---|
1797 | CPAN module.
|
---|
1798 |
|
---|
1799 | $ ./script/my_app generate makefile
|
---|
1800 | $ perl Makefile.PL
|
---|
1801 | $ make test
|
---|
1802 | $ make manifest
|
---|
1803 | $ make dist
|
---|
1804 |
|
---|
1805 | And if you have a PAUSE account (which can be requested at
|
---|
1806 | L<http://pause.perl.org>) even upload it.
|
---|
1807 |
|
---|
1808 | $ mojo cpanify -u USER -p PASS MyApp-0.01.tar.gz
|
---|
1809 |
|
---|
1810 | =head2 Hello World
|
---|
1811 |
|
---|
1812 | If every byte matters this is the smallest C<Hello World> application you can
|
---|
1813 | write with L<Mojolicious::Lite>.
|
---|
1814 |
|
---|
1815 | use Mojolicious::Lite;
|
---|
1816 | any {text => 'Hello World!'};
|
---|
1817 | app->start;
|
---|
1818 |
|
---|
1819 | It works because all routes without a pattern default to C</> and automatic
|
---|
1820 | rendering kicks in even if no actual code gets executed by the router. The
|
---|
1821 | renderer just picks up the C<text> value from the stash and generates a
|
---|
1822 | response.
|
---|
1823 |
|
---|
1824 | =head2 Hello World one-liners
|
---|
1825 |
|
---|
1826 | The C<Hello World> example above can get even a little bit shorter in an L<ojo>
|
---|
1827 | one-liner.
|
---|
1828 |
|
---|
1829 | $ perl -Mojo -E 'a({text => "Hello World!"})->start' daemon
|
---|
1830 |
|
---|
1831 | And you can use all the commands from L<Mojolicious::Commands>.
|
---|
1832 |
|
---|
1833 | $ perl -Mojo -E 'a({text => "Hello World!"})->start' get -v /
|
---|
1834 |
|
---|
1835 | =head1 MORE
|
---|
1836 |
|
---|
1837 | You can continue with L<Mojolicious::Guides> now or take a look at the
|
---|
1838 | L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
|
---|
1839 | documentation and examples by many different authors.
|
---|
1840 |
|
---|
1841 | =head1 SUPPORT
|
---|
1842 |
|
---|
1843 | If you have any questions the documentation might not yet answer, don't
|
---|
1844 | hesitate to ask on the
|
---|
1845 | L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
|
---|
1846 | channel C<#mojo> on C<irc.perl.org>
|
---|
1847 | (L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
|
---|
1848 |
|
---|
1849 | =cut
|
---|