├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── linux.yml │ ├── macos.yml │ ├── perltidy.yml │ ├── rebuild-website.yml │ └── windows.yml ├── .gitignore ├── .mergify └── config.yml ├── .perltidyrc ├── Changes ├── LICENSE ├── MANIFEST.SKIP ├── Makefile.PL ├── README.md ├── examples ├── chat.pl ├── connect-proxy.pl ├── entities.pl ├── fast.pl ├── hello-template.pl ├── hello.pl ├── login │ ├── lib │ │ ├── LoginApp.pm │ │ └── LoginApp │ │ │ ├── Controller │ │ │ └── Login.pm │ │ │ └── Model │ │ │ └── Users.pm │ ├── script │ │ └── login_app │ ├── t │ │ └── login.t │ └── templates │ │ ├── layouts │ │ └── default.html.ep │ │ └── login │ │ ├── index.html.ep │ │ └── protected.html.ep ├── microhttpd.pl ├── proxy.pl └── responses.pl ├── lib ├── Mojo.pm ├── Mojo │ ├── Asset.pm │ ├── Asset │ │ ├── File.pm │ │ └── Memory.pm │ ├── Base.pm │ ├── BaseUtil.pm │ ├── ByteStream.pm │ ├── Cache.pm │ ├── Collection.pm │ ├── Content.pm │ ├── Content │ │ ├── MultiPart.pm │ │ └── Single.pm │ ├── Cookie.pm │ ├── Cookie │ │ ├── Request.pm │ │ └── Response.pm │ ├── DOM.pm │ ├── DOM │ │ ├── CSS.pm │ │ └── HTML.pm │ ├── Date.pm │ ├── DynamicMethods.pm │ ├── EventEmitter.pm │ ├── Exception.pm │ ├── File.pm │ ├── Headers.pm │ ├── HelloWorld.pm │ ├── Home.pm │ ├── IOLoop.pm │ ├── IOLoop │ │ ├── Client.pm │ │ ├── Server.pm │ │ ├── Stream.pm │ │ ├── Subprocess.pm │ │ ├── TLS.pm │ │ └── resources │ │ │ ├── server.crt │ │ │ └── server.key │ ├── JSON.pm │ ├── JSON │ │ └── Pointer.pm │ ├── Loader.pm │ ├── Log.pm │ ├── Message.pm │ ├── Message │ │ ├── Request.pm │ │ └── Response.pm │ ├── Parameters.pm │ ├── Path.pm │ ├── Promise.pm │ ├── Reactor.pm │ ├── Reactor │ │ ├── EV.pm │ │ └── Poll.pm │ ├── Server.pm │ ├── Server │ │ ├── CGI.pm │ │ ├── Daemon.pm │ │ ├── Hypnotoad.pm │ │ ├── Morbo.pm │ │ ├── Morbo │ │ │ ├── Backend.pm │ │ │ └── Backend │ │ │ │ └── Poll.pm │ │ ├── PSGI.pm │ │ └── Prefork.pm │ ├── Template.pm │ ├── Transaction.pm │ ├── Transaction │ │ ├── HTTP.pm │ │ └── WebSocket.pm │ ├── URL.pm │ ├── Upload.pm │ ├── UserAgent.pm │ ├── UserAgent │ │ ├── CookieJar.pm │ │ ├── Proxy.pm │ │ ├── Server.pm │ │ └── Transactor.pm │ ├── Util.pm │ ├── WebSocket.pm │ └── resources │ │ └── html_entities.txt ├── Mojolicious.pm ├── Mojolicious │ ├── Command.pm │ ├── Command │ │ ├── Author │ │ │ ├── cpanify.pm │ │ │ ├── generate.pm │ │ │ ├── generate │ │ │ │ ├── app.pm │ │ │ │ ├── dockerfile.pm │ │ │ │ ├── lite_app.pm │ │ │ │ ├── makefile.pm │ │ │ │ └── plugin.pm │ │ │ └── inflate.pm │ │ ├── cgi.pm │ │ ├── daemon.pm │ │ ├── eval.pm │ │ ├── get.pm │ │ ├── prefork.pm │ │ ├── psgi.pm │ │ ├── routes.pm │ │ └── version.pm │ ├── Commands.pm │ ├── Controller.pm │ ├── Guides.pod │ ├── Guides │ │ ├── Contributing.pod │ │ ├── Cookbook.pod │ │ ├── FAQ.pod │ │ ├── Growing.pod │ │ ├── Rendering.pod │ │ ├── Routing.pod │ │ ├── Testing.pod │ │ └── Tutorial.pod │ ├── Lite.pm │ ├── Plugin.pm │ ├── Plugin │ │ ├── Config.pm │ │ ├── DefaultHelpers.pm │ │ ├── EPLRenderer.pm │ │ ├── EPRenderer.pm │ │ ├── HeaderCondition.pm │ │ ├── JSONConfig.pm │ │ ├── Mount.pm │ │ ├── NotYAMLConfig.pm │ │ └── TagHelpers.pm │ ├── Plugins.pm │ ├── Renderer.pm │ ├── Routes.pm │ ├── Routes │ │ ├── Match.pm │ │ ├── Pattern.pm │ │ └── Route.pm │ ├── Sessions.pm │ ├── Static.pm │ ├── Types.pm │ ├── Validator.pm │ ├── Validator │ │ └── Validation.pm │ └── resources │ │ ├── public │ │ ├── favicon.ico │ │ └── mojo │ │ │ ├── bootstrap │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.css.map │ │ │ ├── failraptor.png │ │ │ ├── highlight.js │ │ │ ├── highlight-mojo-dark.css │ │ │ ├── highlight.min.js │ │ │ └── mojolicious.min.js │ │ │ ├── logo-white-2x.png │ │ │ ├── logo-white.png │ │ │ ├── logo.png │ │ │ ├── mojo.css │ │ │ ├── noraptor.png │ │ │ ├── notfound.png │ │ │ ├── pinstripe-dark.png │ │ │ ├── pinstripe-light.png │ │ │ └── popper │ │ │ └── popper.js │ │ └── templates │ │ └── mojo │ │ ├── debug.html.ep │ │ ├── exception.html.ep │ │ └── not_found.html.ep ├── Test │ └── Mojo.pm └── ojo.pm ├── script ├── hypnotoad ├── mojo └── morbo └── t ├── mojo ├── asset.t ├── base.t ├── base_util.t ├── bytestream.t ├── cache.t ├── certs │ ├── bad.crt │ ├── bad.key │ ├── ca.crt │ ├── ca.key │ ├── client.crt │ ├── client.key │ ├── domain.crt │ ├── domain.key │ ├── server.crt │ └── server.key ├── cgi.t ├── collection.t ├── content.t ├── cookie.t ├── cookiejar.t ├── cookies │ └── curl.txt ├── daemon.t ├── daemon_ipv6_tls.t ├── date.t ├── dom.t ├── dynamic_methods.t ├── eventemitter.t ├── exception.t ├── exception │ ├── non_utf8.txt │ └── utf8.txt ├── file.t ├── file_download.t ├── headers.t ├── home.t ├── hypnotoad.t ├── ioloop.t ├── ioloop_ipv6.t ├── ioloop_tls.t ├── json.t ├── json_pointer.t ├── json_xs.t ├── lib │ ├── Mojo │ │ ├── .hidden.txt │ │ ├── .test │ │ │ └── hidden.txt │ │ ├── BaseTest │ │ │ ├── Base1.pm │ │ │ ├── Base2.pm │ │ │ └── Base3.pm │ │ ├── DeprecationTest.pm │ │ ├── LoaderException.pm │ │ ├── LoaderException2.pm │ │ ├── LoaderTest │ │ │ ├── A.pm │ │ │ ├── B.pm │ │ │ ├── C.pm │ │ │ ├── D.txt │ │ │ └── E │ │ │ │ ├── F.pm │ │ │ │ └── G.txt │ │ ├── LoaderTestException │ │ │ └── A.pm │ │ ├── Server │ │ │ └── Morbo │ │ │ │ └── Backend │ │ │ │ └── TestBackend.pm │ │ └── TestConnectProxy.pm │ ├── myapp-module-true.pl │ └── myapp.pl ├── loader.t ├── log.t ├── morbo.t ├── parameters.t ├── path.t ├── prefork.t ├── promise.t ├── promise_async_await.t ├── proxy.t ├── psgi.t ├── reactor_detect.t ├── reactor_ev.t ├── reactor_poll.t ├── request.t ├── request_cgi.t ├── response.t ├── roles.t ├── signatures.t ├── subprocess.t ├── subprocess_ev.t ├── template.t ├── templates │ ├── exception.mt │ ├── test.mt │ └── utf8_exception.mt ├── tls.t ├── transactor.t ├── url.t ├── user_agent.t ├── user_agent_online.t ├── user_agent_socks.t ├── user_agent_tls.t ├── user_agent_unix.t ├── util.t ├── websocket.t ├── websocket_frames.t ├── websocket_proxy.t └── websocket_proxy_tls.t ├── mojolicious ├── app.t ├── charset_lite_app.t ├── command.t ├── commands.t ├── dispatch.t ├── dispatcher_lite_app.t ├── embedded_app.t ├── embedded_lite_app.json ├── embedded_lite_app.t ├── exception_lite_app.t ├── external │ ├── lib │ │ └── MyApp.pm │ ├── my_app.conf │ ├── my_app.testing.conf │ ├── myapp.conf │ ├── myapp.pl │ ├── myapp.testing.conf │ ├── myapp2.pl │ ├── public │ │ └── index.html │ ├── script │ │ └── my_app │ └── templates │ │ └── index.html.ep ├── external_app.t ├── external_lite_app.t ├── group_lite_app.t ├── json_config_lite_app.json ├── json_config_lite_app.t ├── json_config_lite_app_abs.development.json ├── json_config_lite_app_abs.json ├── json_config_mode_lite_app.json ├── json_config_mode_lite_app.t ├── json_config_mode_lite_app.testing.json ├── layouted_lite_app.t ├── lib │ ├── AroundPlugin.pm │ ├── EmbeddedTestApp.pm │ ├── MojoliciousConfigTest.pm │ ├── MojoliciousTest.pm │ ├── MojoliciousTest │ │ ├── Baz.pm │ │ ├── Command │ │ │ ├── _test2_command.pm │ │ │ └── test_command.pm │ │ ├── Controller │ │ │ └── Foo │ │ │ │ └── Bar.pm │ │ ├── Exceptional.pm │ │ ├── Foo.pm │ │ ├── PODTest.pm │ │ ├── Plugin │ │ │ ├── DeploymentPlugin.pm │ │ │ ├── Test │ │ │ │ └── SomePlugin2.pm │ │ │ └── UPPERCASETestPlugin.pm │ │ ├── SideEffects │ │ │ └── Test.pm │ │ └── SyntaxError.pm │ ├── MojoliciousTest2 │ │ └── Foo.pm │ ├── MojoliciousTest3 │ │ ├── Bar.pm │ │ └── Baz.pm │ ├── PluginWithEmbeddedApp.pm │ ├── PluginWithTemplate.pm │ └── SingleFileTestApp.pm ├── lite_app.t ├── log_lite_app.t ├── longpolling_lite_app.t ├── mojolicious_config_test.whatever.conf ├── multipath_lite_app.t ├── ojo.t ├── ojo_signatures.t ├── pattern.t ├── production_app.t ├── proxy_app.t ├── public │ ├── assets │ │ ├── foo.ab1234cd5678ef.css │ │ ├── foo.ab1234cd5678ef.js │ │ └── foo │ │ │ ├── bar.321.js │ │ │ └── bar │ │ │ ├── baz.123.js │ │ │ ├── baz.development.js │ │ │ ├── test.ab1234cd5678ef.min.js │ │ │ └── yada.css │ ├── hello.txt │ ├── hello2.txt │ └── hello4.txt ├── public2 │ ├── hello.txt │ └── hello3.txt ├── public_dev │ ├── another │ │ └── file │ └── hello.txt ├── rebased_lite_app.t ├── renderer.t ├── restful_lite_app.t ├── routes.t ├── secret.txt ├── session_lite_app.t ├── signatures_lite_app.t ├── single_file_test_app.conf ├── static_lite_app.t ├── static_prefix_lite_app.t ├── tag_helper_lite_app.t ├── templates │ ├── 23.html.epl │ ├── WithGreenLayout.html.epl │ ├── dies_too.html.ep │ ├── encoding.koi8-r.ep │ ├── exception.html.epl │ ├── exception.testing.html.ep │ ├── foo │ │ ├── bar.rss.ep │ │ ├── bar │ │ │ ├── index.html.epl │ │ │ └── test.html.ep │ │ ├── index.html.xpl │ │ └── yada.html.ep │ ├── layouts │ │ ├── default.html.epl │ │ └── green.html.epl │ ├── not_found.testing.html.ep │ ├── simple.html.pod │ ├── syntaxerror.html.epl │ └── withblock.txt.epl ├── templates2 │ ├── 42.html+test.ep │ ├── 42.html.ep │ └── foo │ │ └── yada.html.epl ├── testing_app.t ├── tls_lite_app.t ├── twinkle_lite_app.conf ├── twinkle_lite_app.t ├── types.t ├── upload_lite_app.t ├── upload_stream_lite_app.t ├── validation_lite_app.t ├── websocket_lite_app.t ├── yaml_config_lite_app.t ├── yaml_config_lite_app.yaml ├── yaml_config_lite_app.yml ├── yaml_config_lite_app_abs.development.yml └── yaml_config_lite_app_abs.yml ├── pod.t ├── pod_coverage.t └── test └── mojo.t /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pl linguist-language=Perl 2 | *.pm linguist-language=Perl 3 | *.t linguist-language=Perl 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please read the guide for [contributing to Mojolicious](http://mojolicious.org/perldoc/Mojolicious/Guides/Contributing). 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * Mojolicious version: VERSION HERE 2 | * Perl version: VERSION HERE 3 | * Operating system: NAME AND VERSION HERE 4 | 5 | ### Steps to reproduce the behavior 6 | EXPLAIN WHAT HAPPENED HERE, PREFERABLY WITH CODE EXAMPLES 7 | 8 | ### Expected behavior 9 | EXPLAIN WHAT SHOULD HAPPEN HERE 10 | 11 | ### Actual behavior 12 | EXPLAIN WHAT HAPPENED INSTEAD HERE 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | DESCRIBE THE BIG PICTURE OF YOUR CHANGES HERE 3 | 4 | ### Motivation 5 | EXPLAIN WHY YOU BELIEVE THESE CHANGES ARE NECESSARY HERE 6 | 7 | ### References 8 | LIST RELEVANT ISSUES, PULL REQUESTS AND FORUM DISCUSSIONS HERE 9 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | tags-ignore: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | perl: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | codename: 15 | - buster 16 | perl-version: 17 | - '5.16' 18 | - '5.18' 19 | - '5.20' 20 | - '5.22' 21 | - '5.30' 22 | - '5.36' 23 | - '5.40' 24 | container: 25 | image: perl:${{ matrix.perl-version }}-${{ matrix.codename }} 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: perl -V 29 | run: perl -V 30 | - name: Fix ExtUtils::MakeMaker (for Perl 5.16 and 5.18) 31 | run: cpanm -n App::cpanminus ExtUtils::MakeMaker 32 | - name: Install dependencies 33 | run: | 34 | cpanm -n --installdeps . 35 | cpanm -n Cpanel::JSON::XS EV Role::Tiny CryptX 36 | cpanm -n Test::Pod Test::Pod::Coverage TAP::Formatter::GitHubActions 37 | - name: Run tests 38 | run: prove --merge --formatter TAP::Formatter::GitHubActions -l t t/mojo t/mojolicious 39 | env: 40 | TEST_POD: 1 41 | TEST_EV: 1 42 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | tags-ignore: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | perl: 11 | runs-on: macOS-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Perl 15 | run: brew install perl 16 | - name: perl -V 17 | run: perl -V 18 | - name: Install Dependencies 19 | run: | 20 | curl -L https://cpanmin.us | perl - --installdeps . 21 | curl -L https://cpanmin.us | perl - -n TAP::Formatter::GitHubActions 22 | - name: Run Tests 23 | run: prove --merge --formatter TAP::Formatter::GitHubActions -l t t/mojo t/mojolicious 24 | -------------------------------------------------------------------------------- /.github/workflows/perltidy.yml: -------------------------------------------------------------------------------- 1 | name: perltidy 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | tags-ignore: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | perltidy: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: perl:5.32 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Fix git permissions 17 | # work around https://github.com/actions/checkout/issues/766 18 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 19 | - name: perl -V 20 | run: perl -V 21 | - name: Install dependencies 22 | run: cpanm -n Perl::Tidy 23 | - name: perltidy --version 24 | run: perltidy --version 25 | - name: Run perltidy 26 | shell: bash 27 | run: | 28 | export GLOBIGNORE=t/mojo/lib/Mojo/LoaderException.pm:t/mojo/lib/Mojo/LoaderTestException/A.pm:t/mojolicious/lib/MojoliciousTest/SyntaxError.pm 29 | shopt -s extglob globstar nullglob 30 | perltidy --pro=.../.perltidyrc -b -bext='/' **/*.p[lm] **/*.t && git diff --exit-code 31 | -------------------------------------------------------------------------------- /.github/workflows/rebuild-website.yml: -------------------------------------------------------------------------------- 1 | name: Rebuild Website 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | rebuild_website: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Trigger website workflow 11 | run: | 12 | curl \ 13 | -X POST \ 14 | -u "${{ secrets.WORKFLOW_DISPATCH_USERINFO }}" \ 15 | -H "Accept: application/vnd.github.everest-preview+json" \ 16 | -H "Content-Type: application/json" \ 17 | --data '{"ref": "main"}' \ 18 | https://api.github.com/repos/mojolicious/mojolicious.org/actions/workflows/publish-website.yml/dispatches 19 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | tags-ignore: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | perl: 11 | runs-on: windows-latest 12 | steps: 13 | - name: Set git to use LF 14 | run: | 15 | git config --global core.autocrlf false 16 | git config --global core.eol lf 17 | - uses: actions/checkout@v2 18 | - name: Set up Perl 19 | run: | 20 | choco install strawberryperl 21 | echo "C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 22 | - name: perl -V 23 | run: perl -V 24 | - name: Install Dependencies 25 | run: | 26 | cpanm --installdeps . 27 | cpanm -n TAP::Formatter::GitHubActions 28 | - name: Run Tests 29 | run: prove --merge --formatter TAP::Formatter::GitHubActions -l t t/mojo t/mojolicious 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *~ 3 | !.gitignore 4 | !.perltidyrc 5 | !/.github 6 | /blib 7 | /pm_to_blib 8 | /Makefile 9 | /Makefile.old 10 | /MANIFEST* 11 | !MANIFEST.SKIP 12 | /META.* 13 | /MYMETA.* 14 | -------------------------------------------------------------------------------- /.mergify/config.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatic merge 3 | conditions: 4 | - "#approved-reviews-by>=2" 5 | - "#changes-requested-reviews-by=0" 6 | - label!=work in progress 7 | - base=main 8 | actions: 9 | merge: 10 | method: merge 11 | - name: remove outdated reviews 12 | conditions: 13 | - base=main 14 | actions: 15 | dismiss_reviews: {} 16 | - name: ask to resolve conflict 17 | conditions: 18 | - conflict 19 | actions: 20 | comment: 21 | message: This pull request is now in conflicts. Could you fix it @{{author}}? 🙏 22 | -------------------------------------------------------------------------------- /.perltidyrc: -------------------------------------------------------------------------------- 1 | -pbp # Start with Perl Best Practices 2 | -w # Show all warnings 3 | -iob # Ignore old breakpoints 4 | -l=120 # 120 characters per line 5 | -mbl=2 # No more than 2 blank lines 6 | -i=2 # Indentation is 2 columns 7 | -ci=2 # Continuation indentation is 2 columns 8 | -vt=0 # Less vertical tightness 9 | -pt=2 # High parenthesis tightness 10 | -bt=2 # High brace tightness 11 | -sbt=2 # High square bracket tightness 12 | -wn # Weld nested containers 13 | -isbc # Don't indent comments without leading space 14 | -nst # Don't output to STDOUT 15 | -------------------------------------------------------------------------------- /MANIFEST.SKIP: -------------------------------------------------------------------------------- 1 | ^\.(?!perltidyrc) 2 | .*\.old$ 3 | \.tar\.gz$ 4 | ^Makefile$ 5 | ^MYMETA\. 6 | ^blib 7 | ^pm_to_blib 8 | \B\.DS_Store 9 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use 5.016; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use ExtUtils::MakeMaker; 7 | 8 | # Sub::Util 1.41 first shipped with Perl 5.21.4 9 | # IO::Socket::IP 0.37 first shipped with Perl 5.21.11 10 | WriteMakefile( 11 | NAME => 'Mojolicious', 12 | VERSION_FROM => 'lib/Mojolicious.pm', 13 | ABSTRACT => 'Real-time web framework', 14 | AUTHOR => 'Sebastian Riedel ', 15 | LICENSE => 'artistic_2', 16 | META_MERGE => { 17 | dynamic_config => 0, 18 | 'meta-spec' => {version => 2}, 19 | no_index => 20 | {directory => [qw(examples t)], package => [qw(Mojo::Log::_Capture Mojo::Server::PSGI::_IO Mojo::Util::_Guard)],}, 21 | prereqs => {runtime => {requires => {perl => '5.016'}}}, 22 | resources => { 23 | bugtracker => {web => 'https://github.com/mojolicious/mojo/issues'}, 24 | homepage => 'https://mojolicious.org', 25 | license => ['http://www.opensource.org/licenses/artistic-license-2.0'], 26 | repository => { 27 | type => 'git', 28 | url => 'https://github.com/mojolicious/mojo.git', 29 | web => 'https://github.com/mojolicious/mojo', 30 | }, 31 | x_IRC => {url => 'irc://irc.libera.chat/#mojo', web => 'https://web.libera.chat/#mojo'} 32 | }, 33 | }, 34 | PREREQ_PM => {'IO::Socket::IP' => '0.37', 'Sub::Util' => '1.41'}, 35 | EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'], 36 | test => {TESTS => 't/*.t t/*/*.t'} 37 | ); 38 | -------------------------------------------------------------------------------- /examples/chat.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Minimal single-process WebSocket chat application for browser testing 3 | # 4 | use Mojolicious::Lite -signatures; 5 | use Mojo::EventEmitter; 6 | 7 | helper events => sub { state $events = Mojo::EventEmitter->new }; 8 | 9 | get '/' => 'chat'; 10 | 11 | websocket '/channel' => sub ($c) { 12 | $c->inactivity_timeout(3600); 13 | 14 | # Forward messages from the browser 15 | $c->on(message => sub ($c, $msg) { $c->events->emit(mojochat => $msg) }); 16 | 17 | # Forward messages to the browser 18 | my $cb = $c->events->on(mojochat => sub { $c->send(pop) }); 19 | $c->on(finish => sub ($c, $code, $reason = undef) { $c->events->unsubscribe(mojochat => $cb) }); 20 | }; 21 | 22 | app->start; 23 | __DATA__ 24 | 25 | @@ chat.html.ep 26 |
27 |
28 | 35 | -------------------------------------------------------------------------------- /examples/connect-proxy.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Minimal CONNECT proxy server for debugging 3 | # 4 | # $ HTTPS_PROXY=http://127.0.0.1:3000 mojo get https://mojolicious.org 5 | # 6 | use Mojo::Base -strict, -signatures; 7 | use Mojo::IOLoop; 8 | 9 | my %buffer; 10 | Mojo::IOLoop->server( 11 | {port => 3000} => sub ($loop, $stream, $id) { 12 | 13 | # Connection to client 14 | $stream->on( 15 | read => sub ($stream, $chunk) { 16 | 17 | # Write chunk from client to server 18 | my $server = $buffer{$id}{connection}; 19 | return Mojo::IOLoop->stream($server)->write($chunk) if $server; 20 | 21 | # Read connect request from client 22 | my $buffer = $buffer{$id}{client} .= $chunk; 23 | if ($buffer =~ /\x0d?\x0a\x0d?\x0a$/) { 24 | $buffer{$id}{client} = ''; 25 | if ($buffer =~ /CONNECT (\S+):(\d+)?/) { 26 | my ($address, $port) = ($1, $2 || 80); 27 | 28 | # Connection to server 29 | $buffer{$id}{connection} = Mojo::IOLoop->client( 30 | {address => $address, port => $port} => sub { 31 | my ($loop, $err, $stream) = @_; 32 | 33 | # Connection to server failed 34 | if ($err) { 35 | say "Connection error for $address:$port: $err"; 36 | Mojo::IOLoop->remove($id); 37 | return delete $buffer{$id}; 38 | } 39 | 40 | # Start forwarding data in both directions 41 | say "Forwarding to $address:$port"; 42 | Mojo::IOLoop->stream($id)->write("HTTP/1.1 200 OK\x0d\x0a" . "Connection: keep-alive\x0d\x0a\x0d\x0a"); 43 | $stream->on(read => sub ($stream, $chunk) { Mojo::IOLoop->stream($id)->write($chunk) }); 44 | 45 | # Server closed connection 46 | $stream->on( 47 | close => sub { 48 | Mojo::IOLoop->remove($id); 49 | delete $buffer{$id}; 50 | } 51 | ); 52 | } 53 | ); 54 | } 55 | 56 | # Invalid request from client 57 | else { Mojo::IOLoop->remove($id) } 58 | } 59 | } 60 | ); 61 | 62 | # Client closed connection 63 | $stream->on( 64 | close => sub { 65 | my $buffer = delete $buffer{$id}; 66 | Mojo::IOLoop->remove($buffer->{connection}) if $buffer->{connection}; 67 | } 68 | ); 69 | } 70 | ); 71 | 72 | print "Starting CONNECT proxy on port 3000.\n"; 73 | Mojo::IOLoop->start; 74 | 75 | 1; 76 | -------------------------------------------------------------------------------- /examples/entities.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Extract named character references from HTML Living Standard 3 | # 4 | use Mojo::Base -strict; 5 | 6 | use Mojo::UserAgent; 7 | use Mojo::Util qw(trim); 8 | 9 | my $res = Mojo::UserAgent->new->get('https://html.spec.whatwg.org')->result; 10 | for my $row ($res->dom('#named-character-references-table tbody > tr')->each) { 11 | my $entity = trim $row->at('td > code')->text; 12 | my $codepoints = trim $row->children('td')->[1]->text; 13 | say "$entity $codepoints"; 14 | } 15 | 16 | 1; 17 | -------------------------------------------------------------------------------- /examples/fast.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Fast "Hello World" application for profiling the HTTP stack 3 | # 4 | use Mojo::Base 'Mojolicious'; 5 | 6 | sub handler { 7 | my $tx = pop; 8 | $tx->res->code(200)->body('Hello World!'); 9 | $tx->resume; 10 | } 11 | 12 | __PACKAGE__->new->start; 13 | -------------------------------------------------------------------------------- /examples/hello-template.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Minimal "Hello World" application with template for profiling 3 | # 4 | use Mojolicious::Lite; 5 | 6 | get '/hello'; 7 | 8 | app->start; 9 | __DATA__ 10 | 11 | @@ hello.html.ep 12 | Hello <%= 'World!' %> 13 | -------------------------------------------------------------------------------- /examples/hello.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Minimal "Hello World" application for profiling 3 | # 4 | use Mojolicious::Lite; 5 | 6 | get '/' => {data => 'Hello World!'}; 7 | 8 | app->start; 9 | -------------------------------------------------------------------------------- /examples/login/lib/LoginApp.pm: -------------------------------------------------------------------------------- 1 | package LoginApp; 2 | use Mojo::Base 'Mojolicious', -signatures; 3 | 4 | use LoginApp::Model::Users; 5 | 6 | sub startup ($self) { 7 | 8 | $self->secrets(['Mojolicious rocks']); 9 | $self->helper(users => sub { state $users = LoginApp::Model::Users->new }); 10 | 11 | my $r = $self->routes; 12 | $r->any('/')->to('login#index')->name('index'); 13 | 14 | my $logged_in = $r->under('/')->to('login#logged_in'); 15 | $logged_in->get('/protected')->to('login#protected'); 16 | 17 | $r->get('/logout')->to('login#logout'); 18 | } 19 | 20 | 1; 21 | -------------------------------------------------------------------------------- /examples/login/lib/LoginApp/Controller/Login.pm: -------------------------------------------------------------------------------- 1 | package LoginApp::Controller::Login; 2 | use Mojo::Base 'Mojolicious::Controller', -signatures; 3 | 4 | sub index ($self) { 5 | my $user = $self->param('user') || ''; 6 | my $pass = $self->param('pass') || ''; 7 | return $self->render unless $self->users->check($user, $pass); 8 | 9 | $self->session(user => $user); 10 | $self->flash(message => 'Thanks for logging in.'); 11 | $self->redirect_to('protected'); 12 | } 13 | 14 | sub logged_in ($self) { 15 | return 1 if $self->session('user'); 16 | $self->redirect_to('index'); 17 | return undef; 18 | } 19 | 20 | sub logout ($self) { 21 | $self->session(expires => 1); 22 | $self->redirect_to('index'); 23 | } 24 | 25 | 1; 26 | -------------------------------------------------------------------------------- /examples/login/lib/LoginApp/Model/Users.pm: -------------------------------------------------------------------------------- 1 | package LoginApp::Model::Users; 2 | 3 | use strict; 4 | use warnings; 5 | use experimental qw(signatures); 6 | 7 | use Mojo::Util qw(secure_compare); 8 | 9 | my $USERS = {joel => 'las3rs', marcus => 'lulz', sebastian => 'secr3t'}; 10 | 11 | sub new ($class) { bless {}, $class } 12 | 13 | sub check ($self, $user, $pass) { 14 | 15 | # Success 16 | return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass; 17 | 18 | # Fail 19 | return undef; 20 | } 21 | 22 | 1; 23 | -------------------------------------------------------------------------------- /examples/login/script/login_app: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Mojo::File qw(curfile); 7 | use lib curfile->dirname->sibling('lib')->to_string; 8 | use Mojolicious::Commands; 9 | 10 | # Start command line interface for application 11 | Mojolicious::Commands->start_app('LoginApp'); 12 | -------------------------------------------------------------------------------- /examples/login/t/login.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Test::Mojo; 5 | 6 | my $t = Test::Mojo->new('LoginApp'); 7 | $t->ua->max_redirects(1); 8 | 9 | subtest 'Test login workflow' => sub { 10 | $t->get_ok('/') 11 | ->status_is(200) 12 | ->element_exists('form input[name="user"]') 13 | ->element_exists('form input[name="pass"]') 14 | ->element_exists('form input[type="submit"]'); 15 | 16 | $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'}) 17 | ->status_is(200) 18 | ->text_like('html body' => qr/Welcome sebastian/); 19 | 20 | $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/); 21 | 22 | $t->get_ok('/logout') 23 | ->status_is(200) 24 | ->element_exists('form input[name="user"]') 25 | ->element_exists('form input[name="pass"]') 26 | ->element_exists('form input[type="submit"]'); 27 | }; 28 | 29 | done_testing(); 30 | -------------------------------------------------------------------------------- /examples/login/templates/layouts/default.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | Login Manager 4 | <%= content %> 5 | begin 3 | % if (param 'user') { 4 | Wrong name or password, please try again.
5 | % } 6 | Name:
7 | %= text_field 'user' 8 |
Password:
9 | %= password_field 'pass' 10 |
11 | %= submit_button 'Login' 12 | % end 13 | -------------------------------------------------------------------------------- /examples/login/templates/login/protected.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'default'; 2 | % if (my $msg = flash 'message') { 3 | <%= $msg %>
4 | % } 5 | Welcome <%= session 'user' %>.
6 | %= link_to Logout => 'logout' 7 | -------------------------------------------------------------------------------- /examples/microhttpd.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Minimal event loop example demonstrating how to cheat at HTTP benchmarks :) 3 | # 4 | use Mojo::Base -strict; 5 | use Mojo::IOLoop; 6 | 7 | my %buffer; 8 | Mojo::IOLoop->server( 9 | {port => 8080} => sub { 10 | my ($loop, $stream, $id) = @_; 11 | $stream->on( 12 | read => sub { 13 | my ($stream, $chunk) = @_; 14 | 15 | # Check if we got start-line and headers (no body support) 16 | $buffer{$id} .= $chunk; 17 | if (index($buffer{$id}, "\x0d\x0a\x0d\x0a") >= 0) { 18 | delete $buffer{$id}; 19 | 20 | # Write a minimal HTTP response 21 | # (the "Hello World!" message has been optimized away!) 22 | $stream->write("HTTP/1.1 200 OK\x0d\x0aContent-Length: 0\x0d\x0a\x0d\x0a"); 23 | } 24 | } 25 | ); 26 | } 27 | ); 28 | 29 | print <<'EOF'; 30 | Starting server on port 8080. 31 | For testing use something like "wrk -c 100 -d 10s http://127.0.0.1:8080/". 32 | EOF 33 | 34 | # Stop gracefully to make profiling easier 35 | Mojo::IOLoop->recurring(1 => sub { }); 36 | local $SIG{INT} = local $SIG{TERM} = sub { Mojo::IOLoop->stop }; 37 | Mojo::IOLoop->start; 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /examples/proxy.pl: -------------------------------------------------------------------------------- 1 | # 2 | # A simple HTTP proxy server for debugging 3 | # 4 | # $ HTTP_PROXY=http://127.0.0.1:3000 mojo get http://mojolicious.org 5 | # 6 | use Mojolicious::Lite -signatures; 7 | 8 | any '/*whatever' => {whatever => ''} => sub ($c) { 9 | my $req = $c->req; 10 | my $method = $req->method; 11 | my $url = $req->url->to_abs; 12 | my $headers = $req->headers->clone->dehop->to_hash; 13 | $c->app->log->debug(qq{Forwarding "$method $url"}); 14 | 15 | $c->proxy->start_p($c->ua->build_tx($method, $url, $headers))->catch(sub ($err) { 16 | $c->render(data => $err, status => 400); 17 | }); 18 | }; 19 | 20 | app->start; 21 | -------------------------------------------------------------------------------- /examples/responses.pl: -------------------------------------------------------------------------------- 1 | # 2 | # Application demonstrating the various HTTP response variants for debugging 3 | # 4 | use Mojolicious::Lite -signatures; 5 | 6 | get '/res1' => sub ($c) { 7 | $c->render(data => 'Hello World!'); 8 | }; 9 | 10 | get '/res2' => sub ($c) { 11 | $c->write('Hello '); 12 | $c->write('World!'); 13 | $c->write(''); 14 | }; 15 | 16 | get '/res3' => sub ($c) { 17 | $c->write_chunk('Hello '); 18 | $c->write_chunk('World!'); 19 | $c->write_chunk(''); 20 | }; 21 | 22 | get '/res4' => sub ($c) { 23 | $c->render(data => '', status => 204); 24 | }; 25 | 26 | get '/res5' => sub { 27 | die 'Hello World!'; 28 | }; 29 | 30 | app->start; 31 | -------------------------------------------------------------------------------- /lib/Mojo.pm: -------------------------------------------------------------------------------- 1 | package Mojo; 2 | use Mojo::Base -strict; 3 | 4 | # "Professor: These old Doomsday devices are dangerously unstable. I'll rest 5 | # easier not knowing where they are." 6 | 1; 7 | 8 | =encoding utf8 9 | 10 | =head1 NAME 11 | 12 | Mojo - Web development toolkit 13 | 14 | =head1 SYNOPSIS 15 | 16 | # HTTP/WebSocket user agent 17 | use Mojo::UserAgent; 18 | my $ua = Mojo::UserAgent->new; 19 | say $ua->get('www.mojolicious.org')->result->headers->server; 20 | 21 | # HTML/XML DOM parser with CSS selectors 22 | use Mojo::DOM; 23 | my $dom = Mojo::DOM->new('
Hello Mojo!
'); 24 | say $dom->at('div > b')->text; 25 | 26 | # Perl-ish templates 27 | use Mojo::Template; 28 | my $mt = Mojo::Template->new(vars => 1); 29 | say $mt->render('Hello <%= $what %>!', {what => 'Mojo'}); 30 | 31 | # HTTP/WebSocket server 32 | use Mojo::Server::Daemon; 33 | my $daemon = Mojo::Server::Daemon->new(listen => ['http://*:8080']); 34 | $daemon->unsubscribe('request')->on(request => sub ($daemon, $tx) { 35 | $tx->res->code(200); 36 | $tx->res->body('Hello Mojo!'); 37 | $tx->resume; 38 | }); 39 | $daemon->run; 40 | 41 | # Event loop 42 | use Mojo::IOLoop; 43 | for my $seconds (1 .. 5) { 44 | Mojo::IOLoop->timer($seconds => sub { say $seconds }); 45 | } 46 | Mojo::IOLoop->start; 47 | 48 | =head1 DESCRIPTION 49 | 50 | A powerful web development toolkit, with all the basic tools and helpers needed to write simple web applications and 51 | higher level web frameworks, such as L. Some of the most commonly used tools are L, 52 | L, L, L, L, L and L. 53 | 54 | See L for more! 55 | 56 | =head1 SEE ALSO 57 | 58 | L, L, L. 59 | 60 | =cut 61 | -------------------------------------------------------------------------------- /lib/Mojo/BaseUtil.pm: -------------------------------------------------------------------------------- 1 | package Mojo::BaseUtil; 2 | 3 | # Only using pure Perl as the only purpose of this module is to break a circular dependency involving Mojo::Base 4 | use strict; 5 | use warnings; 6 | use feature ':5.16'; 7 | 8 | use Exporter qw(import); 9 | use Sub::Util qw(set_subname); 10 | 11 | our @EXPORT_OK = (qw(class_to_path monkey_patch)); 12 | 13 | sub class_to_path { join '.', join('/', split(/::|'/, shift)), 'pm' } 14 | 15 | sub monkey_patch { 16 | my ($class, %patch) = @_; 17 | no strict 'refs'; 18 | no warnings 'redefine'; 19 | *{"${class}::$_"} = set_subname("${class}::$_", $patch{$_}) for keys %patch; 20 | } 21 | 22 | 1; 23 | 24 | =encoding utf8 25 | 26 | =head1 NAME 27 | 28 | Mojo::BaseUtil - Common utility functions used in Mojo::Base, re-exported in Mojo::Util 29 | 30 | =head1 SYNOPSIS 31 | 32 | use Mojo::BaseUtil qw(class_to_patch monkey_path); 33 | 34 | my $path = class_to_path 'Foo::Bar'; 35 | monkey_patch 'MyApp', foo => sub { say 'Foo!' }; 36 | 37 | =head1 DESCRIPTION 38 | 39 | L provides functions to both L and L, so that C does not have to 40 | load the rest of L, while preventing a circular dependency. 41 | 42 | =head1 SEE ALSO 43 | 44 | L, L, L. 45 | 46 | =cut 47 | -------------------------------------------------------------------------------- /lib/Mojo/Cache.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Cache; 2 | use Mojo::Base -base; 3 | 4 | has 'max_keys' => 100; 5 | 6 | sub get { (shift->{cache} // {})->{shift()} } 7 | 8 | sub set { 9 | my ($self, $key, $value) = @_; 10 | 11 | return $self unless (my $max = $self->max_keys) > 0; 12 | 13 | my $cache = $self->{cache} //= {}; 14 | my $queue = $self->{queue} //= []; 15 | delete $cache->{shift @$queue} while @$queue >= $max; 16 | push @$queue, $key unless exists $cache->{$key}; 17 | $cache->{$key} = $value; 18 | 19 | return $self; 20 | } 21 | 22 | 1; 23 | 24 | =encoding utf8 25 | 26 | =head1 NAME 27 | 28 | Mojo::Cache - Naive in-memory cache 29 | 30 | =head1 SYNOPSIS 31 | 32 | use Mojo::Cache; 33 | 34 | my $cache = Mojo::Cache->new(max_keys => 50); 35 | $cache->set(foo => 'bar'); 36 | my $foo = $cache->get('foo'); 37 | 38 | =head1 DESCRIPTION 39 | 40 | L is a naive in-memory cache with size limits. 41 | 42 | =head1 ATTRIBUTES 43 | 44 | L implements the following attributes. 45 | 46 | =head2 max_keys 47 | 48 | my $max = $cache->max_keys; 49 | $cache = $cache->max_keys(50); 50 | 51 | Maximum number of cache keys, defaults to C<100>. Setting the value to C<0> will disable caching. 52 | 53 | =head1 METHODS 54 | 55 | L inherits all methods from L and implements the following new ones. 56 | 57 | =head2 get 58 | 59 | my $value = $cache->get('foo'); 60 | 61 | Get cached value. 62 | 63 | =head2 set 64 | 65 | $cache = $cache->set(foo => 'bar'); 66 | 67 | Set cached value. 68 | 69 | =head1 SEE ALSO 70 | 71 | L, L, L. 72 | 73 | =cut 74 | -------------------------------------------------------------------------------- /lib/Mojo/Cookie.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Cookie; 2 | use Mojo::Base -base; 3 | use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; 4 | 5 | use Carp qw(croak); 6 | 7 | has [qw(name value)]; 8 | 9 | sub parse { croak 'Method "parse" not implemented by subclass' } 10 | sub to_string { croak 'Method "to_string" not implemented by subclass' } 11 | 12 | 1; 13 | 14 | =encoding utf8 15 | 16 | =head1 NAME 17 | 18 | Mojo::Cookie - HTTP cookie base class 19 | 20 | =head1 SYNOPSIS 21 | 22 | package Mojo::Cookie::MyCookie; 23 | use Mojo::Base 'Mojo::Cookie'; 24 | 25 | sub parse {...} 26 | sub to_string {...} 27 | 28 | =head1 DESCRIPTION 29 | 30 | L is an abstract base class for HTTP cookie containers, based on L, like L and L. 32 | 33 | =head1 ATTRIBUTES 34 | 35 | L implements the following attributes. 36 | 37 | =head2 name 38 | 39 | my $name = $cookie->name; 40 | $cookie = $cookie->name('foo'); 41 | 42 | Cookie name. 43 | 44 | =head2 value 45 | 46 | my $value = $cookie->value; 47 | $cookie = $cookie->value('/test'); 48 | 49 | Cookie value. 50 | 51 | =head1 METHODS 52 | 53 | L inherits all methods from L and implements the following new ones. 54 | 55 | =head2 parse 56 | 57 | my $cookies = $cookie->parse($str); 58 | 59 | Parse cookies. Meant to be overloaded in a subclass. 60 | 61 | =head2 to_string 62 | 63 | my $str = $cookie->to_string; 64 | 65 | Render cookie. Meant to be overloaded in a subclass. 66 | 67 | =head1 OPERATORS 68 | 69 | L overloads the following operators. 70 | 71 | =head2 bool 72 | 73 | my $bool = !!$cookie; 74 | 75 | Always true. 76 | 77 | =head2 stringify 78 | 79 | my $str = "$cookie"; 80 | 81 | Alias for L. 82 | 83 | =head1 SEE ALSO 84 | 85 | L, L, L. 86 | 87 | =cut 88 | -------------------------------------------------------------------------------- /lib/Mojo/Cookie/Request.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Cookie::Request; 2 | use Mojo::Base 'Mojo::Cookie'; 3 | 4 | use Mojo::Util qw(quote split_header); 5 | 6 | sub parse { 7 | my ($self, $str) = @_; 8 | 9 | my @cookies; 10 | my @pairs = map {@$_} @{split_header $str // ''}; 11 | while (my ($name, $value) = splice @pairs, 0, 2) { 12 | next if $name =~ /^\$/; 13 | push @cookies, $self->new(name => $name, value => $value // ''); 14 | } 15 | 16 | return \@cookies; 17 | } 18 | 19 | sub to_string { 20 | my $self = shift; 21 | return '' unless length(my $name = $self->name // ''); 22 | my $value = $self->value // ''; 23 | return join '=', $name, $value =~ /[,;" ]/ ? quote $value : $value; 24 | } 25 | 26 | 1; 27 | 28 | =encoding utf8 29 | 30 | =head1 NAME 31 | 32 | Mojo::Cookie::Request - HTTP request cookie 33 | 34 | =head1 SYNOPSIS 35 | 36 | use Mojo::Cookie::Request; 37 | 38 | my $cookie = Mojo::Cookie::Request->new; 39 | $cookie->name('foo'); 40 | $cookie->value('bar'); 41 | say "$cookie"; 42 | 43 | =head1 DESCRIPTION 44 | 45 | L is a container for HTTP request cookies, based on L. 47 | 48 | =head1 ATTRIBUTES 49 | 50 | L inherits all attributes from L. 51 | 52 | =head1 METHODS 53 | 54 | L inherits all methods from L and implements the following new ones. 55 | 56 | =head2 parse 57 | 58 | my $cookies = Mojo::Cookie::Request->parse('f=b; g=a'); 59 | 60 | Parse cookies. 61 | 62 | =head2 to_string 63 | 64 | my $str = $cookie->to_string; 65 | 66 | Render cookie. 67 | 68 | =head1 SEE ALSO 69 | 70 | L, L, L. 71 | 72 | =cut 73 | -------------------------------------------------------------------------------- /lib/Mojo/DynamicMethods.pm: -------------------------------------------------------------------------------- 1 | package Mojo::DynamicMethods; 2 | use Mojo::Base -strict; 3 | 4 | use Hash::Util::FieldHash qw(fieldhash); 5 | use Mojo::Util qw(monkey_patch); 6 | 7 | sub import { 8 | my ($flag, $caller) = ($_[1] // '', caller); 9 | return unless $flag eq '-dispatch'; 10 | 11 | my $dyn_pkg = "${caller}::_Dynamic"; 12 | my $caller_can = $caller->can('SUPER::can'); 13 | monkey_patch $dyn_pkg, 'can', sub { 14 | my ($self, $method, @rest) = @_; 15 | 16 | # Delegate to our parent's "can" if there is one, without breaking if not 17 | my $can = $self->$caller_can($method, @rest); 18 | return undef unless $can; 19 | no warnings 'once'; 20 | my $h = do { no strict 'refs'; *{"${dyn_pkg}::${method}"}{CODE} }; 21 | return $h && $h eq $can ? undef : $can; 22 | }; 23 | 24 | { 25 | no strict 'refs'; 26 | unshift @{"${caller}::ISA"}, $dyn_pkg; 27 | } 28 | } 29 | 30 | sub register { 31 | my ($target, $object, $name, $code) = @_; 32 | 33 | state %dyn_methods; 34 | state $setup = do { fieldhash %dyn_methods; 1 }; 35 | 36 | my $dyn_pkg = "${target}::_Dynamic"; 37 | monkey_patch($dyn_pkg, $name, $target->BUILD_DYNAMIC($name, \%dyn_methods)) 38 | unless do { no strict 'refs'; *{"${dyn_pkg}::${name}"}{CODE} }; 39 | $dyn_methods{$object}{$name} = $code; 40 | } 41 | 42 | "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn"; 43 | 44 | =encoding utf8 45 | 46 | =head1 NAME 47 | 48 | Mojo::DynamicMethods - Fast dynamic method dispatch 49 | 50 | =head1 SYNOPSIS 51 | 52 | package MyClass; 53 | use Mojo::Base -base, -signatures; 54 | 55 | use Mojo::DynamicMethods -dispatch; 56 | 57 | sub BUILD_DYNAMIC ($class, $method, $dyn_methods) { 58 | return sub {...}; 59 | } 60 | 61 | sub add_helper ($self, $name, $cb) { 62 | Mojo::DynamicMethods::register 'MyClass', $self, $name, $cb; 63 | } 64 | 65 | package main; 66 | 67 | # Generate methods dynamically (and hide them from "$obj->can(...)") 68 | my $obj = MyClass->new; 69 | $obj->add_helper(foo => sub { warn 'Hello Helper!' }); 70 | $obj->foo; 71 | 72 | =head1 DESCRIPTION 73 | 74 | L provides dynamic method dispatch for per-object helper methods without requiring use of 75 | C. 76 | 77 | To opt your class into dynamic dispatch simply pass the C<-dispatch> flag. 78 | 79 | use Mojo::DynamicMethods -dispatch; 80 | 81 | And then implement a C method in your class, making sure that the key you use to lookup methods in 82 | C<$dyn_methods> is the same thing you pass as C<$ref> to L. 83 | 84 | sub BUILD_DYNAMIC ($class, $method, $dyn_methods) { 85 | return sub ($self, @args) { 86 | my $dynamic = $dyn_methods->{$self}{$method}; 87 | return $self->$dynamic(@args) if $dynamic; 88 | my $package = ref $self; 89 | croak qq{Can't locate object method "$method" via package "$package"}; 90 | }; 91 | } 92 | 93 | Note that this module will summon B, use it at your own risk! 94 | 95 | =head1 FUNCTIONS 96 | 97 | L implements the following functions. 98 | 99 | =head2 register 100 | 101 | Mojo::DynamicMethods::register $class, $ref, $name, $cb; 102 | 103 | Registers the method C<$name> as eligible for dynamic dispatch for C<$class>, and sets C<$cb> to be looked up for 104 | C<$name> by reference C<$ref> in a dynamic method constructed by C. 105 | 106 | =head1 SEE ALSO 107 | 108 | L, L, L. 109 | 110 | =cut 111 | -------------------------------------------------------------------------------- /lib/Mojo/HelloWorld.pm: -------------------------------------------------------------------------------- 1 | package Mojo::HelloWorld; 2 | use Mojo::Base 'Mojolicious'; 3 | 4 | sub startup { 5 | my $self = shift; 6 | $self->preload_namespaces([])->log->level('error')->path(undef); 7 | $self->routes->any('/*whatever' => {whatever => '', text => 'Your Mojo is working!'}); 8 | } 9 | 10 | 1; 11 | 12 | =encoding utf8 13 | 14 | =head1 NAME 15 | 16 | Mojo::HelloWorld - Hello World! 17 | 18 | =head1 SYNOPSIS 19 | 20 | use Mojo::HelloWorld; 21 | 22 | my $hello = Mojo::HelloWorld->new; 23 | $hello->start; 24 | 25 | =head1 DESCRIPTION 26 | 27 | L is the default L application, used mostly for testing. 28 | 29 | =head1 ATTRIBUTES 30 | 31 | L inherits all attributes from L. 32 | 33 | =head1 METHODS 34 | 35 | L inherits all methods from L and implements the following new ones. 36 | 37 | =head2 startup 38 | 39 | $hello->startup; 40 | 41 | Creates a catch-all route that renders a text message. 42 | 43 | =head1 SEE ALSO 44 | 45 | L, L, L. 46 | 47 | =cut 48 | -------------------------------------------------------------------------------- /lib/Mojo/Home.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Home; 2 | use Mojo::Base 'Mojo::File'; 3 | 4 | use Mojo::Util qw(class_to_path); 5 | 6 | sub detect { 7 | my ($self, $class) = @_; 8 | 9 | # Environment variable 10 | my $home; 11 | if ($ENV{MOJO_HOME}) { $home = Mojo::File->new($ENV{MOJO_HOME})->to_array } 12 | 13 | # Location of the application class (Windows mixes backslash and slash) 14 | elsif ($class && (my $path = $INC{my $file = class_to_path $class})) { 15 | $home = Mojo::File->new($path)->to_array; 16 | splice @$home, (my @dummy = split(/\//, $file)) * -1; 17 | @$home && $home->[-1] eq $_ && pop @$home for qw(lib blib); 18 | } 19 | 20 | $$self = Mojo::File->new(@$home)->to_abs->to_string if $home; 21 | return $self; 22 | } 23 | 24 | sub rel_file { shift->child(split(/\//, shift)) } 25 | 26 | 1; 27 | 28 | =encoding utf8 29 | 30 | =head1 NAME 31 | 32 | Mojo::Home - Home sweet home 33 | 34 | =head1 SYNOPSIS 35 | 36 | use Mojo::Home; 37 | 38 | # Find and manage the project root directory 39 | my $home = Mojo::Home->new; 40 | $home->detect; 41 | say $home->child('templates', 'layouts', 'default.html.ep'); 42 | say "$home"; 43 | 44 | =head1 DESCRIPTION 45 | 46 | L is a container for home directories based on L. 47 | 48 | =head1 METHODS 49 | 50 | L inherits all methods from L and implements the following new ones. 51 | 52 | =head2 detect 53 | 54 | $home = $home->detect; 55 | $home = $home->detect('My::App'); 56 | 57 | Detect home directory from the value of the C environment variable or the location of the application class. 58 | 59 | =head2 rel_file 60 | 61 | my $path = $home->rel_file('foo/bar.html'); 62 | 63 | Return a new L object relative to the home directory. 64 | 65 | =head1 OPERATORS 66 | 67 | L inherits all overloaded operators from L. 68 | 69 | =head1 SEE ALSO 70 | 71 | L, L, L. 72 | 73 | =cut 74 | -------------------------------------------------------------------------------- /lib/Mojo/IOLoop/resources/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEpDCCAowCCQD2f63fTFHflTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTkwNjI4MjExNDI5WhcNMzkwNjIzMjExNDI5WjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2 5 | lW4DOBswU1YJkekNF6c4b1VVcpOvtqsLHhTxUz538bffcvhI2vv+aCltG6g5mlvJ 6 | wo5NEu9l0ZG5TD9Ca4+WOOisVWrAI/i2YxXFQLOdjhKRBB1BvrOxSaFOuCXz9+cj 7 | VRo0R8Dq3k+1aSy93Yf+fq9pL7LFJaUOlxcU2FOM+HW9FYPeVbqCzYqpPJoaBnwN 8 | tQkQg7i8ufbeMS0bCcFpfTSV4pCgpWg1L9z6cVmBHtxc4MQv7rTTal+BF/iZDfDk 9 | qTNFJpuK7IGtSVB5laTcssYKGuY5QhN5BBPoGEMP3f0KiZmgMOUqwR6fMUiidacG 10 | iSIcgy05uOJyZ4oroqOzesz8nm2jH1eRPys2WLLFd801GKOZZE2LvNhCVzNIE0s1 11 | Rr8yyWBU9jbjQuxlTAtyMUKKOqG9qsfEnKOsl9T9/pFcpJjad3spwhQSWhWEPWca 12 | avw0CGVaGQ3nYmr9aJ9vpGBIiIsLQOPTzpOOPCDauMFpAPOoKnvIu+iz3Z8sUrMu 13 | Ld+aT/3yxpAtNkmVv5A951XyFt9WDXF7jZOMHdOSZPvvI/Yn7joJUzfP9d7TLKjz 14 | Xu+dzQnrAN3xuAXuy+jBpMIl3OPzwER6a8v7gUKRA/achNlIeOOmBdNn1cyHddcn 15 | k6wiaXHJlFsl8X6IjCs9ILwv6H+ZGq/5QNU1Nrv5kQIDAQABMA0GCSqGSIb3DQEB 16 | CwUAA4ICAQCo3xjINrsIQYvpVwVLpcO1p+oE5NV0ipA30JT+89Dn+vCejel9NzxT 17 | msuD9aQSiNaB4znlIDqux4bSKkcRXDGINiaGNIDNXOtO/787LXjUZlljPVVHoPWi 18 | hxgwc0nUHz3l/YvoXMKHI8blPkIhXl7xgKSuKQu05evjd//kpdHs1h+7b2vtCB0/ 19 | VoYTX/NrIX5oMYCvHkZEypQbDJ3VeAkOhRJ4efGEuEskPRm0+eDSL7elas/65saZ 20 | l8vgkKDHZ9K0pd8JXc7EKmg3OBS22C5Lfhhy8AgqMa6R9p54oE4rH4yFaTe3BzFL 21 | xSA6HWqC987L2OCFr2LJ8hZpawDF1otukGHDou/5+4Q03EZz10RuZfzlCLO5DXzW 22 | Q28AtcCxz40n9o1giWzEj4LSYW4qsrpr5cNIhCqMzGPwp2OyS/TazPNJGoT8WKFU 23 | Kr+Y/prfkXAwgVkXlUSiu7ukiYslSM4BbYWHDxd75Iv4GzzhUirSuJKN95RglxR8 24 | XsJFlQwZ/tLvpflqb1Z8gPIV/4avtF+ybdx1AvqYViBQDf6GmLkM3p6Nwfj1LnCn 25 | kFhnqY80gyVjbZXvp9ClypExzgz55/o2ZIijznCaDkFSVBdv+aUIzl98IicZxHqP 26 | WREB+GMKmkaYrfKqlliQKdkXd2mXP/N8rv7SJEzHHpGRKBXsIAalrA== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /lib/Mojo/IOLoop/resources/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC2lW4DOBswU1YJ 3 | kekNF6c4b1VVcpOvtqsLHhTxUz538bffcvhI2vv+aCltG6g5mlvJwo5NEu9l0ZG5 4 | TD9Ca4+WOOisVWrAI/i2YxXFQLOdjhKRBB1BvrOxSaFOuCXz9+cjVRo0R8Dq3k+1 5 | aSy93Yf+fq9pL7LFJaUOlxcU2FOM+HW9FYPeVbqCzYqpPJoaBnwNtQkQg7i8ufbe 6 | MS0bCcFpfTSV4pCgpWg1L9z6cVmBHtxc4MQv7rTTal+BF/iZDfDkqTNFJpuK7IGt 7 | SVB5laTcssYKGuY5QhN5BBPoGEMP3f0KiZmgMOUqwR6fMUiidacGiSIcgy05uOJy 8 | Z4oroqOzesz8nm2jH1eRPys2WLLFd801GKOZZE2LvNhCVzNIE0s1Rr8yyWBU9jbj 9 | QuxlTAtyMUKKOqG9qsfEnKOsl9T9/pFcpJjad3spwhQSWhWEPWcaavw0CGVaGQ3n 10 | Ymr9aJ9vpGBIiIsLQOPTzpOOPCDauMFpAPOoKnvIu+iz3Z8sUrMuLd+aT/3yxpAt 11 | NkmVv5A951XyFt9WDXF7jZOMHdOSZPvvI/Yn7joJUzfP9d7TLKjzXu+dzQnrAN3x 12 | uAXuy+jBpMIl3OPzwER6a8v7gUKRA/achNlIeOOmBdNn1cyHddcnk6wiaXHJlFsl 13 | 8X6IjCs9ILwv6H+ZGq/5QNU1Nrv5kQIDAQABAoICAAINoiQVIHElrsUCyA0mo/HF 14 | hr8kP7btJfVFDFU+a2hr5nZz04j2NXlB8J1Sf0zOiJO3RWRmfxy1A5+C1P9JOF8n 15 | Gq69cyrf/K8IZDlIpfxymZDZ6/5OR7UJr++zsHGS6x2Bmn7WA7xgbaMLoL4t3Jan 16 | FA/pwmfnKXkFh/PrDt15+dD7ifUZH7TS3OlUTiNWyVRaIdT2tkAhEz6ibPBt5qfq 17 | CYpZ9uhnk8ltVV3XonsKPs4olOw5Ef2Cp7pK67fE6V2Y7YOskHk6eabaOTZ00VrO 18 | A94fOVGRhaiJvDOS+kYWZ/8TVw/vHNSjQVXm9vskuZEgP6r0arDIfHtu4KXm+VJJ 19 | f6v8VLHdP7EU9ce2COc77iWMpUZrLBGRo0K1aZAVknzIKrt5aiRcG5e/PzPtxh6h 20 | eTMHlMak9XLnENDRsbJEMedxLb2VOmqiJOikOPy9U33nt403oi2h2eOZ6+wh+IMK 21 | d8EJH7cxbeiq/Aelp3IvwOagCiFpOatYL29zhUC/fufR8/y82Xz1TWlJ/mwZbPqo 22 | 6R/LPrEBafAilBApzpRvcxs+zofe2FhnSRbk+Hozu5XfmECdivoavr2SZhtDLfrK 23 | LaHTUPxVbK4BOSTqoXsUtnUSpiP5F1IYzu59cm4S85KBB95KJuAGAaykeuWRjGXX 24 | 7kQ4T6vWn9JAdj3QZqVBAoIBAQDt/q3VvuinB2xjJZae2B0XYBXKgGl1svLPjP3w 25 | tfQmi+tefjZ+GY8V4L05GraBMi/qcaQmy4wipVdVu7isXF3GancMsCu549ZZSAJO 26 | DOv+u6oq0kd4mkiQ1/LUUoTNwwjKpcH6fEsXJHXKdnhUGE15hm+YGh3YrDo6xmpC 27 | HoXk9qefDy7xL4mTJAfdr/KGIc1BpXic3VF+S0ewHom1L+dhkdRpew0oeeVTZ10O 28 | 9NQP4SqI2jIiNTLDSZ37FFJXD3dIxJ1niX3hRlSAKAIRvhzcs9581ea30F2BenhT 29 | EuSM89kXJPub/dVG/WWuC5VQBCHmvVtGUWv8u0lacc3Ge4PZAoIBAQDEZZX9l2NN 30 | viPwN2joiJa4LLH1+HC7X6MaKXQZ+jPr2ptO5F3ZekJ9W2jJOoXQCso7wnuEGYB5 31 | KnbS/NWF3V9NSAWFb4nukXgIvTNudrgXr4bBkXVa26YwfxcCRv9qWtWp3W76K9F6 32 | /jRe4MYf7NGbP7SndViGO7u2AhwejsxgqET1AM8eHrdtpkvC/aSqpEAOUWbwSXxc 33 | G5dgVzoH0RZV5YVldPbdS7DOUZoh1co92lTB5LfPGOxwsb364nH61+lkhxWAiMe0 34 | Q3hG8WLDF3wTRkpTUKAyjuBEE7Ve+bdFaC9cyhRiwgxPjie4qtt100IEHgpF0mw7 35 | mWBB6x+pDuh5AoIBAQCs/eMzrAoGZxH023ypR2OV+yS7xi1h/UobbVukXU3zut7C 36 | F7HaZQ+pkmtYl78zF9zWZ/YusOPSxyY9Ti9FMfqD4B1a3q9Z9m93BC2QuDnONnDR 37 | oXmMA3Fdv2plxPl9axf33Rar0S7vynPIT+bVEbk27W4uPEWXmlDVKiZQm0kuDc/3 38 | gRzY+Xnht130WRFLSESfQ/zw4Lp8t5GLRhdI2WIxfMPOTEBbPIdh4Y818OY4CK5X 39 | PWsVjF+yrc8kkzfqynYlMa1MdhdG6U1AvlQKu4rVLfU5/m0vDUj6daACmogAoLsa 40 | 5KnzUEV3zXbcVNUajXZq9xbifQqmcSg3kuNFM8C5AoIBAHRKirPsLlrcWb9lr/Lw 41 | 3f4USRQSlf39NUDKhvrS0me3u/rM8l1SLYi41aVBx/ZWTUVxdV3VE+OrJ0zrdSuc 42 | 10+Vc999GjlvXZofHhMsrPkpcCuyC8FPCmrw9hjdHWRGgPniKlJsG9AuMah0hBxn 43 | R/4bjMcTjuV8/TtaqHfXqmEZgito3TtCiO6eZ4IAWr7IHz3bKY7ilIadt9bOD4iN 44 | YCJgk8ptpbeHmBuy6gda5jQV0dY1rjks0uQv+wRRjZgwvPxPmIXReB7fTJsFV6uZ 45 | fliTaHNI7HLDczwcR2sDhmfMty7EYanQqSV6UT7hvK1Z+F8jwoVxgbEQspSVutuJ 46 | /lECggEAVdvU6sPQH2QNnN8mxYF5zqST8Fzsi9+O6iQe/aymDKZoHa8/9O/BOx39 47 | JSasQCnOt1yhRZwo50WhSUquJ1R0KUiybDyv1jvff7R+i3pl98Czjfc3iZuEDHGI 48 | anD3qC9DrbsqotIsnjpxULJ3Hotohhy5NQtoQLsucNzZRWquQGE0sUGes6IIeEJR 49 | 1NWA6VnGdVrlltm3fkkJMwdn1pbEWXXI3VutEIn9NJfvuVDzPb1f5ih1ChLm5ijf 50 | nK13sEavqpo7L8cpeaPeNLY2Tt4mVXw6Ujq1fLM/7VOvmNTQMu3lVXQve32w+gm0 51 | 0N/URKPaZ8Z9V/c15kNhIZBgJhOoVg== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /lib/Mojo/JSON/Pointer.pm: -------------------------------------------------------------------------------- 1 | package Mojo::JSON::Pointer; 2 | use Mojo::Base -base; 3 | 4 | has 'data'; 5 | 6 | sub contains { shift->_pointer(0, @_) } 7 | sub get { shift->_pointer(1, @_) } 8 | 9 | sub new { @_ > 1 ? shift->SUPER::new(data => shift) : shift->SUPER::new } 10 | 11 | sub _pointer { 12 | my ($self, $get, $pointer) = @_; 13 | 14 | my $data = $self->data; 15 | return length $pointer ? undef : $get ? $data : 1 unless $pointer =~ s!^/!!; 16 | for my $p (length $pointer ? (split /\//, $pointer, -1) : ($pointer)) { 17 | $p =~ s!~1!/!g; 18 | $p =~ s/~0/~/g; 19 | 20 | # Hash 21 | if (ref $data eq 'HASH' && exists $data->{$p}) { $data = $data->{$p} } 22 | 23 | # Array 24 | elsif (ref $data eq 'ARRAY' && $p =~ /^\d+$/ && @$data > $p) { $data = $data->[$p] } 25 | 26 | # Nothing 27 | else { return undef } 28 | } 29 | 30 | return $get ? $data : 1; 31 | } 32 | 33 | 1; 34 | 35 | =encoding utf8 36 | 37 | =head1 NAME 38 | 39 | Mojo::JSON::Pointer - JSON Pointers 40 | 41 | =head1 SYNOPSIS 42 | 43 | use Mojo::JSON::Pointer; 44 | 45 | my $pointer = Mojo::JSON::Pointer->new({foo => [23, 'bar']}); 46 | say $pointer->get('/foo/1'); 47 | say 'Contains "/foo".' if $pointer->contains('/foo'); 48 | 49 | =head1 DESCRIPTION 50 | 51 | L is an implementation of L. 52 | 53 | =head1 ATTRIBUTES 54 | 55 | L implements the following attributes. 56 | 57 | =head2 data 58 | 59 | my $data = $pointer->data; 60 | $pointer = $pointer->data({foo => 'bar'}); 61 | 62 | Data structure to be processed. 63 | 64 | =head1 METHODS 65 | 66 | L inherits all methods from L and implements the following new ones. 67 | 68 | =head2 contains 69 | 70 | my $bool = $pointer->contains('/foo/1'); 71 | 72 | Check if L contains a value that can be identified with the given JSON Pointer. 73 | 74 | # True 75 | Mojo::JSON::Pointer->new('just a string')->contains(''); 76 | Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/♥'); 77 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/foo'); 78 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/1'); 79 | 80 | # False 81 | Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/☃'); 82 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/bar'); 83 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/9'); 84 | 85 | =head2 get 86 | 87 | my $value = $pointer->get('/foo/bar'); 88 | 89 | Extract value from L identified by the given JSON Pointer. 90 | 91 | # "just a string" 92 | Mojo::JSON::Pointer->new('just a string')->get(''); 93 | 94 | # "mojolicious" 95 | Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->get('/♥'); 96 | 97 | # "bar" 98 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/foo'); 99 | 100 | # "4" 101 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/0'); 102 | 103 | # "6" 104 | Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/2'); 105 | 106 | =head2 new 107 | 108 | my $pointer = Mojo::JSON::Pointer->new; 109 | my $pointer = Mojo::JSON::Pointer->new({foo => 'bar'}); 110 | 111 | Build new L object. 112 | 113 | =head1 SEE ALSO 114 | 115 | L, L, L. 116 | 117 | =cut 118 | -------------------------------------------------------------------------------- /lib/Mojo/Server/CGI.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Server::CGI; 2 | use Mojo::Base 'Mojo::Server'; 3 | 4 | has 'nph'; 5 | 6 | sub run { 7 | my $self = shift; 8 | 9 | $self->app->server($self); 10 | my $tx = $self->build_tx; 11 | my $req = $tx->req->parse(\%ENV); 12 | $tx->local_port($ENV{SERVER_PORT})->remote_address($ENV{REMOTE_ADDR}); 13 | 14 | # Request body (may block if we try to read too much) 15 | binmode STDIN; 16 | my $len = $req->headers->content_length; 17 | until ($req->is_finished) { 18 | my $chunk = ($len && $len < 131072) ? $len : 131072; 19 | last unless my $read = STDIN->read(my $buffer, $chunk, 0); 20 | $req->parse($buffer); 21 | last if ($len -= $read) <= 0; 22 | } 23 | 24 | $self->emit(request => $tx); 25 | 26 | # Response start-line 27 | STDOUT->autoflush(1); 28 | binmode STDOUT; 29 | my $res = $tx->res->fix_headers; 30 | return undef if $self->nph && !_write($res, 'get_start_line_chunk'); 31 | 32 | # Response headers 33 | my $code = $res->code || 404; 34 | my $msg = $res->message || $res->default_message; 35 | $res->headers->status("$code $msg") unless $self->nph; 36 | return undef unless _write($res, 'get_header_chunk'); 37 | 38 | # Response body 39 | return undef unless $tx->is_empty || _write($res, 'get_body_chunk'); 40 | 41 | # Finish transaction 42 | $tx->closed; 43 | 44 | return $res->code; 45 | } 46 | 47 | sub _write { 48 | my ($res, $method) = @_; 49 | 50 | my $offset = 0; 51 | while (1) { 52 | 53 | # No chunk yet, try again 54 | sleep 1 and next unless defined(my $chunk = $res->$method($offset)); 55 | 56 | # End of part 57 | last unless my $len = length $chunk; 58 | 59 | # Make sure we can still write 60 | $offset += $len; 61 | return undef unless STDOUT->opened; 62 | print STDOUT $chunk; 63 | } 64 | 65 | return 1; 66 | } 67 | 68 | 1; 69 | 70 | =encoding utf8 71 | 72 | =head1 NAME 73 | 74 | Mojo::Server::CGI - CGI server 75 | 76 | =head1 SYNOPSIS 77 | 78 | use Mojo::Server::CGI; 79 | 80 | my $cgi = Mojo::Server::CGI->new; 81 | $cgi->unsubscribe('request')->on(request => sub ($cgi, $tx) { 82 | 83 | # Request 84 | my $method = $tx->req->method; 85 | my $path = $tx->req->url->path; 86 | 87 | # Response 88 | $tx->res->code(200); 89 | $tx->res->headers->content_type('text/plain'); 90 | $tx->res->body("$method request for $path!"); 91 | 92 | # Resume transaction 93 | $tx->resume; 94 | }); 95 | $cgi->run; 96 | 97 | =head1 DESCRIPTION 98 | 99 | L is a simple and portable implementation of L. 100 | 101 | See L for more. 102 | 103 | =head1 EVENTS 104 | 105 | L inherits all events from L. 106 | 107 | =head1 ATTRIBUTES 108 | 109 | L inherits all attributes from L and implements the following new ones. 110 | 111 | =head2 nph 112 | 113 | my $bool = $cgi->nph; 114 | $cgi = $cgi->nph($bool); 115 | 116 | Activate non-parsed header mode. 117 | 118 | =head1 METHODS 119 | 120 | L inherits all methods from L and implements the following new ones. 121 | 122 | =head2 run 123 | 124 | my $status = $cgi->run; 125 | 126 | Run CGI. 127 | 128 | =head1 SEE ALSO 129 | 130 | L, L, L. 131 | 132 | =cut 133 | -------------------------------------------------------------------------------- /lib/Mojo/Server/Morbo/Backend.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Server::Morbo::Backend; 2 | use Mojo::Base -base; 3 | 4 | use Carp qw(croak); 5 | 6 | has watch => sub { [qw(lib templates)] }; 7 | has watch_timeout => sub { $ENV{MOJO_MORBO_TIMEOUT} || 1 }; 8 | 9 | sub modified_files { croak 'Method "modified_files" not implemented by subclass' } 10 | 11 | 1; 12 | 13 | =encoding utf8 14 | 15 | =head1 NAME 16 | 17 | Mojo::Server::Morbo::Backend - Morbo backend base class 18 | 19 | =head1 SYNOPSIS 20 | 21 | package Mojo::Server::Morbo::Backend::Inotify: 22 | use Mojo::Base 'Mojo::Server::Morbo::Backend'; 23 | 24 | sub modified_files {...} 25 | 26 | =head1 DESCRIPTION 27 | 28 | L is an abstract base class for Morbo backends, like 29 | L. 30 | 31 | =head1 ATTRIBUTES 32 | 33 | L implements the following attributes. 34 | 35 | =head2 watch 36 | 37 | my $watch = $backend->watch; 38 | $backend = $backend->watch(['/home/sri/my_app']); 39 | 40 | Files and directories to watch for changes, defaults to the application script as well as the C and C 41 | directories in the current working directory. 42 | 43 | =head2 watch_timeout 44 | 45 | my $timeout = $backend->watch_timeout; 46 | $backend = $backend->watch_timeout(10); 47 | 48 | Maximum amount of time in seconds a backend may block when waiting for files to change, defaults to the value of the 49 | C environment variable or C<1>. 50 | 51 | =head1 METHODS 52 | 53 | L inherits all methods from L and implements the following new ones. 54 | 55 | =head2 modified_files 56 | 57 | my $files = $backend->modified_files; 58 | 59 | Check if files from L have been modified since the last check and return an array reference with the results. 60 | Meant to be overloaded in a subclass. 61 | 62 | # All files that have been modified 63 | say for @{$backend->modified_files}; 64 | 65 | =head1 SEE ALSO 66 | 67 | L, L, L. 68 | 69 | =cut 70 | -------------------------------------------------------------------------------- /lib/Mojo/Server/Morbo/Backend/Poll.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Server::Morbo::Backend::Poll; 2 | use Mojo::Base 'Mojo::Server::Morbo::Backend'; 3 | 4 | use Mojo::File qw(path); 5 | 6 | sub modified_files { 7 | my $self = shift; 8 | 9 | my $cache = $self->{cache} //= {}; 10 | my $known_files = {map { $_ => 1 } keys %$cache}; 11 | my @files; 12 | for my $file (map { -f $_ && -r _ ? $_ : _list($_) } @{$self->watch}) { 13 | delete $known_files->{$file}; 14 | my ($size, $mtime) = (stat $file)[7, 9]; 15 | next unless defined $size and defined $mtime; 16 | my $stats = $cache->{$file} ||= [$^T, $size]; 17 | next if $mtime <= $stats->[0] && $size == $stats->[1]; 18 | @$stats = ($mtime, $size); 19 | push @files, $file; 20 | } 21 | 22 | for my $file (keys %$known_files) { 23 | 24 | # Any leftover file means they were deleted 25 | push @files, $file; 26 | delete $cache->{$file}; 27 | } 28 | 29 | sleep $self->watch_timeout unless @files; 30 | 31 | return \@files; 32 | } 33 | 34 | sub _list { path(shift)->list_tree->map('to_string')->each } 35 | 36 | 1; 37 | 38 | =encoding utf8 39 | 40 | =head1 NAME 41 | 42 | Mojo::Server::Morbo::Backend::Poll - Morbo default backend 43 | 44 | =head1 SYNOPSIS 45 | 46 | use Mojo::Server::Morbo::Backend::Poll; 47 | 48 | my $backend = Mojo::Server::Morbo::Backend::Poll->new; 49 | if (my $files = $backend->modified_files) { 50 | ... 51 | } 52 | 53 | =head1 DESCRIPTION 54 | 55 | L is the default backend for L. 56 | 57 | =head1 ATTRIBUTES 58 | 59 | L inherits all attributes from L. 60 | 61 | =head1 METHODS 62 | 63 | L inherits all methods from L and implements the 64 | following new ones. 65 | 66 | =head2 modified_files 67 | 68 | my $files = $backend->modified_files; 69 | 70 | Check file size and mtime to determine which files have changed, this is not particularly efficient, but very portable. 71 | 72 | # All files that have been modified 73 | say for @{$backend->modified_files}; 74 | 75 | =head1 SEE ALSO 76 | 77 | L, L, L. 78 | 79 | =cut 80 | -------------------------------------------------------------------------------- /lib/Mojo/Server/PSGI.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Server::PSGI; 2 | use Mojo::Base 'Mojo::Server'; 3 | 4 | sub run { 5 | my ($self, $env) = @_; 6 | 7 | my $tx = $self->build_tx; 8 | my $req = $tx->req->parse($env); 9 | $tx->local_port($env->{SERVER_PORT})->remote_address($env->{REMOTE_ADDR}); 10 | 11 | # Request body (may block if we try to read too much) 12 | my $len = $env->{CONTENT_LENGTH}; 13 | until ($req->is_finished) { 14 | my $chunk = ($len && $len < 131072) ? $len : 131072; 15 | last unless my $read = $env->{'psgi.input'}->read(my $buffer, $chunk, 0); 16 | $req->parse($buffer); 17 | last if ($len -= $read) <= 0; 18 | } 19 | 20 | $self->emit(request => $tx); 21 | 22 | # Response headers 23 | my $res = $tx->res->fix_headers; 24 | my $hash = $res->headers->to_hash(1); 25 | my @headers; 26 | for my $name (keys %$hash) { push @headers, $name, $_ for @{$hash->{$name}} } 27 | 28 | # PSGI response 29 | my $io = Mojo::Server::PSGI::_IO->new(tx => $tx, empty => $tx->is_empty); 30 | return [$res->code // 404, \@headers, $io]; 31 | } 32 | 33 | sub to_psgi_app { 34 | my $self = shift; 35 | 36 | # Preload application and wrap it 37 | $self->app->server($self); 38 | return sub { $self->run(@_) } 39 | } 40 | 41 | package Mojo::Server::PSGI::_IO; 42 | use Mojo::Base -base; 43 | 44 | # Finish transaction 45 | sub close { shift->{tx}->closed } 46 | 47 | sub getline { 48 | my $self = shift; 49 | 50 | # Empty 51 | return undef if $self->{empty}; 52 | 53 | # No content yet, try again later 54 | my $chunk = $self->{tx}->res->get_body_chunk($self->{offset} //= 0); 55 | return '' unless defined $chunk; 56 | 57 | # End of content 58 | return undef unless length $chunk; 59 | 60 | $self->{offset} += length $chunk; 61 | return $chunk; 62 | } 63 | 64 | 1; 65 | 66 | =encoding utf8 67 | 68 | =head1 NAME 69 | 70 | Mojo::Server::PSGI - PSGI server 71 | 72 | =head1 SYNOPSIS 73 | 74 | use Mojo::Server::PSGI; 75 | 76 | my $psgi = Mojo::Server::PSGI->new; 77 | $psgi->unsubscribe('request')->on(request => sub ($psgi, $tx) { 78 | 79 | # Request 80 | my $method = $tx->req->method; 81 | my $path = $tx->req->url->path; 82 | 83 | # Response 84 | $tx->res->code(200); 85 | $tx->res->headers->content_type('text/plain'); 86 | $tx->res->body("$method request for $path!"); 87 | 88 | # Resume transaction 89 | $tx->resume; 90 | }); 91 | my $app = $psgi->to_psgi_app; 92 | 93 | =head1 DESCRIPTION 94 | 95 | L allows L applications to run on all L compatible servers. 96 | 97 | See L for more. 98 | 99 | =head1 EVENTS 100 | 101 | L inherits all events from L. 102 | 103 | =head1 ATTRIBUTES 104 | 105 | L inherits all attributes from L. 106 | 107 | =head1 METHODS 108 | 109 | L inherits all methods from L and implements the following new ones. 110 | 111 | =head2 run 112 | 113 | my $res = $psgi->run($env); 114 | 115 | Run L. 116 | 117 | =head2 to_psgi_app 118 | 119 | my $app = $psgi->to_psgi_app; 120 | 121 | Turn L application into L application. 122 | 123 | =head1 SEE ALSO 124 | 125 | L, L, L. 126 | 127 | =cut 128 | -------------------------------------------------------------------------------- /lib/Mojo/Upload.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Upload; 2 | use Mojo::Base -base; 3 | 4 | has [qw(asset filename headers name)]; 5 | 6 | sub move_to { $_[0]->asset->move_to($_[1]) and return $_[0] } 7 | 8 | sub size { shift->asset->size } 9 | sub slurp { shift->asset->slurp } 10 | 11 | 1; 12 | 13 | =encoding utf8 14 | 15 | =head1 NAME 16 | 17 | Mojo::Upload - Upload 18 | 19 | =head1 SYNOPSIS 20 | 21 | use Mojo::Upload; 22 | 23 | my $upload = Mojo::Upload->new; 24 | say $upload->filename; 25 | $upload->move_to('/home/sri/foo.txt'); 26 | 27 | =head1 DESCRIPTION 28 | 29 | L is a container for uploaded files. 30 | 31 | =head1 ATTRIBUTES 32 | 33 | L implements the following attributes. 34 | 35 | =head2 asset 36 | 37 | my $asset = $upload->asset; 38 | $upload = $upload->asset(Mojo::Asset::File->new); 39 | 40 | Asset containing the uploaded data, usually a L or L object. 41 | 42 | =head2 filename 43 | 44 | my $filename = $upload->filename; 45 | $upload = $upload->filename('foo.txt'); 46 | 47 | Name of the uploaded file. 48 | 49 | =head2 headers 50 | 51 | my $headers = $upload->headers; 52 | $upload = $upload->headers(Mojo::Headers->new); 53 | 54 | Headers for upload, usually a L object. 55 | 56 | =head2 name 57 | 58 | my $name = $upload->name; 59 | $upload = $upload->name('foo'); 60 | 61 | Name of the upload. 62 | 63 | =head1 METHODS 64 | 65 | L inherits all methods from L and implements the following new ones. 66 | 67 | =head2 move_to 68 | 69 | $upload = $upload->move_to('/home/sri/foo.txt'); 70 | 71 | Move uploaded data into a specific file. 72 | 73 | =head2 size 74 | 75 | my $size = $upload->size; 76 | 77 | Size of uploaded data in bytes. 78 | 79 | =head2 slurp 80 | 81 | my $bytes = $upload->slurp; 82 | 83 | Read all uploaded data at once. 84 | 85 | =head1 SEE ALSO 86 | 87 | L, L, L. 88 | 89 | =cut 90 | -------------------------------------------------------------------------------- /lib/Mojo/UserAgent/Proxy.pm: -------------------------------------------------------------------------------- 1 | package Mojo::UserAgent::Proxy; 2 | use Mojo::Base -base; 3 | 4 | use Mojo::URL; 5 | 6 | has [qw(http https not)]; 7 | 8 | sub detect { 9 | my $self = shift; 10 | $self->http($ENV{HTTP_PROXY} || $ENV{http_proxy}); 11 | $self->https($ENV{HTTPS_PROXY} || $ENV{https_proxy}); 12 | return $self->not([split /,/, $ENV{NO_PROXY} || $ENV{no_proxy} || '']); 13 | } 14 | 15 | sub is_needed { 16 | !grep { $_[1] =~ /\Q$_\E$/ } @{$_[0]->not // []}; 17 | } 18 | 19 | sub prepare { 20 | my ($self, $tx) = @_; 21 | 22 | $self->detect if $ENV{MOJO_PROXY}; 23 | my $req = $tx->req; 24 | my $url = $req->url; 25 | return unless $self->is_needed($url->host); 26 | 27 | # HTTP proxy 28 | my $proto = $url->protocol; 29 | my $http = $self->http; 30 | $req->proxy(Mojo::URL->new($http)) if $http && $proto eq 'http'; 31 | 32 | # HTTPS proxy 33 | my $https = $self->https; 34 | $req->proxy(Mojo::URL->new($https)) if $https && $proto eq 'https'; 35 | } 36 | 37 | 1; 38 | 39 | =encoding utf8 40 | 41 | =head1 NAME 42 | 43 | Mojo::UserAgent::Proxy - User agent proxy manager 44 | 45 | =head1 SYNOPSIS 46 | 47 | use Mojo::UserAgent::Proxy; 48 | 49 | my $proxy = Mojo::UserAgent::Proxy->new; 50 | $proxy->detect; 51 | say $proxy->http; 52 | 53 | =head1 DESCRIPTION 54 | 55 | L manages proxy servers for L. 56 | 57 | =head1 ATTRIBUTES 58 | 59 | L implements the following attributes. 60 | 61 | =head2 http 62 | 63 | my $http = $proxy->http; 64 | $proxy = $proxy->http('socks://sri:secret@127.0.0.1:8080'); 65 | 66 | Proxy server to use for HTTP and WebSocket requests. 67 | 68 | =head2 https 69 | 70 | my $https = $proxy->https; 71 | $proxy = $proxy->https('http://sri:secret@127.0.0.1:8080'); 72 | 73 | Proxy server to use for HTTPS and WebSocket requests. 74 | 75 | =head2 not 76 | 77 | my $not = $proxy->not; 78 | $proxy = $proxy->not(['localhost', 'intranet.mojolicious.org']); 79 | 80 | Domains that don't require a proxy server to be used. 81 | 82 | =head1 METHODS 83 | 84 | L inherits all methods from L and implements the following new ones. 85 | 86 | =head2 detect 87 | 88 | $proxy = $proxy->detect; 89 | 90 | Check environment variables C, C, C, C, C and C 91 | for proxy information. Automatic proxy detection can be enabled with the C environment variable. 92 | 93 | =head2 is_needed 94 | 95 | my $bool = $proxy->is_needed('intranet.example.com'); 96 | 97 | Check if request for domain would use a proxy server. 98 | 99 | =head2 prepare 100 | 101 | $proxy->prepare(Mojo::Transaction::HTTP->new); 102 | 103 | Prepare proxy server information for transaction. 104 | 105 | =head1 SEE ALSO 106 | 107 | L, L, L. 108 | 109 | =cut 110 | -------------------------------------------------------------------------------- /lib/Mojo/UserAgent/Server.pm: -------------------------------------------------------------------------------- 1 | package Mojo::UserAgent::Server; 2 | use Mojo::Base -base; 3 | 4 | use Mojo::IOLoop; 5 | use Mojo::Server::Daemon; 6 | use Scalar::Util qw(weaken); 7 | 8 | has ioloop => sub { Mojo::IOLoop->singleton }; 9 | 10 | sub app { 11 | my ($self, $app) = @_; 12 | 13 | # Singleton application 14 | state $singleton; 15 | return $singleton = $app ? $app : $singleton unless ref $self; 16 | 17 | # Default to singleton application 18 | return $self->{app} || $singleton unless $app; 19 | $self->{app} = $app; 20 | return $self; 21 | } 22 | 23 | sub nb_url { shift->_url(1, @_) } 24 | 25 | sub restart { delete @{$_[0]}{qw(nb_port nb_server port server)} } 26 | 27 | sub url { shift->_url(0, @_) } 28 | 29 | sub _url { 30 | my ($self, $nb, $proto) = @_; 31 | 32 | if (!$self->{server} || $proto) { 33 | $proto = $self->{proto} = $proto || 'http'; 34 | 35 | # Blocking 36 | my $server = $self->{server} = Mojo::Server::Daemon->new(ioloop => $self->ioloop, silent => 1); 37 | weaken $server->app($self->app)->{app}; 38 | my $port = $self->{port} ? ":$self->{port}" : ''; 39 | $self->{port} = $server->listen(["$proto://127.0.0.1$port"])->start->ports->[0]; 40 | 41 | # Non-blocking 42 | $server = $self->{nb_server} = Mojo::Server::Daemon->new(silent => 1); 43 | weaken $server->app($self->app)->{app}; 44 | $port = $self->{nb_port} ? ":$self->{nb_port}" : ''; 45 | $self->{nb_port} = $server->listen(["$proto://127.0.0.1$port"])->start->ports->[0]; 46 | } 47 | 48 | my $port = $nb ? $self->{nb_port} : $self->{port}; 49 | return Mojo::URL->new("$self->{proto}://127.0.0.1:$port/"); 50 | } 51 | 52 | 1; 53 | 54 | =encoding utf8 55 | 56 | =head1 NAME 57 | 58 | Mojo::UserAgent::Server - Application server 59 | 60 | =head1 SYNOPSIS 61 | 62 | use Mojo::UserAgent::Server; 63 | 64 | my $server = Mojo::UserAgent::Server->new; 65 | say $server->url; 66 | 67 | =head1 DESCRIPTION 68 | 69 | L is an embedded web server based on L that processes requests for 70 | L. 71 | 72 | =head1 ATTRIBUTES 73 | 74 | L implements the following attributes. 75 | 76 | =head2 ioloop 77 | 78 | my $loop = $server->ioloop; 79 | $server = $server->ioloop(Mojo::IOLoop->new); 80 | 81 | Event loop object to use for I/O operations, defaults to the global L singleton. 82 | 83 | =head1 METHODS 84 | 85 | L inherits all methods from L and implements the following new ones. 86 | 87 | =head2 app 88 | 89 | my $app = Mojo::UserAgent::Server->app; 90 | Mojo::UserAgent::Server->app(Mojolicious->new); 91 | my $app = $server->app; 92 | $server = $server->app(Mojolicious->new); 93 | 94 | Application this server handles, instance specific applications override the global default. 95 | 96 | # Change application behavior 97 | $server->app->defaults(testing => 'oh yea!'); 98 | 99 | =head2 nb_url 100 | 101 | my $url = $server->nb_url; 102 | my $url = $server->nb_url('http'); 103 | my $url = $server->nb_url('https'); 104 | 105 | Get absolute L object for server processing non-blocking requests with L and switch protocol if 106 | necessary. 107 | 108 | =head2 restart 109 | 110 | $server->restart; 111 | 112 | Restart server with new port. 113 | 114 | =head2 url 115 | 116 | my $url = $server->url; 117 | my $url = $server->url('http'); 118 | my $url = $server->url('https'); 119 | 120 | Get absolute L object for server processing blocking requests with L and switch protocol if 121 | necessary. 122 | 123 | =head1 SEE ALSO 124 | 125 | L, L, L. 126 | 127 | =cut 128 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/cpanify.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::cpanify; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::File qw(path); 5 | use Mojo::Util qw(getopt); 6 | 7 | has description => 'Upload distribution to CPAN'; 8 | has usage => sub { shift->extract_usage }; 9 | 10 | sub run { 11 | my ($self, @args) = @_; 12 | 13 | die $self->usage unless getopt \@args, 14 | 'p|password=s' => \(my $password = ''), 15 | 'u|user=s' => \(my $user = ''); 16 | die $self->usage unless my $file = shift @args; 17 | 18 | my $tx = $self->app->ua->tap(sub { $_->proxy->detect })->post( 19 | "https://$user:$password\@pause.perl.org/pause/authenquery" => form => { 20 | HIDDENNAME => $user, 21 | CAN_MULTIPART => 1, 22 | pause99_add_uri_upload => path($file)->basename, 23 | SUBMIT_pause99_add_uri_httpupload => ' Upload this file from my disk ', 24 | pause99_add_uri_uri => '', 25 | pause99_add_uri_httpupload => {file => $file}, 26 | } 27 | ); 28 | 29 | if (my $err = $tx->error) { 30 | my $code = $tx->res->code // 0; 31 | my $msg = $err->{message}; 32 | if ($code == 401) { $msg = 'Wrong username or password.' } 33 | elsif ($code == 409) { $msg = 'File already exists on CPAN.' } 34 | die qq{Problem uploading file "$file": $msg\n}; 35 | } 36 | 37 | say 'Upload successful!'; 38 | } 39 | 40 | 1; 41 | 42 | =encoding utf8 43 | 44 | =head1 NAME 45 | 46 | Mojolicious::Command::Author::cpanify - CPAN-ify command 47 | 48 | =head1 SYNOPSIS 49 | 50 | Usage: APPLICATION cpanify [OPTIONS] [FILE] 51 | 52 | mojo cpanify -u sri -p secr3t Mojolicious-Plugin-MyPlugin-0.01.tar.gz 53 | 54 | Options: 55 | -h, --help Show this summary of available options 56 | -p, --password PAUSE password 57 | -u, --user PAUSE username 58 | 59 | =head1 DESCRIPTION 60 | 61 | L uploads files to CPAN. 62 | 63 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 64 | you're welcome to fork it. 65 | 66 | See L for a list of commands that are available by default. 67 | 68 | =head1 ATTRIBUTES 69 | 70 | L inherits all attributes from L and implements the 71 | following new ones. 72 | 73 | =head2 description 74 | 75 | my $description = $cpanify->description; 76 | $cpanify = $cpanify->description('Foo'); 77 | 78 | Short description of this command, used for the command list. 79 | 80 | =head2 usage 81 | 82 | my $usage = $cpanify->usage; 83 | $cpanify = $cpanify->usage('Foo'); 84 | 85 | Usage information for this command, used for the help screen. 86 | 87 | =head1 METHODS 88 | 89 | L inherits all methods from L and implements the following 90 | new ones. 91 | 92 | =head2 run 93 | 94 | $cpanify->run(@ARGV); 95 | 96 | Run this command. 97 | 98 | =head1 SEE ALSO 99 | 100 | L, L, L. 101 | 102 | =cut 103 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/generate.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::generate; 2 | use Mojo::Base 'Mojolicious::Commands'; 3 | 4 | has description => 'Generate files and directories from templates'; 5 | has hint => < sub { shift->extract_usage . "\nGenerators:\n" }; 11 | has namespaces => sub { ['Mojolicious::Command::Author::generate'] }; 12 | 13 | sub help { shift->run(@_) } 14 | 15 | 1; 16 | 17 | =encoding utf8 18 | 19 | =head1 NAME 20 | 21 | Mojolicious::Command::Author::generate - Generator command 22 | 23 | =head1 SYNOPSIS 24 | 25 | Usage: APPLICATION generate GENERATOR [OPTIONS] 26 | 27 | mojo generate app 28 | mojo generate lite-app 29 | 30 | =head1 DESCRIPTION 31 | 32 | L lists available generators. 33 | 34 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 35 | you're welcome to fork it. 36 | 37 | See L for a list of commands that are available by default. 38 | 39 | =head1 ATTRIBUTES 40 | 41 | L inherits all attributes from L and implements the 42 | following new ones. 43 | 44 | =head2 description 45 | 46 | my $description = $generator->description; 47 | $generator = $generator->description('Foo'); 48 | 49 | Short description of this command, used for the command list. 50 | 51 | =head2 hint 52 | 53 | my $hint = $generator->hint; 54 | $generator = $generator->hint('Foo'); 55 | 56 | Short hint shown after listing available generator commands. 57 | 58 | =head2 message 59 | 60 | my $msg = $generator->message; 61 | $generator = $generator->message('Bar'); 62 | 63 | Short usage message shown before listing available generator commands. 64 | 65 | =head2 namespaces 66 | 67 | my $namespaces = $generator->namespaces; 68 | $generator = $generator->namespaces(['MyApp::Command::generate']); 69 | 70 | Namespaces to search for available generator commands, defaults to L. 71 | 72 | =head1 METHODS 73 | 74 | L inherits all methods from L and implements the 75 | following new ones. 76 | 77 | =head2 help 78 | 79 | $generator->help('app'); 80 | 81 | Print usage information for generator command. 82 | 83 | =head1 SEE ALSO 84 | 85 | L, L, L. 86 | 87 | =cut 88 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/generate/dockerfile.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::generate::dockerfile; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::File qw(path); 5 | 6 | has description => 'Generate "Dockerfile"'; 7 | has usage => sub { shift->extract_usage }; 8 | 9 | sub run { 10 | my $self = shift; 11 | my $name = $self->app->moniker; 12 | my $exe = $ENV{MOJO_EXE} ? path($ENV{MOJO_EXE})->to_rel($self->app->home)->to_string : "script/$name"; 13 | $self->render_to_rel_file('dockerfile', 'Dockerfile', {name => $name, cmd => "./$exe prefork"}); 14 | } 15 | 16 | 1; 17 | 18 | =encoding utf8 19 | 20 | =head1 NAME 21 | 22 | Mojolicious::Command::Author::generate::dockerfile - Dockerfile generator command 23 | 24 | =head1 SYNOPSIS 25 | 26 | Usage: APPLICATION generate dockerfile [OPTIONS] 27 | 28 | ./myapp.pl generate dockerfile 29 | ./script/my_app generate dockerfile 30 | 31 | Options: 32 | -h, --help Show this summary of available options 33 | 34 | =head1 DESCRIPTION 35 | 36 | L generates C for applications. 37 | 38 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 39 | you're welcome to fork it. 40 | 41 | See L for a list of commands that are available by default. 42 | 43 | =head1 ATTRIBUTES 44 | 45 | L inherits all attributes from L and 46 | implements the following new ones. 47 | 48 | =head2 description 49 | 50 | my $description = $dockerfile->description; 51 | $dockerfile = $dockerfile->description('Foo'); 52 | 53 | Short description of this command, used for the command list. 54 | 55 | =head2 usage 56 | 57 | my $usage = $dockerfile->usage; 58 | $dockerfile = $dockerfile->usage('Foo'); 59 | 60 | Usage information for this command, used for the help screen. 61 | 62 | =head1 METHODS 63 | 64 | L inherits all methods from L and implements 65 | the following new ones. 66 | 67 | =head2 run 68 | 69 | $dockerfile->run(@ARGV); 70 | 71 | Run this command. 72 | 73 | =head1 SEE ALSO 74 | 75 | L, L, L. 76 | 77 | =cut 78 | 79 | __DATA__ 80 | 81 | @@ dockerfile 82 | FROM perl 83 | WORKDIR /opt/<%= $name %> 84 | COPY . . 85 | RUN cpanm --installdeps -n . 86 | EXPOSE 3000 87 | CMD <%= $cmd %> 88 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/generate/lite_app.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::generate::lite_app; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | has description => 'Generate Mojolicious::Lite application'; 5 | has usage => sub { shift->extract_usage }; 6 | 7 | sub run { 8 | my ($self, $name) = (shift, shift || 'myapp.pl'); 9 | $self->render_to_rel_file('liteapp', $name); 10 | $self->chmod_rel_file($name, 0744); 11 | } 12 | 13 | 1; 14 | 15 | =encoding utf8 16 | 17 | =head1 NAME 18 | 19 | Mojolicious::Command::Author::generate::lite_app - Lite app generator command 20 | 21 | =head1 SYNOPSIS 22 | 23 | Usage: APPLICATION generate lite-app [OPTIONS] [NAME] 24 | 25 | mojo generate lite-app 26 | mojo generate lite-app foo.pl 27 | 28 | Options: 29 | -h, --help Show this summary of available options 30 | 31 | =head1 DESCRIPTION 32 | 33 | L generate fully functional L applications. 34 | 35 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 36 | you're welcome to fork it. 37 | 38 | See L for a list of commands that are available by default. 39 | 40 | =head1 ATTRIBUTES 41 | 42 | L inherits all attributes from L and implements 43 | the following new ones. 44 | 45 | =head2 description 46 | 47 | my $description = $app->description; 48 | $app = $app->description('Foo'); 49 | 50 | Short description of this command, used for the command list. 51 | 52 | =head2 usage 53 | 54 | my $usage = $app->usage; 55 | $app = $app->usage('Foo'); 56 | 57 | Usage information for this command, used for the help screen. 58 | 59 | =head1 METHODS 60 | 61 | L inherits all methods from L and implements 62 | the following new ones. 63 | 64 | =head2 run 65 | 66 | $app->run(@ARGV); 67 | 68 | Run this command. 69 | 70 | =head1 SEE ALSO 71 | 72 | L, L, L. 73 | 74 | =cut 75 | 76 | __DATA__ 77 | 78 | @@ liteapp 79 | #!/usr/bin/env perl 80 | use Mojolicious::Lite -signatures; 81 | 82 | get '/' => sub ($c) { 83 | $c->render(template => 'index'); 84 | }; 85 | 86 | app->start; 87 | <% %>__DATA__ 88 | 89 | <% %>@@ index.html.ep 90 | %% layout 'default'; 91 | %% title 'Welcome'; 92 |

Welcome to the Mojolicious real-time web framework!

93 | 94 | <% %>@@ layouts/default.html.ep 95 | 96 | 97 | <%%= title %> 98 | <%%= content %> 99 | 100 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/generate/makefile.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::generate::makefile; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | has description => 'Generate "Makefile.PL"'; 5 | has usage => sub { shift->extract_usage }; 6 | 7 | sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') } 8 | 9 | 1; 10 | 11 | =encoding utf8 12 | 13 | =head1 NAME 14 | 15 | Mojolicious::Command::Author::generate::makefile - Makefile generator command 16 | 17 | =head1 SYNOPSIS 18 | 19 | Usage: APPLICATION generate makefile [OPTIONS] 20 | 21 | ./myapp.pl generate makefile 22 | mojo generate makefile 23 | 24 | Options: 25 | -h, --help Show this summary of available options 26 | 27 | =head1 DESCRIPTION 28 | 29 | L generates C files for applications. 30 | 31 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 32 | you're welcome to fork it. 33 | 34 | See L for a list of commands that are available by default. 35 | 36 | =head1 ATTRIBUTES 37 | 38 | L inherits all attributes from L and implements 39 | the following new ones. 40 | 41 | =head2 description 42 | 43 | my $description = $makefile->description; 44 | $makefile = $makefile->description('Foo'); 45 | 46 | Short description of this command, used for the command list. 47 | 48 | =head2 usage 49 | 50 | my $usage = $makefile->usage; 51 | $makefile = $makefile->usage('Foo'); 52 | 53 | Usage information for this command, used for the help screen. 54 | 55 | =head1 METHODS 56 | 57 | L inherits all methods from L and implements 58 | the following new ones. 59 | 60 | =head2 run 61 | 62 | $makefile->run(@ARGV); 63 | 64 | Run this command. 65 | 66 | =head1 SEE ALSO 67 | 68 | L, L, L. 69 | 70 | =cut 71 | 72 | __DATA__ 73 | 74 | @@ makefile 75 | use strict; 76 | use warnings; 77 | 78 | use ExtUtils::MakeMaker; 79 | 80 | WriteMakefile( 81 | VERSION => '0.01', 82 | PREREQ_PM => { 83 | 'Mojolicious' => '<%= $Mojolicious::VERSION %>' 84 | }, 85 | test => {TESTS => 't/*.t'} 86 | ); 87 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/Author/inflate.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::Author::inflate; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::Loader qw(data_section file_is_binary); 5 | use Mojo::Util qw(encode); 6 | 7 | has description => 'Inflate embedded files to real files'; 8 | has usage => sub { shift->extract_usage }; 9 | 10 | sub run { 11 | my $self = shift; 12 | 13 | # Find all embedded files 14 | my %all; 15 | my $app = $self->app; 16 | for my $class (@{$app->renderer->classes}, @{$app->static->classes}) { 17 | for my $name (keys %{data_section $class}) { 18 | my $data = data_section $class, $name; 19 | $data = encode 'UTF-8', $data unless file_is_binary $class, $name; 20 | $all{$name} = $data; 21 | } 22 | } 23 | 24 | # Turn them into real files 25 | for my $name (grep {/\.\w+$/} keys %all) { 26 | my $prefix = $name =~ /\.\w+\.\w+$/ ? 'templates' : 'public'; 27 | $self->write_file($self->rel_file("$prefix/$name"), $all{$name}); 28 | } 29 | } 30 | 31 | 1; 32 | 33 | =encoding utf8 34 | 35 | =head1 NAME 36 | 37 | Mojolicious::Command::Author::inflate - Inflate command 38 | 39 | =head1 SYNOPSIS 40 | 41 | Usage: APPLICATION inflate [OPTIONS] 42 | 43 | ./myapp.pl inflate 44 | 45 | Options: 46 | -h, --help Show this summary of available options 47 | --home Path to home directory of your application, defaults to 48 | the value of MOJO_HOME or auto-detection 49 | -m, --mode Operating mode for your application, defaults to the 50 | value of MOJO_MODE/PLACK_ENV or "development" 51 | 52 | =head1 DESCRIPTION 53 | 54 | L turns templates and static files embedded in the C sections of your 55 | application into real files. 56 | 57 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 58 | you're welcome to fork it. 59 | 60 | See L for a list of commands that are available by default. 61 | 62 | =head1 ATTRIBUTES 63 | 64 | L inherits all attributes from L and implements the 65 | following new ones. 66 | 67 | =head2 description 68 | 69 | my $description = $inflate->description; 70 | $inflate = $inflate->description('Foo'); 71 | 72 | Short description of this command, used for the command list. 73 | 74 | =head2 usage 75 | 76 | my $usage = $inflate->usage; 77 | $inflate = $inflate->usage('Foo'); 78 | 79 | Usage information for this command, used for the help screen. 80 | 81 | =head1 METHODS 82 | 83 | L inherits all methods from L and implements the following 84 | new ones. 85 | 86 | =head2 run 87 | 88 | $inflate->run(@ARGV); 89 | 90 | Run this command. 91 | 92 | =head1 SEE ALSO 93 | 94 | L, L, L. 95 | 96 | =cut 97 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/cgi.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::cgi; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::Server::CGI; 5 | use Mojo::Util qw(getopt); 6 | 7 | has description => 'Start application with CGI'; 8 | has usage => sub { shift->extract_usage }; 9 | 10 | sub run { 11 | my ($self, @args) = @_; 12 | die $self->usage unless getopt \@args, nph => \(my $nph = 0); 13 | Mojo::Server::CGI->new(app => $self->app, nph => $nph)->run; 14 | } 15 | 16 | 1; 17 | 18 | =encoding utf8 19 | 20 | =head1 NAME 21 | 22 | Mojolicious::Command::cgi - CGI command 23 | 24 | =head1 SYNOPSIS 25 | 26 | Usage: APPLICATION cgi [OPTIONS] 27 | 28 | ./myapp.pl cgi 29 | 30 | Options: 31 | -h, --help Show this summary of available options 32 | --home Path to home directory of your application, defaults to 33 | the value of MOJO_HOME or auto-detection 34 | -m, --mode Operating mode for your application, defaults to the 35 | value of MOJO_MODE/PLACK_ENV or "development" 36 | --nph Enable non-parsed-header mode 37 | 38 | =head1 DESCRIPTION 39 | 40 | L starts applications with the L backend. 41 | 42 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 43 | you're welcome to fork it. 44 | 45 | See L for a list of commands that are available by default. 46 | 47 | =head1 ATTRIBUTES 48 | 49 | L inherits all attributes from L and implements the following new 50 | ones. 51 | 52 | =head2 description 53 | 54 | my $description = $cgi->description; 55 | $cgi = $cgi->description('Foo'); 56 | 57 | Short description of this command, used for the command list. 58 | 59 | =head2 usage 60 | 61 | my $usage = $cgi->usage; 62 | $cgi = $cgi->usage('Foo'); 63 | 64 | Usage information for this command, used for the help screen. 65 | 66 | =head1 METHODS 67 | 68 | L inherits all methods from L and implements the following new ones. 69 | 70 | =head2 run 71 | 72 | $cgi->run(@ARGV); 73 | 74 | Run this command. 75 | 76 | =head1 SEE ALSO 77 | 78 | L, L, L. 79 | 80 | =cut 81 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/eval.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::eval; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::Promise; 5 | use Mojo::Util qw(getopt); 6 | 7 | has description => 'Run code against application'; 8 | has usage => sub { shift->extract_usage }; 9 | 10 | sub run { 11 | my ($self, @args) = @_; 12 | 13 | die $self->usage unless getopt \@args, 'v|verbose' => \my $v1, 'V' => \my $v2; 14 | my $code = shift @args || ''; 15 | 16 | # Run code against application 17 | my $app = $self->app; 18 | my $result = eval "package main; no warnings 'redefine'; sub app; local *app = sub { \$app }; $code"; 19 | die $@ if $@; 20 | 21 | # Handle promises 22 | my $err; 23 | Mojo::Promise->resolve($result)->then(sub { $result = shift }, sub { $err = shift })->wait; 24 | die $err if $err; 25 | 26 | return $result unless defined $result && ($v1 || $v2); 27 | $v2 ? print($app->dumper($result)) : say $result; 28 | } 29 | 30 | 1; 31 | 32 | =encoding utf8 33 | 34 | =head1 NAME 35 | 36 | Mojolicious::Command::eval - Eval command 37 | 38 | =head1 SYNOPSIS 39 | 40 | Usage: APPLICATION eval [OPTIONS] CODE 41 | 42 | ./myapp.pl eval 'say app->ua->get("/")->result->body' 43 | ./myapp.pl eval 'say for sort keys %{app->renderer->helpers}' 44 | ./myapp.pl eval -v 'app->home' 45 | ./myapp.pl eval -V 'app->renderer->paths' 46 | 47 | Options: 48 | -h, --help Show this summary of available options 49 | --home Path to home directory of your application, defaults to 50 | the value of MOJO_HOME or auto-detection 51 | -m, --mode Operating mode for your application, defaults to the 52 | value of MOJO_MODE/PLACK_ENV or "development" 53 | -v, --verbose Print return value to STDOUT 54 | -V Print returned data structure to STDOUT 55 | 56 | =head1 DESCRIPTION 57 | 58 | L runs code against applications. If the result is a promise (then-able), it will wait 59 | until the promise is fulfilled or rejected and the result is returned. 60 | 61 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 62 | you're welcome to fork it. 63 | 64 | See L for a list of commands that are available by default. 65 | 66 | =head1 ATTRIBUTES 67 | 68 | L inherits all attributes from L and implements the following new 69 | ones. 70 | 71 | =head2 description 72 | 73 | my $description = $eval->description; 74 | $eval = $eval->description('Foo'); 75 | 76 | Short description of this command, used for the command list. 77 | 78 | =head2 usage 79 | 80 | my $usage = $eval->usage; 81 | $eval = $eval->usage('Foo'); 82 | 83 | Usage information for this command, used for the help screen. 84 | 85 | =head1 METHODS 86 | 87 | L inherits all methods from L and implements the following new ones. 88 | 89 | =head2 run 90 | 91 | $eval->run(@ARGV); 92 | 93 | Run this command. 94 | 95 | =head1 SEE ALSO 96 | 97 | L, L, L. 98 | 99 | =cut 100 | -------------------------------------------------------------------------------- /lib/Mojolicious/Command/psgi.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Command::psgi; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::Server::PSGI; 5 | 6 | has description => 'Start application with PSGI'; 7 | has usage => sub { shift->extract_usage }; 8 | 9 | sub run { Mojo::Server::PSGI->new(app => shift->app)->to_psgi_app } 10 | 11 | 1; 12 | 13 | =encoding utf8 14 | 15 | =head1 NAME 16 | 17 | Mojolicious::Command::psgi - PSGI command 18 | 19 | =head1 SYNOPSIS 20 | 21 | Usage: APPLICATION psgi [OPTIONS] 22 | 23 | ./myapp.pl psgi 24 | 25 | Options: 26 | -h, --help Show this summary of available options 27 | --home Path to home directory of your application, defaults to 28 | the value of MOJO_HOME or auto-detection 29 | -m, --mode Operating mode for your application, defaults to the 30 | value of MOJO_MODE/PLACK_ENV or "development" 31 | 32 | =head1 DESCRIPTION 33 | 34 | L starts applications with the L backend. 35 | 36 | This is a core command, that means it is always enabled and its code a good example for learning to build new commands, 37 | you're welcome to fork it. 38 | 39 | See L for a list of commands that are available by default. 40 | 41 | =head1 ATTRIBUTES 42 | 43 | L inherits all attributes from L and implements the following new 44 | ones. 45 | 46 | =head2 description 47 | 48 | my $description = $psgi->description; 49 | $psgi = $psgi->description('Foo'); 50 | 51 | Short description of this command, used for the command list. 52 | 53 | =head2 usage 54 | 55 | my $usage = $psgi->usage; 56 | $psgi = $psgi->usage('Foo'); 57 | 58 | Usage information for this command, used for the help screen. 59 | 60 | =head1 METHODS 61 | 62 | L inherits all methods from L and implements the following new ones. 63 | 64 | =head2 run 65 | 66 | my $app = $psgi->run; 67 | 68 | Run this command. 69 | 70 | =head1 SEE ALSO 71 | 72 | L, L, L. 73 | 74 | =cut 75 | -------------------------------------------------------------------------------- /lib/Mojolicious/Plugin.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Plugin; 2 | use Mojo::Base -base; 3 | 4 | use Carp qw(croak); 5 | 6 | sub register { croak 'Method "register" not implemented by subclass' } 7 | 8 | 1; 9 | 10 | =encoding utf8 11 | 12 | =head1 NAME 13 | 14 | Mojolicious::Plugin - Plugin base class 15 | 16 | =head1 SYNOPSIS 17 | 18 | # CamelCase plugin name 19 | package Mojolicious::Plugin::MyPlugin; 20 | use Mojo::Base 'Mojolicious::Plugin', -signatures; 21 | 22 | sub register ($self, $app, $conf) { 23 | 24 | # Magic here! :) 25 | } 26 | 27 | =head1 DESCRIPTION 28 | 29 | L is an abstract base class for L plugins. 30 | 31 | See L for a list of plugins that are available by default. 32 | 33 | =head1 METHODS 34 | 35 | L inherits all methods from L and implements the following new ones. 36 | 37 | =head2 register 38 | 39 | $plugin->register(Mojolicious->new); 40 | $plugin->register(Mojolicious->new, {foo => 'bar'}); 41 | 42 | This method will be called by L at startup time. Meant to be overloaded in a subclass. 43 | 44 | =head1 SEE ALSO 45 | 46 | L, L, L. 47 | 48 | =cut 49 | -------------------------------------------------------------------------------- /lib/Mojolicious/Plugin/EPLRenderer.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Plugin::EPLRenderer; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | use Mojo::Template; 5 | use Mojo::Util qw(encode md5_sum); 6 | 7 | sub register { 8 | my ($self, $app) = @_; 9 | $app->renderer->add_handler(epl => sub { _render(@_, Mojo::Template->new, $_[1]) }); 10 | } 11 | 12 | sub _render { 13 | my ($renderer, $c, $output, $options, $mt, @args) = @_; 14 | 15 | # Cached 16 | if ($mt->compiled) { 17 | $c->helpers->log->trace("Rendering cached @{[$mt->name]}"); 18 | $$output = $mt->process(@args); 19 | } 20 | 21 | # Not cached 22 | else { 23 | my $inline = $options->{inline}; 24 | my $name = defined $inline ? md5_sum encode('UTF-8', $inline) : undef; 25 | return unless defined($name //= $renderer->template_name($options)); 26 | 27 | # Inline 28 | if (defined $inline) { 29 | $c->helpers->log->trace(qq{Rendering inline template "$name"}); 30 | $$output = $mt->name(qq{inline template "$name"})->render($inline, @args); 31 | } 32 | 33 | # File 34 | else { 35 | if (my $encoding = $renderer->encoding) { $mt->encoding($encoding) } 36 | 37 | # Try template 38 | if (defined(my $path = $renderer->template_path($options))) { 39 | $c->helpers->log->trace(qq{Rendering template "$name"}); 40 | $$output = $mt->name(qq{template "$name"})->render_file($path, @args); 41 | } 42 | 43 | # Try DATA section 44 | elsif (defined(my $d = $renderer->get_data_template($options))) { 45 | $c->helpers->log->trace(qq{Rendering template "$name" from DATA section}); 46 | $$output = $mt->name(qq{template "$name" from DATA section})->render($d, @args); 47 | } 48 | 49 | # No template 50 | else { $c->helpers->log->trace(qq{Template "$name" not found}) } 51 | } 52 | } 53 | 54 | # Exception 55 | die $$output if ref $$output; 56 | } 57 | 58 | 1; 59 | 60 | =encoding utf8 61 | 62 | =head1 NAME 63 | 64 | Mojolicious::Plugin::EPLRenderer - Embedded Perl Lite renderer plugin 65 | 66 | =head1 SYNOPSIS 67 | 68 | # Mojolicious 69 | $app->plugin('EPLRenderer'); 70 | 71 | # Mojolicious::Lite 72 | plugin 'EPLRenderer'; 73 | 74 | =head1 DESCRIPTION 75 | 76 | L is a renderer for C templates, which are pretty much just raw 77 | L. 78 | 79 | This is a core plugin, that means it is always enabled and its code a good example for learning to build new plugins, 80 | you're welcome to fork it. 81 | 82 | See L for a list of plugins that are available by default. 83 | 84 | =head1 METHODS 85 | 86 | L inherits all methods from L and implements the following new 87 | ones. 88 | 89 | =head2 register 90 | 91 | $plugin->register(Mojolicious->new); 92 | 93 | Register renderer in L application. 94 | 95 | =head1 SEE ALSO 96 | 97 | L, L, L. 98 | 99 | =cut 100 | -------------------------------------------------------------------------------- /lib/Mojolicious/Plugin/HeaderCondition.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Plugin::HeaderCondition; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | use re qw(is_regexp); 5 | 6 | sub register { 7 | my ($self, $app) = @_; 8 | 9 | $app->routes->add_condition(headers => \&_headers); 10 | $app->routes->add_condition(agent => sub { _headers(@_[0 .. 2], {'User-Agent' => $_[3]}) }); 11 | $app->routes->add_condition(host => sub { _check($_[1]->req->url->to_abs->host, $_[3]) }); 12 | } 13 | 14 | sub _check { 15 | my ($value, $pattern) = @_; 16 | return 1 if $value && $pattern && is_regexp($pattern) && $value =~ $pattern; 17 | return $value && defined $pattern && $pattern eq $value; 18 | } 19 | 20 | sub _headers { 21 | my ($route, $c, $captures, $patterns) = @_; 22 | return undef unless $patterns && ref $patterns eq 'HASH' && keys %$patterns; 23 | 24 | # All headers need to match 25 | my $headers = $c->req->headers; 26 | _check($headers->header($_), $patterns->{$_}) || return undef for keys %$patterns; 27 | return 1; 28 | } 29 | 30 | 1; 31 | 32 | =encoding utf8 33 | 34 | =head1 NAME 35 | 36 | Mojolicious::Plugin::HeaderCondition - Header condition plugin 37 | 38 | =head1 SYNOPSIS 39 | 40 | # Mojolicious 41 | $app->plugin('HeaderCondition'); 42 | $app->routes->get('/foo')->requires(headers => {Referer => qr/example\.com/}); 43 | 44 | # Mojolicious::Lite 45 | plugin 'HeaderCondition'; 46 | get '/' => (headers => {Referer => qr/example\.com/}) => sub {...}; 47 | 48 | # All headers need to match 49 | $app->routes->get('/foo')->requires(headers => { 50 | 'X-Secret-Header' => 'Foo', 51 | Referer => qr/example\.com/ 52 | }); 53 | 54 | # The "agent" condition is a shortcut for the "User-Agent" header 55 | get '/' => (agent => qr/Firefox/) => sub {...}; 56 | 57 | # The "host" condition is a shortcut for the detected host 58 | get '/' => (host => qr/mojolicious\.org/) => sub {...}; 59 | 60 | =head1 DESCRIPTION 61 | 62 | L is a route condition for header-based routes. 63 | 64 | This is a core plugin, that means it is always enabled and its code a good example for learning to build new plugins, 65 | you're welcome to fork it. 66 | 67 | See L for a list of plugins that are available by default. 68 | 69 | =head1 METHODS 70 | 71 | L inherits all methods from L and implements the following 72 | new ones. 73 | 74 | =head2 register 75 | 76 | $plugin->register(Mojolicious->new); 77 | 78 | Register conditions in L application. 79 | 80 | =head1 SEE ALSO 81 | 82 | L, L, L. 83 | 84 | =cut 85 | -------------------------------------------------------------------------------- /lib/Mojolicious/Plugin/Mount.pm: -------------------------------------------------------------------------------- 1 | package Mojolicious::Plugin::Mount; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | use Mojo::Server; 5 | 6 | sub register { 7 | my ($self, $app, $conf) = @_; 8 | 9 | my $path = (keys %$conf)[0]; 10 | my $embed = Mojo::Server->new->load_app($conf->{$path})->secrets($app->secrets)->log($app->log); 11 | 12 | # Extract host 13 | my $host; 14 | ($host, $path) = ($1 ? qr/^(?:.*\.)?\Q$2\E$/i : qr/^\Q$2\E$/i, $3) if $path =~ m!^(\*\.)?([^/]+)(/.*)?$!; 15 | 16 | my $route = $app->routes->any($path)->partial(1)->to(app => $embed); 17 | return $host ? $route->requires(host => $host) : $route; 18 | } 19 | 20 | 1; 21 | 22 | =encoding utf8 23 | 24 | =head1 NAME 25 | 26 | Mojolicious::Plugin::Mount - Application mount plugin 27 | 28 | =head1 SYNOPSIS 29 | 30 | # Mojolicious 31 | my $route = $app->plugin(Mount => {'/prefix' => '/home/sri/foo/script/foo'}); 32 | 33 | # Mojolicious::Lite 34 | my $route = plugin Mount => {'/prefix' => '/home/sri/myapp.pl'}; 35 | 36 | # Adjust the generated route and mounted application 37 | my $example = plugin Mount => {'/example' => '/home/sri/example.pl'}; 38 | $example->to(message => 'It works great!'); 39 | my $app = $example->pattern->defaults->{app}; 40 | $app->config(foo => 'bar'); 41 | $app->log(app->log); 42 | 43 | # Mount application with host 44 | plugin Mount => {'example.com' => '/home/sri/myapp.pl'}; 45 | 46 | # Host and path 47 | plugin Mount => {'example.com/myapp' => '/home/sri/myapp.pl'}; 48 | 49 | # Or even hosts with wildcard subdomains 50 | plugin Mount => {'*.example.com/myapp' => '/home/sri/myapp.pl'}; 51 | 52 | =head1 DESCRIPTION 53 | 54 | L is a plugin that allows you to mount whole L applications. 55 | 56 | The code of this plugin is a good example for learning to build new plugins, you're welcome to fork it. 57 | 58 | See L for a list of plugins that are available by default. 59 | 60 | =head1 METHODS 61 | 62 | L inherits all methods from L and implements the following new ones. 63 | 64 | =head2 register 65 | 66 | my $route = $plugin->register(Mojolicious->new, {'/foo' => '/some/app.pl'}); 67 | 68 | Mount L application and return the generated route, which is usually a L 69 | object. 70 | 71 | =head1 SEE ALSO 72 | 73 | L, L, L. 74 | 75 | =cut 76 | -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/favicon.ico -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/failraptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/failraptor.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/highlight.js/highlight-mojo-dark.css: -------------------------------------------------------------------------------- 1 | .hljs { 2 | display: block; 3 | overflow-x: auto; 4 | } 5 | 6 | .hljs-keyword, 7 | .hljs-selector-tag, 8 | .hljs-literal, 9 | .hljs-section, 10 | .hljs-link { 11 | color:#fcf0a4; 12 | } 13 | 14 | .hljs, 15 | .hljs-subst { 16 | color:#889dbc; 17 | } 18 | 19 | .hljs-string, 20 | .hljs-title, 21 | .hljs-name, 22 | .hljs-type, 23 | .hljs-attribute, 24 | .hljs-symbol, 25 | .hljs-bullet, 26 | .hljs-built_in, 27 | .hljs-addition, 28 | .hljs-variable, 29 | .hljs-template-tag, 30 | .hljs-template-variable { 31 | color: #9daa7e; 32 | } 33 | 34 | .hljs-comment, 35 | .hljs-quote, 36 | .hljs-deletion, 37 | .hljs-meta { 38 | color:#726d73; 39 | } 40 | 41 | .hljs-keyword, 42 | .hljs-selector-tag, 43 | .hljs-literal, 44 | .hljs-title, 45 | .hljs-section, 46 | .hljs-doctag, 47 | .hljs-type, 48 | .hljs-name, 49 | .hljs-strong { 50 | font-weight: bold; 51 | color:#d5b57c; 52 | } 53 | 54 | .hljs-emphasis { 55 | font-style: italic; 56 | } 57 | -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/highlight.js/mojolicious.min.js: -------------------------------------------------------------------------------- 1 | hljs.registerLanguage("mojolicious",function(){"use strict";return function(e){return{name:"Mojolicious",subLanguage:"xml",contains:[{className:"meta",begin:"^__(END|DATA)__$"},{begin:"^\\s*%{1,2}={0,2}",end:"$",subLanguage:"perl"},{begin:"<%{1,2}={0,2}",end:"={0,1}%>",subLanguage:"perl",excludeBegin:!0,excludeEnd:!0}]}}}()); -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/logo-white-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/logo-white-2x.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/logo-white.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/logo.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/noraptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/noraptor.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/notfound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/notfound.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/pinstripe-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/pinstripe-dark.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/public/mojo/pinstripe-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/lib/Mojolicious/resources/public/mojo/pinstripe-light.png -------------------------------------------------------------------------------- /lib/Mojolicious/resources/templates/mojo/exception.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Server Error 6 | 21 | %= favicon 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/Mojolicious/resources/templates/mojo/not_found.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found 6 | 29 | %= favicon 30 | 31 | 32 | %= link_to url_for->base => begin 33 | %= image '/mojo/noraptor.png', alt => 'Bye!', id => 'noraptor' 34 | % end 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /script/hypnotoad: -------------------------------------------------------------------------------- 1 | #!perl 2 | use Mojo::Base -strict; 3 | 4 | use Mojo::Server::Hypnotoad; 5 | use Mojo::Util qw(extract_usage getopt); 6 | 7 | getopt 8 | 'f|foreground' => \$ENV{HYPNOTOAD_FOREGROUND}, 9 | 'h|help' => \my $help, 10 | 's|stop' => \$ENV{HYPNOTOAD_STOP}, 11 | 't|test' => \$ENV{HYPNOTOAD_TEST}; 12 | 13 | die extract_usage if $help || !(my $app = shift || $ENV{HYPNOTOAD_APP}); 14 | Mojo::Server::Hypnotoad->new->run($app); 15 | 16 | =encoding utf8 17 | 18 | =head1 NAME 19 | 20 | hypnotoad - Hypnotoad HTTP and WebSocket server 21 | 22 | =head1 SYNOPSIS 23 | 24 | Usage: hypnotoad [OPTIONS] [APPLICATION] 25 | 26 | hypnotoad ./script/my_app 27 | hypnotoad ./myapp.pl 28 | hypnotoad -f ./myapp.pl 29 | 30 | Options: 31 | -f, --foreground Keep manager process in foreground 32 | -h, --help Show this message 33 | -s, --stop Stop server gracefully 34 | -t, --test Test application and exit 35 | 36 | =head1 DESCRIPTION 37 | 38 | Start L and L applications with the L web server. 39 | 40 | =head1 SEE ALSO 41 | 42 | L, L, L. 43 | 44 | =cut 45 | -------------------------------------------------------------------------------- /script/mojo: -------------------------------------------------------------------------------- 1 | #!perl 2 | use Mojo::Base -strict; 3 | 4 | use Mojolicious::Commands; 5 | 6 | Mojolicious::Commands->start_app('Mojo::HelloWorld'); 7 | 8 | =encoding utf8 9 | 10 | =head1 NAME 11 | 12 | mojo - The Mojolicious command system 13 | 14 | =head1 SYNOPSIS 15 | 16 | $ mojo --help 17 | 18 | =head1 DESCRIPTION 19 | 20 | List and run L commands as described in L. 21 | 22 | =head1 SEE ALSO 23 | 24 | L, L, L. 25 | 26 | =cut 27 | -------------------------------------------------------------------------------- /script/morbo: -------------------------------------------------------------------------------- 1 | #!perl 2 | use Mojo::Base -strict; 3 | 4 | use Mojo::Server::Morbo; 5 | use Mojo::Util qw(extract_usage getopt); 6 | 7 | getopt 8 | 'b|backend=s' => \$ENV{MOJO_MORBO_BACKEND}, 9 | 'h|help' => \my $help, 10 | 'l|listen=s' => \my @listen, 11 | 'm|mode=s' => \$ENV{MOJO_MODE}, 12 | 'v|verbose' => \my $verbose, 13 | 'w|watch=s' => \my @watch; 14 | 15 | die extract_usage if $help || !(my $app = shift); 16 | my $morbo = Mojo::Server::Morbo->new(silent => !$verbose); 17 | $morbo->daemon->listen(\@listen) if @listen; 18 | $morbo->backend->watch(\@watch) if @watch; 19 | $morbo->run($app); 20 | 21 | =encoding utf8 22 | 23 | =head1 NAME 24 | 25 | morbo - Morbo HTTP and WebSocket development server 26 | 27 | =head1 SYNOPSIS 28 | 29 | Usage: morbo [OPTIONS] [APPLICATION] 30 | 31 | morbo ./script/my_app 32 | morbo ./myapp.pl 33 | morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl 34 | morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl 35 | morbo -w /usr/local/lib -w public -w myapp.conf ./myapp.pl 36 | 37 | Options: 38 | -b, --backend Morbo backend to use for reloading, defaults 39 | to "Poll" 40 | -h, --help Show this message 41 | -l, --listen One or more locations you want to listen on, 42 | defaults to the value of MOJO_LISTEN or 43 | "http://*:3000" 44 | -m, --mode Operating mode for your application, 45 | defaults to the value of 46 | MOJO_MODE/PLACK_ENV or "development" 47 | -v, --verbose Print details about what files changed to 48 | STDOUT 49 | -w, --watch One or more directories and files to watch 50 | for changes, defaults to the application 51 | script as well as the "lib" and "templates" 52 | directories in the current working 53 | directory 54 | 55 | =head1 DESCRIPTION 56 | 57 | Start L and L applications with the L web server. 58 | 59 | =head1 SEE ALSO 60 | 61 | L, L, L. 62 | 63 | =cut 64 | -------------------------------------------------------------------------------- /t/mojo/base_util.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Sub::Util qw(subname); 5 | 6 | use Mojo::BaseUtil qw(class_to_path monkey_patch); 7 | 8 | subtest 'class_to_path' => sub { 9 | is Mojo::BaseUtil::class_to_path('Foo::Bar'), 'Foo/Bar.pm', 'right path'; 10 | is Mojo::BaseUtil::class_to_path("Foo'Bar"), 'Foo/Bar.pm', 'right path'; 11 | is Mojo::BaseUtil::class_to_path("Foo'Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; 12 | is Mojo::BaseUtil::class_to_path("Foo::Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; 13 | is Mojo::BaseUtil::class_to_path("Foo::Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; 14 | is Mojo::BaseUtil::class_to_path("Foo'Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; 15 | }; 16 | 17 | subtest 'monkey_patch' => sub { 18 | { 19 | 20 | package MojoMonkeyTest; 21 | sub foo {'foo'} 22 | } 23 | ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; 24 | is MojoMonkeyTest::foo(), 'foo', 'right result'; 25 | ok !MojoMonkeyTest->can('bar'), 'function "bar" does not exist'; 26 | monkey_patch 'MojoMonkeyTest', bar => sub {'bar'}; 27 | ok !!MojoMonkeyTest->can('bar'), 'function "bar" exists'; 28 | is MojoMonkeyTest::bar(), 'bar', 'right result'; 29 | monkey_patch 'MojoMonkeyTest', foo => sub {'baz'}; 30 | ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; 31 | is MojoMonkeyTest::foo(), 'baz', 'right result'; 32 | ok !MojoMonkeyTest->can('yin'), 'function "yin" does not exist'; 33 | ok !MojoMonkeyTest->can('yang'), 'function "yang" does not exist'; 34 | monkey_patch 'MojoMonkeyTest', 35 | yin => sub {'yin'}, 36 | yang => sub {'yang'}; 37 | ok !!MojoMonkeyTest->can('yin'), 'function "yin" exists'; 38 | is MojoMonkeyTest::yin(), 'yin', 'right result'; 39 | ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists'; 40 | is MojoMonkeyTest::yang(), 'yang', 'right result'; 41 | }; 42 | 43 | subtest 'monkey_patch (with name)' => sub { 44 | is subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo', 'right name'; 45 | is subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar', 'right name'; 46 | }; 47 | 48 | done_testing(); 49 | -------------------------------------------------------------------------------- /t/mojo/cache.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Mojo::Cache; 5 | 6 | subtest 'Basics' => sub { 7 | my $cache = Mojo::Cache->new(max_keys => 2); 8 | is $cache->get('foo'), undef, 'no result'; 9 | $cache->set(foo => 'bar'); 10 | is $cache->get('foo'), 'bar', 'right result'; 11 | $cache->set(bar => 'baz'); 12 | is $cache->get('foo'), 'bar', 'right result'; 13 | is $cache->get('bar'), 'baz', 'right result'; 14 | $cache->set(baz => 'yada'); 15 | is $cache->get('foo'), undef, 'no result'; 16 | is $cache->get('bar'), 'baz', 'right result'; 17 | is $cache->get('baz'), 'yada', 'right result'; 18 | $cache->set(yada => 23); 19 | is $cache->get('foo'), undef, 'no result'; 20 | is $cache->get('bar'), undef, 'no result'; 21 | is $cache->get('baz'), 'yada', 'right result'; 22 | is $cache->get('yada'), 23, 'right result'; 23 | $cache->max_keys(1)->set(one => 1)->set(two => 2); 24 | is $cache->get('one'), undef, 'no result'; 25 | is $cache->get('two'), 2, 'right result'; 26 | }; 27 | 28 | subtest 'Bigger cache' => sub { 29 | my $cache = Mojo::Cache->new(max_keys => 3); 30 | is $cache->get('foo'), undef, 'no result'; 31 | is $cache->set(foo => 'bar')->get('foo'), 'bar', 'right result'; 32 | $cache->set(bar => 'baz'); 33 | is $cache->get('foo'), 'bar', 'right result'; 34 | is $cache->get('bar'), 'baz', 'right result'; 35 | $cache->set(baz => 'yada'); 36 | is $cache->get('foo'), 'bar', 'right result'; 37 | is $cache->get('bar'), 'baz', 'right result'; 38 | is $cache->get('baz'), 'yada', 'right result'; 39 | $cache->set(yada => 23); 40 | is $cache->get('foo'), undef, 'no result'; 41 | is $cache->get('bar'), 'baz', 'right result'; 42 | is $cache->get('baz'), 'yada', 'right result'; 43 | is $cache->get('yada'), 23, 'right result'; 44 | }; 45 | 46 | subtest 'Cache disabled' => sub { 47 | my $cache = Mojo::Cache->new(max_keys => 0); 48 | is $cache->get('foo'), undef, 'no result'; 49 | is $cache->set(foo => 'bar')->get('foo'), undef, 'no result'; 50 | 51 | $cache = Mojo::Cache->new(max_keys => -1); 52 | is $cache->get('foo'), undef, 'no result'; 53 | $cache->set(foo => 'bar'); 54 | is $cache->get('foo'), undef, 'no result'; 55 | }; 56 | 57 | done_testing(); 58 | -------------------------------------------------------------------------------- /t/mojo/certs/bad.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICMDCCAZmgAwIBAgIJAL3i8FjIa505MA0GCSqGSIb3DQEBBQUAMBsxCzAJBgNV 3 | BAYTAlVTMQwwCgYDVQQDEwNiYWQwHhcNMTQxMjEyMDUwNDA3WhcNMzQxMjA3MDUw 4 | NDA3WjAbMQswCQYDVQQGEwJVUzEMMAoGA1UEAxMDYmFkMIGfMA0GCSqGSIb3DQEB 5 | AQUAA4GNADCBiQKBgQDa3Y/PG8MEfLboJQ2pPKOTgXLCBDPPPii8L3AYMaqo6b9M 6 | S4ofj0qE6uiAne4ZMOoXl18U/9isZ5V8DKYTiNBDwTYyl9r6n+z+4uyINfxPhwhm 7 | q3eYK2/WRRNgGkaBX1Jn+woSQNj/Xq9hxu0oy5oUez+vDtg5vfgvQHZX48igbQID 8 | AQABo3wwejAdBgNVHQ4EFgQUG+9Mb16GLXdUYxAkv5T0VNIXAMgwSwYDVR0jBEQw 9 | QoAUG+9Mb16GLXdUYxAkv5T0VNIXAMihH6QdMBsxCzAJBgNVBAYTAlVTMQwwCgYD 10 | VQQDEwNiYWSCCQC94vBYyGudOTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA 11 | A4GBALxX+nkjDBh+Ir6QqBcdhiZ8i4rBhLUHaNDUFKq18WoxpTOtgwh7LUf+gT3D 12 | XeelteXWgd4Ta7+emaAMS0+lSceJGHJJGYqcgHecIrKWtBAXL3z5K1YTZJb6jpCb 13 | nAdXvj3b84/6AkrguWUeh/S0VMUtI11L984xCPbNizvnepAV 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /t/mojo/certs/bad.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDa3Y/PG8MEfLboJQ2pPKOTgXLCBDPPPii8L3AYMaqo6b9MS4of 3 | j0qE6uiAne4ZMOoXl18U/9isZ5V8DKYTiNBDwTYyl9r6n+z+4uyINfxPhwhmq3eY 4 | K2/WRRNgGkaBX1Jn+woSQNj/Xq9hxu0oy5oUez+vDtg5vfgvQHZX48igbQIDAQAB 5 | AoGAJuHz2YyKEfMCH63thmJygRMfSx6qAah+XihjVrkEI6wbQzesWzz+tSxyj97+ 6 | 3NTW0cgfqUBmwwJICVjJ7HCC5yJQMvgAVv8MdPjuabZOEVthhHJJKe9CtVlkQC+F 7 | 5gybqf7m5EIWosd9ROjhBes4FaPLDG3ujOO27KEzY9CJmpUCQQD9aAn8qe7TVXnj 8 | on9q6lu5aNB1lQ1G2h1yOkZDauY1TDec8/Vk6MjLmWIKu0GAATQAnGohNPGxVwZP 9 | WdchXbAHAkEA3RsFRJkigPHudmimE2RH5Tf8UxfE89R7lhsBs7hTXSxeLUx2U5iv 10 | G2g5KcKKnOJ+di5hB1dzW9q8zgTYGGEm6wJBAJqNcMHkVuAYGt5GRpZL16OulK2S 11 | OeXDs//uINqPgVZyZNzaQgnInGbo5s4KxXkvwqq4u1YDd4GkxRlyfu4/uNMCQEST 12 | KtEZxY/EeTcUQd3lzj6TXJjQ2G7fSe1GNwLsej59kj/uZQyMYFLZwlDnW2JJBPvW 13 | apWCU+77Km+jJPp6tz0CQQDleMOZi70ikb04E0kagpJiRtNOLThDKjY6r6ZZWPro 14 | saqxw29J/bAReUYXAcW5EzwRG97R/2u1HY77Y43YA3v0 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /t/mojo/certs/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICLTCCAZagAwIBAgIJALx7JaBkhvU1MA0GCSqGSIb3DQEBBQUAMBoxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQDEwJjYTAeFw0xNDEyMTIwNTAzMDFaFw0zNDEyMDcwNTAz 4 | MDFaMBoxCzAJBgNVBAYTAlVTMQswCQYDVQQDEwJjYTCBnzANBgkqhkiG9w0BAQEF 5 | AAOBjQAwgYkCgYEAozbnJr3IvXwQeQV7n63p8XgXSpLgGmOsldyyIOBNy9zhoN7e 6 | ZFvzNysr80+4a2+o3vRzXptGYuFjocBRruWm7Z42HLOKiiI/X317whfvm4dqsa9s 7 | Bo4WN+2Ptsz31X+v/XnD6oZS5nLCeQ/WNuzd6Am9V0Y6Z//NReGOA3lotPkCAwEA 8 | AaN7MHkwHQYDVR0OBBYEFPW1qzZDKPG4QYd7GN8rHWpVCdaLMEoGA1UdIwRDMEGA 9 | FPW1qzZDKPG4QYd7GN8rHWpVCdaLoR6kHDAaMQswCQYDVQQGEwJVUzELMAkGA1UE 10 | AxMCY2GCCQC8eyWgZIb1NTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB 11 | ABHz4htmRT6rG6tcSZ4dfpFZpd0Njj2pikkeymDK6s7vmfKVdY7NK0kisoHG88ms 12 | y2mzRwbUSshuYfjghqZdizwf5bdprNSHRPfeey44pNTV8aVeKK13IxHGZk49hWHX 13 | cq59GdoKy+AX9n0NI58u2GUrdwmM7Q8Qo8gVvfY19NFW 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /t/mojo/certs/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQCjNucmvci9fBB5BXufrenxeBdKkuAaY6yV3LIg4E3L3OGg3t5k 3 | W/M3KyvzT7hrb6je9HNem0Zi4WOhwFGu5abtnjYcs4qKIj9ffXvCF++bh2qxr2wG 4 | jhY37Y+2zPfVf6/9ecPqhlLmcsJ5D9Y27N3oCb1XRjpn/81F4Y4DeWi0+QIDAQAB 5 | AoGAD3Sdgv+pyLe835jzUfWoF6OloPwzmIwjxbU2cQKMm2d5PkCckqhaabj+2nWM 6 | IzBZr3M6kZTmn2p0gtaxZXx9yA/iSc/H5agWtmP2L5+XLVFY2U+GlLtlF8BPCzf8 7 | GoUHfq4phHyJ0tqzduVxO6p9UgdLhud0E07KuiF/lw0a5uECQQDOSIoMezNkOKkt 8 | Ilza9RZR4R2S1NB23RfASddKyQK91sXC7uBiKvQO+GWzlJnbpTOEjpxTi0zMaYBS 9 | 9OpODiCVAkEAyo0POddC8VOVSSXZfbVuATmbr7Gu9JR9Qy9zzXObrrUtQDAX/8Kp 10 | MKAujFlT+6XqM/AFfP9ZH4FvFj2qLVX11QJAX/hLkYb339akjoUAIjYIDkvnUFqG 11 | KeaumB1Cdl6SUfPLyecMqV9GcHiMCEJIWnG/SBp5DD0wm6ExvGaJY4sbCQJBAJNE 12 | Bfr24GwXaiX5x+yXGbj4SpJuLJcU0xIjER4iXOGkRpcxoHFK9bot7EoMtHv1gJds 13 | foEIIqM+dycDhJRbuFUCQQCRnWBGk+Ess3FIilnfS0wfHzuY0DvMgyiF+qu+EWuD 14 | 254B3GxDGf2PMDa59DSY+zIzj/vfIhQqYdjiJbJpoXGK 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /t/mojo/certs/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBsjCCARsCCQCptEBZlSnk3zANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV 3 | UzELMAkGA1UEAxMCY2EwHhcNMTQxMjEyMDUwMzQ5WhcNMzQxMjA3MDUwMzQ5WjAh 4 | MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJMTI3LjAuMC4xMIGfMA0GCSqGSIb3DQEB 5 | AQUAA4GNADCBiQKBgQC74ZFnrU3GHG/XdPih2bdMLXKlKL86dTsdt5y4dWHP2yFS 6 | 2hyDNC8Oy5qlrL5GjbTbqpX65IkjYa8IfWbvn036DofU2vCYnQQZW25Egmrek+K1 7 | J3hR8sxHyOFsGBcV7UDgzuQD8UtDzfalQ0DvmqTUgNl00gT3B8tV/eHmL8XPlQID 8 | AQABMA0GCSqGSIb3DQEBBQUAA4GBAHZ/un7lY0QzL7NSpF4xr5ThtNQyxkGDb63B 9 | etmaRrmTyH1dhZ7haXrYC12biE9r5H4mMo01SdOIkwpbN3zabGxKJM5bB627hXW3 10 | +DAsS/a3riKU5lQHByADwXhblbFFOMEUiDqb80ukhemRtE3fE+qV9jeLYhc/EO9c 11 | nmX6gNkZ 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /t/mojo/certs/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQC74ZFnrU3GHG/XdPih2bdMLXKlKL86dTsdt5y4dWHP2yFS2hyD 3 | NC8Oy5qlrL5GjbTbqpX65IkjYa8IfWbvn036DofU2vCYnQQZW25Egmrek+K1J3hR 4 | 8sxHyOFsGBcV7UDgzuQD8UtDzfalQ0DvmqTUgNl00gT3B8tV/eHmL8XPlQIDAQAB 5 | AoGAaeEbtWa4Dq5V5QD5YRS1tItkMWk+Iy6PqxafjmAwSBgAtpVOPi55WogpVbZ8 6 | 4HvZcXW34dVK55KUNl+dU+rd4NaDpka8RBjY99c1sJz7CmtPh9G3HHvcq0LIHZ+o 7 | zDDLOwY5501czxJ+H6nwf3tmiZlGVqd7D4UQsG3ZWSBdVH0CQQDv2zYFX3H2sMZV 8 | ntLvL00XizzNinQTK5Znnjpb4ZpAQFJcGTZa0jahc/8cTQRqcGfJATg4lLEcs+Jy 9 | C1Kroe+zAkEAyIbPi8ONBRJA+pWoPvSRBNRhmboWVJ9424t8mLzXhwyo+NOYb3f8 10 | 6Rm2YNfkUB/DEJTwhxmRPeL2fCPT4HpflwJAc805KqTnJ7w3NXVFz7bdmIVf6Lh0 11 | zCCaMBLDiELGr0ieutptVLHzMEYJoAdLH3x/jxsooqCfVgU/SeJPmo/HYwJARaPL 12 | 4FYBW4mDV4Mx8usskejQHHsr1ier6VL/6mtqzlPvOrsBbXTWOH3dgLR5bfoZ3GBd 13 | SA3xCvUPdP1cT9Ev8QJBANIsQHxb4VsyRvz5La0oNwrjzp9BrV8t7xf4Sz46T/6m 14 | uVl7tyb/M2x6qpYmz1u9D3hUDSysssd0eJbtCbfuU/0= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /t/mojo/certs/domain.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBtDCCAR0CCQCEuM5/riCs5zANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV 3 | UzELMAkGA1UEAxMCY2EwHhcNMTYwMTA3MTY0MjE0WhcNMzYwMTAyMTY0MjE0WjAj 4 | MQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcN 5 | AQEBBQADgY0AMIGJAoGBAMQc4F687m9y9Wab9YIPfYDeIN9o+wut8P0uVvCgyGny 6 | TtbQNmT6iaeJBm8xemElV5G67CF/aR6mHoJOXpxD3Xym9qro4qbfubHeDJZS93OB 7 | JS/WoMYyfwES/E0GODONM/QDrwUanB8Arx88hxU9+8ARuVY+ML7ZgMllO6u9lA0T 8 | AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEANS5lefxEri0D4cfFN3Q5EhFKqzVQkwuy 9 | RtvItyH1GdLSSjHwFcpnUj4y2luR+VmLJW2QEFw/mnKzCUxX9bp8bZITYcchIJNH 10 | fJ+8MaoyxhlVrv0E5d6jiwOGZoBMYOTf7UAkfFkkLPGbWFVBur5mXaQ68SBgo6Cf 11 | Pl2WcT5ghyM= 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /t/mojo/certs/domain.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDEHOBevO5vcvVmm/WCD32A3iDfaPsLrfD9LlbwoMhp8k7W0DZk 3 | +omniQZvMXphJVeRuuwhf2keph6CTl6cQ918pvaq6OKm37mx3gyWUvdzgSUv1qDG 4 | Mn8BEvxNBjgzjTP0A68FGpwfAK8fPIcVPfvAEblWPjC+2YDJZTurvZQNEwIDAQAB 5 | AoGBALujaWYb3QLTektX83oiuhE/9zHrLzBImCiWWubW34rHJXnRNAo/0M90gqyH 6 | KbGjWfr8XwvZ7Uk+5jgFJe7b3CDGTUh7yvuHuypwJg/wmqnBlfj0S4c8lC8LB4+6 7 | 199D+lkfZyJKbBoIdHjCcB1RpZkb7DN3d9ylwRWA0OEWXwl5AkEA+GuhfS3C/627 8 | 7ax3ojQsyqmJKqzloxU34YxWw+R14dZOClF/tPwOsgbzy6Cvihq5azbWHCcZ06MI 9 | DAfs0s+CVQJBAMoYrgG4XPVXxR0fdpclY1S1r+4RixgswawNTXdPCpcnhlfRy4ej 10 | 03UwyxeA7M4pxmVteTbbjjopVecamOgtyccCQChEUf3XcBc/kwm4ff/V0zjaeDhp 11 | pCNmKhOuStYf7xe3RBkaEshEXyFuTRBBsJKDOHDvh48yq1YJxCEnG7UkG60CQAGn 12 | 7B0VfqV//5x6eoVIiCTUjEl+GU6sZzXasgzNN///Eem8TVeiLwRhzvg1VTtnOjnw 13 | iLK7X9H4Lr0DCce1QFcCQCZseRu73kcYpIAJjPlgYCco6H3lObT4fekB3XRA2MRU 14 | HP/9DewejMCAHgttD91mJ5BPY5xB8pfsJMUz2pC0FMg= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /t/mojo/certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBsjCCARsCCQCptEBZlSnk3jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV 3 | UzELMAkGA1UEAxMCY2EwHhcNMTQxMjEyMDUwMzI1WhcNMzQxMjA3MDUwMzI1WjAh 4 | MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJMTI3LjAuMC4xMIGfMA0GCSqGSIb3DQEB 5 | AQUAA4GNADCBiQKBgQDDhbj7nsfzahPilwn6pGdo6nKYCR21WZ73CuwPN86DmsZi 6 | 5LIRYRfKA0unape2BQBnMnSmInaXvHHBdVsTyt3XSFZj5+iCF9RcorXAqcDygScj 7 | 8MTWYAZxCu3lGAjtw0bGGYutlLg5jtEXvZwfe61XfJj9xDUPNQrP7mf/HTBmgQID 8 | AQABMA0GCSqGSIb3DQEBBQUAA4GBACRIx9fB4x8UO44C9TGj3bKb1NX3bkuHMz0m 9 | WdhCkzUUiANtRMxp2oLA3KHY4yOusZLZIUNyP10Ri5q/U1mR0poYCMm7AYee2OV7 10 | NdQIyppeDLoWQ9uPISPjp1d+zjpGOrLrSkpD1rYLVw4R56A9ZQks/LNs6TSceZjZ 11 | c5QST/9i 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /t/mojo/certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDDhbj7nsfzahPilwn6pGdo6nKYCR21WZ73CuwPN86DmsZi5LIR 3 | YRfKA0unape2BQBnMnSmInaXvHHBdVsTyt3XSFZj5+iCF9RcorXAqcDygScj8MTW 4 | YAZxCu3lGAjtw0bGGYutlLg5jtEXvZwfe61XfJj9xDUPNQrP7mf/HTBmgQIDAQAB 5 | AoGBAKqQ+XLobV435+kAAkCZM20fOaDQMw6FhFvXN19/O7UrTG3xPDZVET+16EXA 6 | g2DStCtSpNhZsENmrYYrLqzxiNxDHagmEizmicdjoU2sXCHUbLtSojwedGTixBNf 7 | ofuDPi/j7T24CQFK34ROwA4diMVsD8YBLOv+jXZMXaq1ROwBAkEA6RfoF2P9r7MW 8 | Q5Yq54eZdU6viQmHXV8ePe43NmcEXGUMKb049RHekCWggvAgcTz3bzLIP4v6SQxz 9 | J17wezU7sQJBANa8nYGOX29/TF2LS4vpAf85jPa+MqeFVOHXunlZbibXwPvx6WWS 10 | EwwimDX6Q0q+dA4XqSDrb622BED5A18fG9ECQHOT1xFCE1g7hJ6ep0eGhSEVkxRw 11 | FR7HO0eaBuaCzjbCI/XOTZ+27JStE5nZVzzO7iHFHGXmEoSGF9M90CBHlQECQH/0 12 | Ffpb5pweg1d+J/7vUcRN+6QPQ0m01hZYoIGse0lj6Fd4F3Xxa64gcwg+3pgMEkVP 13 | bfTytTISMkiw2IR2mfECQE343mUbDSOGGoDAN5ByWydkwfqaJvNBCHaBAkJ6lthU 14 | 4yVXQabDGkZMh1jdxi8hK/TRLiNgGks8tgFp6Zqc0SU= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /t/mojo/cookies/curl.txt: -------------------------------------------------------------------------------- 1 | # Netscape HTTP Cookie File 2 | # https://curl.se/docs/http-cookies.html 3 | # This file was generated by libcurl! Edit at your own risk. 4 | 5 | www.suse.com FALSE / FALSE 0 susecom-cookie 50fbf56aa575290e 6 | .reddit.com TRUE / TRUE 4761486052 csv 2 7 | .reddit.com TRUE / TRUE 0 csrf_token 3329d93c563f6a017045f516c5c515fc 8 | #HttpOnly_.google.com TRUE / TRUE 4713964099 AEC Ack 9 | #HttpOnly_.google.com TRUE / TRUE 4732598797 __Secure-ENID 15.SE 10 | .whatever.youtube.com TRUE /about TRUE 4761484436 CONSENT PENDING+648 11 | -------------------------------------------------------------------------------- /t/mojo/daemon_ipv6_tls.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::IOLoop::TLS; 7 | 8 | use Mojo::File qw(curfile); 9 | use lib curfile->sibling('lib')->to_string; 10 | 11 | plan skip_all => 'set TEST_IPV6 to enable this test (developer only!)' unless $ENV{TEST_IPV6} || $ENV{TEST_ALL}; 12 | plan skip_all => 'set TEST_TLS to enable this test (developer only!)' unless $ENV{TEST_TLS} || $ENV{TEST_ALL}; 13 | plan skip_all => 'IO::Socket::SSL 2.009+ required for this test!' unless Mojo::IOLoop::TLS->can_tls; 14 | 15 | # To regenerate all required certificates run these commands (07.01.2016) 16 | # openssl genrsa -out domain.key 1024 17 | # openssl req -new -key domain.key -out domain.csr -subj "/C=US/CN=example.com" 18 | # openssl x509 -req -days 7300 -in domain.csr -out domain.crt -CA ca.crt \ 19 | # -CAkey ca.key -CAcreateserial 20 | use Mojo::IOLoop; 21 | use Mojo::Server::Daemon; 22 | use Mojo::TestConnectProxy; 23 | use Mojo::UserAgent; 24 | use Mojolicious::Lite; 25 | 26 | # Silence 27 | app->log->level('fatal'); 28 | 29 | get '/' => {text => 'works!'}; 30 | 31 | # IPv6 and TLS 32 | my $daemon = Mojo::Server::Daemon->new(app => app, listen => ['https://[::1]'], silent => 1); 33 | my $port = $daemon->start->ports->[0]; 34 | my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton, insecure => 1); 35 | my $tx = $ua->get("https://[::1]:$port/"); 36 | is $tx->res->code, 200, 'right status'; 37 | is $tx->res->body, 'works!', 'right content'; 38 | 39 | subtest 'IPv6, TLS, SNI and a proxy' => sub { 40 | plan skip_all => 'SNI support required!' unless IO::Socket::SSL->can_client_sni && IO::Socket::SSL->can_server_sni; 41 | $daemon = Mojo::Server::Daemon->new(app => app, silent => 1); 42 | my $listen 43 | = 'https://[::1]' 44 | . '?127.0.0.1_cert=t/mojo/certs/server.crt' 45 | . '&127.0.0.1_key=t/mojo/certs/server.key' 46 | . '&example.com_cert=t/mojo/certs/domain.crt' 47 | . '&example.com_key=t/mojo/certs/domain.key'; 48 | my $forward = $daemon->listen([$listen])->start->ports->[0]; 49 | my $id = Mojo::TestConnectProxy::proxy({address => '[::1]'}, {address => '[::1]', port => $forward}); 50 | my $proxy = Mojo::IOLoop->acceptor($id)->port; 51 | $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton, ca => 't/mojo/certs/ca.crt'); 52 | $ua->proxy->https("http://[::1]:$proxy"); 53 | $tx = $ua->get("https://example.com/"); 54 | is $tx->res->code, 200, 'right status'; 55 | is $tx->res->body, 'works!', 'right content'; 56 | ok !$tx->error, 'no error'; 57 | $tx = $ua->get("https://127.0.0.1/"); 58 | is $tx->res->code, 200, 'right status'; 59 | is $tx->res->body, 'works!', 'right content'; 60 | ok !$tx->error, 'no error'; 61 | $tx = $ua->get("https://has.no.cert/"); 62 | like $tx->error->{message}, qr/name/, 'right error'; 63 | }; 64 | 65 | done_testing(); 66 | -------------------------------------------------------------------------------- /t/mojo/dynamic_methods.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | package Mojo::TestDynamic; 6 | use Mojo::Base -base; 7 | 8 | use Mojo::DynamicMethods -dispatch; 9 | 10 | has hashref => sub { {} }; 11 | 12 | sub BUILD_DYNAMIC { 13 | my ($class, $method, $dyn_methods) = @_; 14 | 15 | return sub { 16 | my $self = shift; 17 | my $dynamic = $dyn_methods->{$self->hashref}{$method}; 18 | return $self->$dynamic($dyn_methods) if $dynamic; 19 | my $package = ref $self; 20 | Carp::croak qq{Can't locate object method "$method" via package "$package"}; 21 | }; 22 | } 23 | 24 | package main; 25 | 26 | my ($t1, $t2) = (Mojo::TestDynamic->new, Mojo::TestDynamic->new); 27 | my ($dyn_methods); 28 | 29 | subtest 'Basics' => sub { 30 | Mojo::DynamicMethods::register 'Mojo::TestDynamic', $t1->hashref, 'foo', sub { }; 31 | my $foo = \&Mojo::TestDynamic::_Dynamic::foo; 32 | my ($called_foo, $dyn_methods); 33 | Mojo::DynamicMethods::register 'Mojo::TestDynamic', $t1->hashref, 'foo', sub { $called_foo++; $dyn_methods = $_[1] }; 34 | is $foo, \&Mojo::TestDynamic::_Dynamic::foo, 'foo not reinstalled'; 35 | ok !Mojo::TestDynamic->can('foo'), 'dynamic method is hidden'; 36 | ok eval { $t1->foo; 1 }, 'foo called ok'; 37 | cmp_ok $called_foo, '==', 1, 'called dynamic method'; 38 | ok !eval { $t2->foo; 1 }, 'error calling foo on wrong object'; 39 | }; 40 | 41 | subtest 'Garbage collection' => sub { 42 | undef($t1); 43 | undef($t2); 44 | ok(!keys(%$dyn_methods), 'dynamic methods expired'); 45 | }; 46 | 47 | done_testing; 48 | -------------------------------------------------------------------------------- /t/mojo/exception/non_utf8.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/t/mojo/exception/non_utf8.txt -------------------------------------------------------------------------------- /t/mojo/exception/utf8.txt: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | 5 | my $s = 'Über•résumé'; 6 | -------------------------------------------------------------------------------- /t/mojo/home.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::File qw(curfile path); 7 | use Mojo::HelloWorld; 8 | use Mojo::Home; 9 | 10 | subtest 'ENV detection' => sub { 11 | my $fake = path->to_abs->child('does_not_exist'); 12 | local $ENV{MOJO_HOME} = $fake->to_string; 13 | my $home = Mojo::Home->new->detect; 14 | is_deeply $home->to_array, $fake->to_array, 'right path detected'; 15 | }; 16 | 17 | subtest 'Specific class detection' => sub { 18 | my $fake = path->to_abs->child('does_not_exist_2'); 19 | local $INC{'My/Class.pm'} = $fake->child('My', 'Class.pm')->to_string; 20 | my $home = Mojo::Home->new->detect('My::Class'); 21 | is_deeply $home->to_array, $fake->to_array, 'right path detected'; 22 | }; 23 | 24 | subtest 'Specific class detection (with "lib")' => sub { 25 | my $fake = path->to_abs->child('does_not_exist_3'); 26 | local $INC{'My/Class.pm'} = $fake->child('lib', 'My', 'Class.pm')->to_string; 27 | my $home = Mojo::Home->new->detect('My::Class'); 28 | is_deeply $home->to_array, $fake->to_array, 'right path detected'; 29 | }; 30 | 31 | subtest 'Specific class detection (with "blib/lib")' => sub { 32 | my $fake = path->to_abs->child('does_not_exist_3'); 33 | local $INC{'My/Class.pm'} = $fake->child('blib', 'lib', 'My', 'Class.pm')->to_string; 34 | my $home = Mojo::Home->new->detect('My::Class'); 35 | is_deeply $home->to_array, $fake->to_array, 'right path detected'; 36 | }; 37 | 38 | subtest 'Specific class detection (relative)' => sub { 39 | local $INC{'My/Class.pm'} = path('My', 'Class.pm')->to_string; 40 | my $home = Mojo::Home->new->detect('My::Class'); 41 | is_deeply $home->to_array, path->to_array, 'right path detected'; 42 | }; 43 | 44 | subtest 'Specific class detection (relative "blib/lib")' => sub { 45 | local $INC{'My/Class.pm'} = path('blib', 'lib', 'My', 'Class.pm')->to_string; 46 | my $home = Mojo::Home->new->detect('My::Class'); 47 | is_deeply $home->to_array, path->to_array, 'right path detected'; 48 | }; 49 | 50 | subtest 'Current working directory' => sub { 51 | my $home = Mojo::Home->new->detect; 52 | is_deeply $home->to_array, path->to_abs->to_array, 'right path detected'; 53 | }; 54 | 55 | subtest 'Path generation' => sub { 56 | my $home = Mojo::Home->new(curfile->dirname); 57 | my $path = curfile->dirname; 58 | is $home->rel_file('foo.txt'), $path->child('foo.txt'), 'right path'; 59 | is $home->rel_file('foo/bar.txt'), $path->child('foo', 'bar.txt'), 'right path'; 60 | is $home->rel_file('foo/bar.txt')->basename, 'bar.txt', 'right result'; 61 | }; 62 | 63 | done_testing(); 64 | -------------------------------------------------------------------------------- /t/mojo/ioloop_ipv6.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::IOLoop::Server; 7 | use Mojo::Promise; 8 | 9 | plan skip_all => 'set TEST_IPV6 to enable this test (developer only!)' unless $ENV{TEST_IPV6} || $ENV{TEST_ALL}; 10 | 11 | use Mojo::IOLoop; 12 | 13 | subtest 'IPv6 roundtrip' => sub { 14 | my ($server, $client); 15 | my $promise = Mojo::Promise->new; 16 | my $id = Mojo::IOLoop->server( 17 | {address => '[::1]'} => sub { 18 | my ($loop, $stream) = @_; 19 | $stream->write('test' => sub { shift->write('321') }); 20 | $stream->on(close => sub { $promise->resolve }); 21 | $stream->on(read => sub { $server .= pop }); 22 | } 23 | ); 24 | my $port = Mojo::IOLoop->acceptor($id)->port; 25 | my $promise2 = Mojo::Promise->new; 26 | Mojo::IOLoop->client( 27 | {address => '[::1]', port => $port} => sub { 28 | my ($loop, $err, $stream) = @_; 29 | $stream->write('tset' => sub { shift->write('123') }); 30 | $stream->on(close => sub { $promise2->resolve }); 31 | $stream->on(read => sub { $client .= pop }); 32 | $stream->timeout(0.5); 33 | } 34 | ); 35 | Mojo::Promise->all($promise, $promise2)->wait; 36 | is $server, 'tset123', 'right content'; 37 | is $client, 'test321', 'right content'; 38 | }; 39 | 40 | done_testing(); 41 | -------------------------------------------------------------------------------- /t/mojo/json_xs.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Mojo::JSON qw(decode_json encode_json false from_json j to_json true); 5 | 6 | BEGIN { 7 | plan skip_all => 'Cpanel::JSON::XS 4.09+ required for this test!' unless Mojo::JSON->JSON_XS; 8 | } 9 | 10 | package JSONTest; 11 | use Mojo::Base -base; 12 | 13 | has 'something' => sub { {} }; 14 | 15 | sub TO_JSON { shift->something } 16 | 17 | package main; 18 | 19 | use Mojo::ByteStream; 20 | use Mojo::Util qw(decode encode); 21 | 22 | subtest 'Basics' => sub { 23 | my $array = decode_json '[]'; 24 | is_deeply $array, [], 'decode_json'; 25 | my $bytes = encode_json []; 26 | is $bytes, '[]', 'encode_json'; 27 | $array = from_json '[]'; 28 | is_deeply $array, [], 'from_json'; 29 | my $chars = to_json []; 30 | is $chars, '[]', 'to_json'; 31 | $array = j('[]'); 32 | is_deeply $array, [], 'j() decode'; 33 | $bytes = j([]); 34 | is $bytes, '[]', 'j() encode'; 35 | is encode_json([true]), '[true]', 'true'; 36 | is encode_json([false]), '[false]', 'false'; 37 | }; 38 | 39 | subtest '"utf8"' => sub { 40 | is_deeply decode_json(encode('UTF-8', '["♥"]')), ['♥'], 'bytes decoded'; 41 | is encode_json(['♥']), encode('UTF-8', '["♥"]'), 'bytes encoded'; 42 | is_deeply from_json('["♥"]'), ['♥'], 'characters decoded'; 43 | is to_json(['♥']), '["♥"]', 'characters encoded'; 44 | }; 45 | 46 | subtest '"canonical"' => sub { 47 | is_deeply encode_json({a => 1, b => 2, c => 3}), '{"a":1,"b":2,"c":3}', 'canonical object'; 48 | }; 49 | 50 | subtest '"allow_nonref"' => sub { 51 | is_deeply encode_json(true), 'true', 'bare true'; 52 | }; 53 | 54 | subtest '"allow_unknown"' => sub { 55 | is_deeply encode_json(sub { }), 'null', 'unknown reference'; 56 | }; 57 | 58 | subtest '"allow_blessed"' => sub { 59 | is_deeply encode_json(Mojo::ByteStream->new('test')), '"test"', 'blessed reference'; 60 | }; 61 | 62 | subtest '"convert_blessed"' => sub { 63 | my $bytes = encode_json(JSONTest->new); 64 | is_deeply decode_json($bytes), {}, 'successful roundtrip'; 65 | $bytes = encode_json(JSONTest->new(something => {just => 'works'}, else => {not => 'working'})); 66 | is_deeply decode_json($bytes), {just => 'works'}, 'successful roundtrip'; 67 | }; 68 | 69 | subtest '"stringify_infnan"' => sub { 70 | like encode_json({test => 9**9**9}), qr/^{"test":".*"}$/, 'encode "inf" as string'; 71 | like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/, 'encode "nan" as string'; 72 | }; 73 | 74 | subtest '"escape_slash"' => sub { 75 | is_deeply encode_json('/test/123'), '"\/test\/123"', 'escaped slash'; 76 | }; 77 | 78 | subtest '"allow_dupkeys"' => sub { 79 | is_deeply decode_json('{"test":1,"test":2}'), {test => 2}, 'no duplicate keys error'; 80 | }; 81 | 82 | done_testing(); 83 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/.hidden.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/t/mojo/lib/Mojo/.hidden.txt -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/.test/hidden.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/t/mojo/lib/Mojo/.test/hidden.txt -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/BaseTest/Base1.pm: -------------------------------------------------------------------------------- 1 | package Mojo::BaseTest::Base1; 2 | use Mojo::Base -base; 3 | 4 | has 'foo'; 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/BaseTest/Base2.pm: -------------------------------------------------------------------------------- 1 | package Mojo::BaseTest::Base2; 2 | use Mojo::Base 'Mojo::BaseTest::Base1'; 3 | 4 | has [qw(bar baz)] => sub {2}; 5 | has yada => 0; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/BaseTest/Base3.pm: -------------------------------------------------------------------------------- 1 | package Mojo::BaseTest::Base3; 2 | use Mojo::BaseTest::Base1 -base; 3 | 4 | has 'test'; 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/DeprecationTest.pm: -------------------------------------------------------------------------------- 1 | package Mojo::DeprecationTest; 2 | 3 | use Mojo::Util qw(deprecated); 4 | 5 | sub foo { 6 | deprecated 'foo is DEPRECATED'; 7 | return 'bar'; 8 | } 9 | 10 | 1; 11 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderException.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderException; 2 | 3 | use Mojo::Base -base; 4 | 5 | sub new { } 6 | 7 | foo { 8 | 9 | 1; 10 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderException2.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderException2; 2 | use Mojo::Base -strict; 3 | 4 | Mojo::LoaderException2_2::throw_error(); 5 | 6 | 1; 7 | 8 | package Mojo::LoaderException2_2; 9 | 10 | use Carp qw(croak); 11 | 12 | sub throw_error { 13 | eval { Mojo::LoaderException2_3::throw_error() }; 14 | croak $@ if $@; 15 | } 16 | 17 | package Mojo::LoaderException2_3; 18 | 19 | use Carp qw(croak); 20 | 21 | sub throw_error { 22 | croak "Exception"; 23 | } 24 | 25 | 1; 26 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/A.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderTest::A; 2 | use Mojo::Base -base; 3 | 4 | 1; 5 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/B.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderTest::B; 2 | use Mojo::Base -base; 3 | 4 | 1; 5 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/C.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderTest::C; 2 | use Mojo::Base -base; 3 | 4 | 1; 5 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/D.txt: -------------------------------------------------------------------------------- 1 | This is not a module. 2 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/E/F.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderTest::E::F; 2 | use Mojo::Base -base; 3 | 4 | 1; 5 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTest/E/G.txt: -------------------------------------------------------------------------------- 1 | Also not a module. 2 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/LoaderTestException/A.pm: -------------------------------------------------------------------------------- 1 | package Mojo::LoaderException::A; 2 | use Mojo::Base -base; 3 | 4 | sub new { } 5 | 6 | foo { 7 | 8 | 1; 9 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/Server/Morbo/Backend/TestBackend.pm: -------------------------------------------------------------------------------- 1 | package Mojo::Server::Morbo::Backend::TestBackend; 2 | use Mojo::Base 'Mojo::Server::Morbo::Backend'; 3 | 4 | sub modified_files { return ['always_changed'] } 5 | 6 | 1 7 | -------------------------------------------------------------------------------- /t/mojo/lib/Mojo/TestConnectProxy.pm: -------------------------------------------------------------------------------- 1 | package Mojo::TestConnectProxy; 2 | use Mojo::Base -strict; 3 | 4 | use Mojo::IOLoop; 5 | 6 | # CONNECT proxy server for testing 7 | sub proxy { 8 | my ($from, $to, $ok, $zero) = @_; 9 | 10 | $ok ||= "HTTP/1.1 200 OK\x0d\x0aServer: Test 1.0\x0d\x0a\x0d\x0a"; 11 | $zero ||= "HTTP/1.1 404 NOT FOUND\x0d\x0aContent-Length: 20\x0d\x0a" 12 | . "Connection: close\x0d\x0a\x0d\x0aSomething went wrong"; 13 | 14 | my %buffer; 15 | return Mojo::IOLoop->server( 16 | $from => sub { 17 | my ($loop, $stream, $id) = @_; 18 | 19 | # Connection to client 20 | $stream->on( 21 | read => sub { 22 | my ($stream, $chunk) = @_; 23 | 24 | # Write chunk from client to server 25 | my $server = $buffer{$id}{connection}; 26 | return Mojo::IOLoop->stream($server)->write($chunk) if $server; 27 | 28 | # Read connect request from client 29 | my $buffer = $buffer{$id}{client} .= $chunk; 30 | if ($buffer =~ /\x0d?\x0a\x0d?\x0a$/) { 31 | $buffer{$id}{client} = ''; 32 | if ($buffer =~ /CONNECT \S+:(\d+)/) { 33 | 34 | return Mojo::IOLoop->stream($id)->write($zero) if $1 == 0; 35 | 36 | # Connection to server 37 | $buffer{$id}{connection} = Mojo::IOLoop->client( 38 | $to => sub { 39 | my ($loop, $err, $stream) = @_; 40 | 41 | # Connection to server failed 42 | if ($err) { 43 | Mojo::IOLoop->remove($id); 44 | return delete $buffer{$id}; 45 | } 46 | 47 | # Start forwarding data in both directions 48 | Mojo::IOLoop->stream($id)->write($ok); 49 | $stream->on( 50 | read => sub { 51 | my ($stream, $chunk) = @_; 52 | Mojo::IOLoop->stream($id)->write($chunk); 53 | } 54 | ); 55 | 56 | # Server closed connection 57 | $stream->on( 58 | close => sub { 59 | Mojo::IOLoop->remove($id); 60 | delete $buffer{$id}; 61 | } 62 | ); 63 | } 64 | ); 65 | } 66 | 67 | # Invalid request from client 68 | else { Mojo::IOLoop->remove($id) } 69 | } 70 | } 71 | ); 72 | 73 | # Client closed connection 74 | $stream->on( 75 | close => sub { 76 | my $buffer = delete $buffer{$id}; 77 | Mojo::IOLoop->remove($buffer->{connection}) if $buffer->{connection}; 78 | } 79 | ); 80 | } 81 | ); 82 | } 83 | 84 | 1; 85 | -------------------------------------------------------------------------------- /t/mojo/lib/myapp-module-true.pl: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use Mojolicious::Lite; 3 | 4 | app->config(script => $0); 5 | 6 | app->start; 7 | 8 | =head1 SYNOPSIS 9 | 10 | USAGE: myapp.pl daemon 11 | 12 | test 13 | 123 14 | 15 | =cut 16 | -------------------------------------------------------------------------------- /t/mojo/lib/myapp.pl: -------------------------------------------------------------------------------- 1 | use Mojolicious::Lite; 2 | 3 | app->config(script => $0); 4 | 5 | app->start; 6 | 7 | =head1 SYNOPSIS 8 | 9 | USAGE: myapp.pl daemon 10 | 11 | test 12 | 123 13 | 14 | =cut 15 | -------------------------------------------------------------------------------- /t/mojo/proxy.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Mojo::UserAgent::Proxy; 5 | 6 | # Proxy detection 7 | subtest 'Proxy detection with uppercase variable names' => sub { 8 | my $proxy = Mojo::UserAgent::Proxy->new; 9 | local $ENV{HTTP_PROXY} = 'http://127.0.0.1'; 10 | local $ENV{HTTPS_PROXY} = 'http://127.0.0.1:8080'; 11 | local $ENV{NO_PROXY} = 'mojolicious.org'; 12 | $proxy->detect; 13 | is $proxy->http, 'http://127.0.0.1', 'right proxy'; 14 | is $proxy->https, 'http://127.0.0.1:8080', 'right proxy'; 15 | $proxy->http(undef); 16 | $proxy->https(undef); 17 | is $proxy->http, undef, 'right proxy'; 18 | is $proxy->https, undef, 'right proxy'; 19 | ok !$proxy->is_needed('dummy.mojolicious.org'), 'no proxy needed'; 20 | ok $proxy->is_needed('icious.org'), 'proxy needed'; 21 | ok $proxy->is_needed('localhost'), 'proxy needed'; 22 | }; 23 | 24 | subtest 'Proxy detection with lowercase variable names' => sub { 25 | local $ENV{HTTP_PROXY}; 26 | local $ENV{HTTPS_PROXY}; 27 | local $ENV{NO_PROXY}; 28 | 29 | local $ENV{http_proxy} = 'proxy.example.com'; 30 | local $ENV{https_proxy} = 'tunnel.example.com'; 31 | local $ENV{no_proxy} = 'localhost,localdomain,foo.com,example.com'; 32 | 33 | my $proxy = Mojo::UserAgent::Proxy->new; 34 | $proxy->detect; 35 | is_deeply $proxy->not, ['localhost', 'localdomain', 'foo.com', 'example.com'], 'right list'; 36 | is $proxy->http, 'proxy.example.com', 'right proxy'; 37 | is $proxy->https, 'tunnel.example.com', 'right proxy'; 38 | ok $proxy->is_needed('dummy.mojolicious.org'), 'proxy needed'; 39 | ok $proxy->is_needed('icious.org'), 'proxy needed'; 40 | ok !$proxy->is_needed('localhost'), 'proxy needed'; 41 | ok !$proxy->is_needed('localhost.localdomain'), 'no proxy needed'; 42 | ok !$proxy->is_needed('foo.com'), 'no proxy needed'; 43 | ok !$proxy->is_needed('example.com'), 'no proxy needed'; 44 | ok !$proxy->is_needed('www.example.com'), 'no proxy needed'; 45 | ok $proxy->is_needed('www.example.com.com'), 'proxy needed'; 46 | }; 47 | 48 | done_testing(); 49 | -------------------------------------------------------------------------------- /t/mojo/reactor_detect.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::Reactor::Poll; 7 | 8 | # Dummy reactor 9 | package Mojo::Reactor::Test; 10 | use Mojo::Base 'Mojo::Reactor::Poll'; 11 | 12 | package main; 13 | 14 | subtest 'Detection (success)' => sub { 15 | local $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Test'; 16 | is(Mojo::Reactor->detect, 'Mojo::Reactor::Test', 'right class'); 17 | }; 18 | 19 | subtest 'Detection (fail)' => sub { 20 | local $ENV{MOJO_REACTOR} = 'Mojo::Reactor::DoesNotExist'; 21 | is(Mojo::Reactor->detect, 'Mojo::Reactor::Poll', 'right class'); 22 | }; 23 | 24 | subtest 'Event loop detection' => sub { 25 | local $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Test'; 26 | require Mojo::IOLoop; 27 | is ref Mojo::IOLoop->new->reactor, 'Mojo::Reactor::Test', 'right class'; 28 | }; 29 | 30 | done_testing(); 31 | -------------------------------------------------------------------------------- /t/mojo/signatures.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | BEGIN { plan skip_all => 'Perl 5.20+ required for this test!' if $] < 5.020 } 6 | 7 | package MojoSignatureBaseTest; 8 | use Mojo::Base -base, -signatures; 9 | 10 | sub foo ($self, $bar, $baz) { $bar + $baz } 11 | 12 | package MojoSignatureBaseTest2; 13 | use Mojo::Base -signatures, -base, -strict; 14 | 15 | sub foo ($self, $bar, $baz) { $bar - $baz } 16 | 17 | package main; 18 | 19 | subtest 'Basics' => sub { 20 | my $test = MojoSignatureBaseTest->new; 21 | is($test->foo(23, 24), 47, 'right result'); 22 | }; 23 | 24 | subtest 'Random order flags' => sub { 25 | my $test2 = MojoSignatureBaseTest2->new; 26 | is($test2->foo(26, 24), 2, 'right result'); 27 | }; 28 | 29 | subtest 'Bad flag' => sub { 30 | eval "package MojoSignaturesTest3; use Mojo::Base -unsupported"; 31 | like $@, qr/Unsupported flag: -unsupported/, 'right error'; 32 | }; 33 | 34 | subtest 'Secret feature' => sub { 35 | my $foo = {bar => [1, 2, 3]}; 36 | is_deeply [$foo->{bar}->@*], [1, 2, 3], 'right structure'; 37 | }; 38 | 39 | done_testing(); 40 | -------------------------------------------------------------------------------- /t/mojo/subprocess_ev.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | plan skip_all => 'set TEST_SUBPROCESS to enable this test (developer only!)' 6 | unless $ENV{TEST_SUBPROCESS} || $ENV{TEST_ALL}; 7 | plan skip_all => 'set TEST_EV to enable this test (developer only!)' unless $ENV{TEST_EV} || $ENV{TEST_ALL}; 8 | plan skip_all => 'EV 4.32+ required for this test!' unless eval { require EV; EV->VERSION('4.32'); 1 }; 9 | 10 | use Mojo::IOLoop; 11 | use Mojo::Promise; 12 | 13 | # Event loop in subprocess (already running event loop) 14 | my ($fail, $result); 15 | Mojo::IOLoop->next_tick(sub { 16 | Mojo::IOLoop->subprocess( 17 | sub { 18 | my $result; 19 | my $promise = Mojo::Promise->new; 20 | $promise->then(sub { $result = shift }); 21 | Mojo::IOLoop->next_tick(sub { $promise->resolve(25) }); 22 | $promise->wait; 23 | return $result; 24 | }, 25 | sub { 26 | my ($subprocess, $err, $twenty_five) = @_; 27 | $fail = $err; 28 | $result = $twenty_five; 29 | } 30 | ); 31 | }); 32 | Mojo::IOLoop->start; 33 | ok !$fail, 'no error'; 34 | is $result, 25, 'right result'; 35 | 36 | done_testing; 37 | -------------------------------------------------------------------------------- /t/mojo/templates/exception.mt: -------------------------------------------------------------------------------- 1 | test 2 | % die; 3 | 123 4 | -------------------------------------------------------------------------------- /t/mojo/templates/test.mt: -------------------------------------------------------------------------------- 1 | %= $_[0] + 20 2 | Hello World! 3 | -------------------------------------------------------------------------------- /t/mojo/templates/utf8_exception.mt: -------------------------------------------------------------------------------- 1 | ☃ 2 | % die;♥ 3 | ☃ 4 | -------------------------------------------------------------------------------- /t/mojo/tls.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::IOLoop::TLS; 7 | 8 | plan skip_all => 'set TEST_TLS to enable this test (developer only!)' unless $ENV{TEST_TLS} || $ENV{TEST_ALL}; 9 | plan skip_all => 'IO::Socket::SSL 2.009+ required for this test!' unless Mojo::IOLoop::TLS->can_tls; 10 | 11 | use Mojo::IOLoop; 12 | use Mojo::Promise; 13 | use Socket; 14 | 15 | subtest 'Built-in certificate' => sub { 16 | socketpair(my $client_sock, my $server_sock, AF_UNIX, SOCK_STREAM, PF_UNSPEC) 17 | or die "Couldn't create socket pair: $!"; 18 | $client_sock->blocking(0); 19 | $server_sock->blocking(0); 20 | my $promise = Mojo::Promise->new; 21 | my $promise2 = Mojo::Promise->new; 22 | my $server = Mojo::IOLoop::TLS->new($server_sock); 23 | $server->once(upgrade => sub { $promise->resolve(pop) }); 24 | $server->once(error => sub { warn pop }); 25 | $server->negotiate({server => 1}); 26 | my $client = Mojo::IOLoop::TLS->new($client_sock); 27 | $client->once(upgrade => sub { $promise2->resolve(pop) }); 28 | $client->once(error => sub { warn pop }); 29 | $client->negotiate(tls_options => {SSL_verify_mode => 0x00}); 30 | my ($client_result, $server_result); 31 | Mojo::Promise->all($promise, $promise2)->then(sub { 32 | ($server_result, $client_result) = map { $_->[0] } @_; 33 | })->wait; 34 | is ref $client_result, 'IO::Socket::SSL', 'right class'; 35 | is ref $server_result, 'IO::Socket::SSL', 'right class'; 36 | }; 37 | 38 | subtest 'Built-in certificate (custom event loop and cipher)' => sub { 39 | my $loop = Mojo::IOLoop->new; 40 | socketpair(my $client_sock2, my $server_sock2, AF_UNIX, SOCK_STREAM, PF_UNSPEC) 41 | or die "Couldn't create socket pair: $!"; 42 | $client_sock2->blocking(0); 43 | $server_sock2->blocking(0); 44 | my $promise = Mojo::Promise->new->ioloop($loop); 45 | my $promise2 = Mojo::Promise->new->ioloop($loop); 46 | my $server = Mojo::IOLoop::TLS->new($server_sock2)->reactor($loop->reactor); 47 | $server->once(upgrade => sub { $promise->resolve(pop) }); 48 | $server->once(error => sub { warn pop }); 49 | $server->negotiate(server => 1, tls_options => {SSL_cipher_list => 'AES256-SHA:ALL'}); 50 | my $client = Mojo::IOLoop::TLS->new($client_sock2)->reactor($loop->reactor); 51 | $client->once(upgrade => sub { $promise2->resolve(pop) }); 52 | $client->once(error => sub { warn pop }); 53 | $client->negotiate(tls_options => {SSL_verify_mode => 0x00}); 54 | my ($client_result, $server_result); 55 | Mojo::Promise->all($promise, $promise2)->then(sub { 56 | ($server_result, $client_result) = map { $_->[0] } @_; 57 | })->wait; 58 | is ref $client_result, 'IO::Socket::SSL', 'right class'; 59 | is ref $server_result, 'IO::Socket::SSL', 'right class'; 60 | my $expect = $server_result->get_sslversion eq 'TLSv1_3' ? 'TLS_AES_256_GCM_SHA384' : 'AES256-SHA'; 61 | is $client_result->get_cipher, $expect, "$expect has been negotiatied"; 62 | is $server_result->get_cipher, $expect, "$expect has been negotiatied"; 63 | }; 64 | 65 | done_testing; 66 | -------------------------------------------------------------------------------- /t/mojolicious/embedded_lite_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "it": "just works" 3 | } 4 | -------------------------------------------------------------------------------- /t/mojolicious/external/lib/MyApp.pm: -------------------------------------------------------------------------------- 1 | package MyApp; 2 | use Mojo::Base 'Mojolicious'; 3 | 4 | sub startup { 5 | my $self = shift; 6 | my $r = $self->routes; 7 | 8 | $self->plugin('Config'); 9 | 10 | $r->get( 11 | '/' => sub { 12 | my $self = shift; 13 | $self->render(text => $self->config->{works}); 14 | } 15 | ); 16 | 17 | $r->get( 18 | '/test' => sub { 19 | my $self = shift; 20 | $self->render(text => $self->config->{whatever}); 21 | } 22 | ); 23 | 24 | $r->get( 25 | '/secondary' => sub { 26 | my $self = shift; 27 | $self->render(text => ++$self->session->{secondary}); 28 | } 29 | ); 30 | 31 | $r->get( 32 | '/inline' => sub { 33 | my $self = shift; 34 | $self->render(inline => '<%= config->{whatever} =%>'); 35 | } 36 | ); 37 | } 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /t/mojolicious/external/my_app.conf: -------------------------------------------------------------------------------- 1 | use Mojo::Util qw(url_escape); 2 | {works => url_escape 'too!'} 3 | -------------------------------------------------------------------------------- /t/mojolicious/external/my_app.testing.conf: -------------------------------------------------------------------------------- 1 | use Mojo::Util qw(url_escape); 2 | {whatever => url_escape 'works!'}; 3 | -------------------------------------------------------------------------------- /t/mojolicious/external/myapp.conf: -------------------------------------------------------------------------------- 1 | app->defaults(also => 'works!!!', in => __PACKAGE__); 2 | {just => "works ♥!"}; 3 | -------------------------------------------------------------------------------- /t/mojolicious/external/myapp.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Mojolicious::Lite; 4 | 5 | # Default for config file tests 6 | app->defaults(secret => 'Insecure!'); 7 | 8 | # Helpers sharing the same name in different embedded applications 9 | helper same_name => sub {'myapp'}; 10 | 11 | # Load plugin 12 | my $config = plugin 'Config'; 13 | my $one = $config->{one}; 14 | 15 | # Message condition 16 | app->routes->add_condition( 17 | message => sub { 18 | my ($route, $c, $captures, $msg) = @_; 19 | $c->res->headers->header('X-Message' => $msg); 20 | return 1; 21 | } 22 | ); 23 | 24 | get '/' => 'index'; 25 | 26 | get '/startup' => {text => $one}; 27 | 28 | get '/echo' => sub { 29 | my $c = shift; 30 | $c->render(text => 'echo: ' . ($c->stash('message') || 'nothing!')); 31 | }; 32 | 33 | get '/stream' => sub { 34 | shift->write_chunk( 35 | 'he' => sub { 36 | shift->write_chunk('ll' => sub { shift->finish('o!') }); 37 | } 38 | ); 39 | }; 40 | 41 | get '/url/☃' => sub { 42 | my $c = shift; 43 | my $route = $c->url_for({format => 'json'}); 44 | my $rel = $c->url_for('/☃/stream'); 45 | $c->render(text => "$route -> $rel!"); 46 | }; 47 | 48 | get '/host' => (message => 'it works!') => sub { 49 | my $c = shift; 50 | $c->render(text => $c->url_for->base->host); 51 | }; 52 | 53 | get '/one' => sub { shift->render(text => 'One') }; 54 | 55 | get '/one/two' => {text => 'Two'}; 56 | 57 | get '/template/:foo' => sub { 58 | my $c = shift; 59 | $c->render($c->param('foo')); 60 | }; 61 | 62 | websocket '/url_for' => sub { 63 | my $c = shift; 64 | $c->on( 65 | message => sub { 66 | my ($c, $msg) = @_; 67 | $c->send($c->url_for($msg)->to_abs); 68 | } 69 | ); 70 | } => 'ws_test'; 71 | 72 | app->start; 73 | __DATA__ 74 | 75 | @@ menubar.html.ep 76 | %= same_name 77 | <%= config->{just} %><%= config->{one} %><%= config->{two} %> 78 | -------------------------------------------------------------------------------- /t/mojolicious/external/myapp.testing.conf: -------------------------------------------------------------------------------- 1 | { 2 | one => app->defaults('secret'), 3 | two => $app->defaults->{secret}, 4 | works => "too!" 5 | }; 6 | -------------------------------------------------------------------------------- /t/mojolicious/external/myapp2.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Mojolicious::Lite; 4 | use Mojo::IOLoop; 5 | 6 | # Default for config file tests 7 | app->defaults(secret => 'Insecure too!'); 8 | 9 | # Helpers sharing the same name in different embedded applications 10 | helper same_name => sub {'myapp2'}; 11 | 12 | # Caching helper for state variable test 13 | helper my_cache => sub { state $cache = shift->param('cache') }; 14 | 15 | # Delay dispatching 16 | hook around_dispatch => sub { 17 | my ($next, $c) = @_; 18 | Mojo::IOLoop->next_tick(sub { $next->() }); 19 | }; 20 | 21 | get '/' => sub { 22 | my $c = shift; 23 | $c->render(text => $c->render_to_string('menubar') . app->defaults->{secret}); 24 | }; 25 | 26 | get '/cached' => sub { 27 | my $c = shift; 28 | $c->render(text => $c->my_cache); 29 | }; 30 | 31 | app->start; 32 | __DATA__ 33 | 34 | @@ menubar.html.ep 35 | %= same_name 36 | %= stash('message') || 'works 4!' 37 | -------------------------------------------------------------------------------- /t/mojolicious/external/public/index.html: -------------------------------------------------------------------------------- 1 | External static file! 2 | -------------------------------------------------------------------------------- /t/mojolicious/external/script/my_app: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Mojo::File qw(curfile); 7 | use lib curfile->dirname->sibling('lib')->to_string; 8 | use Mojolicious::Commands; 9 | 10 | # Start command line interface for application 11 | Mojolicious::Commands->start_app('MyApp'); 12 | -------------------------------------------------------------------------------- /t/mojolicious/external/templates/index.html.ep: -------------------------------------------------------------------------------- 1 | %= include 'menubar' 2 | <%= config->{works} . (stash('also') // '*') . (stash('in') // '?') %> 3 | %= link_to Test => '/' 4 | %= form_for '/☃' => begin 5 | %= submit_button '☃' 6 | %= end 7 | -------------------------------------------------------------------------------- /t/mojolicious/external_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'testing'; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::More; 9 | 10 | use Mojo::File qw(curfile); 11 | use lib curfile->sibling('external', 'lib')->to_string; 12 | 13 | use Test::Mojo; 14 | 15 | my $t = Test::Mojo->new('MyApp'); 16 | 17 | subtest 'Text from config file' => sub { 18 | $t->get_ok('/')->status_is(200)->content_is('too%21'); 19 | }; 20 | 21 | subtest 'Static file' => sub { 22 | $t->get_ok('/index.html')->status_is(200)->content_is("External static file!\n"); 23 | }; 24 | 25 | subtest 'More text from config file' => sub { 26 | $t->get_ok('/test')->status_is(200)->content_is('works%21'); 27 | }; 28 | 29 | # Config override 30 | $t = Test::Mojo->new(MyApp => {whatever => 'override!', works => 'override two!'}); 31 | 32 | subtest 'Text from config override' => sub { 33 | $t->get_ok('/')->status_is(200)->content_is('override two!'); 34 | }; 35 | 36 | subtest 'Static file again' => sub { 37 | $t->get_ok('/index.html')->status_is(200)->content_is("External static file!\n"); 38 | }; 39 | 40 | subtest 'More text from config override' => sub { 41 | $t->get_ok('/test')->status_is(200)->content_is('override!'); 42 | }; 43 | 44 | subtest 'Config stash value from template' => sub { 45 | $t->get_ok('/inline')->status_is(200)->content_is('override!'); 46 | }; 47 | 48 | done_testing(); 49 | -------------------------------------------------------------------------------- /t/mojolicious/external_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'testing'; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::More; 9 | 10 | use Test::Mojo; 11 | use Mojo::File qw(curfile); 12 | 13 | my $t = Test::Mojo->new(curfile->sibling('external', 'myapp.pl')); 14 | 15 | subtest 'Template from myapp.pl' => sub { 16 | $t->get_ok('/')->status_is(200)->content_is(<<'EOF'); 17 | myapp 18 | works ♥!Insecure!Insecure! 19 | 20 | too!works!!!Mojolicious::Plugin::Config::Sandbox 21 | Test 22 |
23 | 24 |
25 | EOF 26 | }; 27 | 28 | subtest 'Static file from myapp.pl' => sub { 29 | $t->get_ok('/index.html')->status_is(200)->content_is("External static file!\n"); 30 | }; 31 | 32 | subtest 'Echo from myapp.pl' => sub { 33 | $t->get_ok('/echo')->status_is(200)->content_is('echo: nothing!'); 34 | }; 35 | 36 | subtest 'Chunked response from myapp.pl' => sub { 37 | $t->get_ok('/stream')->status_is(200)->content_is('hello!'); 38 | }; 39 | 40 | subtest 'URL generated by myapp.pl' => sub { 41 | $t->get_ok('/url/☃')->status_is(200)->content_is('/url/%E2%98%83.json -> /%E2%98%83/stream!'); 42 | }; 43 | 44 | subtest 'Config value at startup time' => sub { 45 | $t->get_ok('/startup')->status_is(200)->content_is('Insecure!'); 46 | }; 47 | 48 | # Config override 49 | $t = Test::Mojo->new(curfile->sibling('external', 'myapp.pl'), 50 | {one => 'override!', two => 'go!', just => 'yada', works => 'override two!'}); 51 | 52 | subtest 'Template from myapp.pl with config override' => sub { 53 | $t->get_ok('/')->status_is(200)->content_is(<<'EOF'); 54 | myapp 55 | yadaoverride!go! 56 | 57 | override two!*? 58 | Test 59 |
60 | 61 |
62 | EOF 63 | }; 64 | 65 | subtest 'Config override at startup time' => sub { 66 | $t->get_ok('/startup')->status_is(200)->content_is('override!'); 67 | }; 68 | 69 | done_testing(); 70 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_lite_app.json: -------------------------------------------------------------------------------- 1 | { 2 | %# Just a comment 3 | "foo": "bar", 4 | "utf": "утф", 5 | "plugins": [ 6 | {"MojoliciousTest::Plugin::DeploymentPlugin": {}}, 7 | { 8 | "MojoliciousTest::Plugin::DeploymentPlugin": { 9 | "name": "another_helper", 10 | "message": "works too!" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'development'; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::Mojo; 9 | use Test::More; 10 | 11 | use Mojo::File qw(curfile); 12 | use lib curfile->sibling('lib')->to_string; 13 | 14 | use Mojolicious::Lite; 15 | 16 | subtest 'Default' => sub { 17 | app->config(it => 'works'); 18 | is_deeply app->config, {it => 'works'}, 'right value'; 19 | }; 20 | 21 | subtest 'Invalid config file' => sub { 22 | eval { plugin JSONConfig => {file => 'public/hello.txt'} }; 23 | like $@, qr/JSON/, 'right error'; 24 | }; 25 | 26 | subtest 'Load plugins' => sub { 27 | my $config = plugin j_s_o_n_config => {default => {foo => 'baz', hello => 'there'}}; 28 | my $path = curfile->sibling('json_config_lite_app_abs.json'); 29 | plugin JSONConfig => {file => $path}; 30 | is $config->{foo}, 'bar', 'right value'; 31 | is $config->{hello}, 'there', 'right value'; 32 | is $config->{utf}, 'утф', 'right value'; 33 | is $config->{absolute}, 'works too!', 'right value'; 34 | is $config->{absolute_dev}, 'dev works too!', 'right value'; 35 | is app->config->{foo}, 'bar', 'right value'; 36 | is app->config->{hello}, 'there', 'right value'; 37 | is app->config->{utf}, 'утф', 'right value'; 38 | is app->config->{absolute}, 'works too!', 'right value'; 39 | is app->config->{absolute_dev}, 'dev works too!', 'right value'; 40 | is app->config('foo'), 'bar', 'right value'; 41 | is app->config('hello'), 'there', 'right value'; 42 | is app->config('utf'), 'утф', 'right value'; 43 | is app->config('absolute'), 'works too!', 'right value'; 44 | is app->config('absolute_dev'), 'dev works too!', 'right value'; 45 | is app->config('it'), 'works', 'right value'; 46 | is app->deployment_helper, 'deployment plugins work!', 'right value'; 47 | is app->another_helper, 'works too!', 'right value'; 48 | }; 49 | 50 | get '/' => 'index'; 51 | 52 | my $t = Test::Mojo->new; 53 | 54 | $t->get_ok('/')->status_is(200)->content_is("barbar\n"); 55 | 56 | subtest 'No config file, default only' => sub { 57 | my $config = plugin JSONConfig => {file => 'nonexistent', default => {foo => 'qux'}}; 58 | is $config->{foo}, 'qux', 'right value'; 59 | is app->config->{foo}, 'qux', 'right value'; 60 | is app->config('foo'), 'qux', 'right value'; 61 | is app->config('it'), 'works', 'right value'; 62 | }; 63 | 64 | subtest 'No config file, no default' => sub { 65 | ok !(eval { plugin JSONConfig => {file => 'nonexistent'} }), 'no config file'; 66 | local $ENV{MOJO_CONFIG} = 'nonexistent'; 67 | ok !(eval { plugin 'JSONConfig' }), 'no config file'; 68 | }; 69 | 70 | done_testing(); 71 | 72 | __DATA__ 73 | @@ index.html.ep 74 | <%= config->{foo} %><%= config 'foo' %> 75 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_lite_app_abs.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_dev": "dev works too!" 3 | } 4 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_lite_app_abs.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute": "works too!" 3 | } 4 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_mode_lite_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "bar": "foo" 4 | } 5 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_mode_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'testing'; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::More; 9 | use Mojolicious::Lite; 10 | use Test::Mojo; 11 | 12 | # Load plugin 13 | plugin 'JSONConfig'; 14 | 15 | get '/' => 'index'; 16 | 17 | my $t = Test::Mojo->new; 18 | 19 | subtest 'Template with config information' => sub { 20 | $t->get_ok('/')->status_is(200)->content_like(qr/bazfoo/); 21 | }; 22 | 23 | done_testing(); 24 | 25 | __DATA__ 26 | @@ index.html.ep 27 | <%= config->{foo} %><%= config->{bar} %> 28 | -------------------------------------------------------------------------------- /t/mojolicious/json_config_mode_lite_app.testing.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "baz" 3 | } 4 | -------------------------------------------------------------------------------- /t/mojolicious/lib/AroundPlugin.pm: -------------------------------------------------------------------------------- 1 | package AroundPlugin; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app) = @_; 6 | 7 | # Render return value 8 | $app->hook( 9 | around_action => sub { 10 | my ($next, $c, $action, $last) = @_; 11 | my $value = $next->(); 12 | $c->render(text => $value) if $last && $c->stash->{return}; 13 | return $value; 14 | } 15 | ); 16 | } 17 | 18 | 1; 19 | -------------------------------------------------------------------------------- /t/mojolicious/lib/EmbeddedTestApp.pm: -------------------------------------------------------------------------------- 1 | package EmbeddedTestApp; 2 | use Mojolicious::Lite; 3 | 4 | plugin "JSONConfig"; 5 | 6 | get '/works'; 7 | 8 | get '/works/too' => 'too'; 9 | 10 | 1; 11 | __DATA__ 12 | @@ works.html.ep 13 | It is <%= $name %>! 14 | 15 | @@ too.html.ep 16 | It <%= config->{it} %>! 17 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousConfigTest.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousConfigTest; 2 | use Mojo::Base 'Mojolicious'; 3 | 4 | sub startup { shift->plugin('Config') } 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Baz.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Baz; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub index { shift->render(text => 'Production namespace has low precedence!') } 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Command/_test2_command.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Command::_test2_command; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | sub run {'works 2!'} 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Command/test_command.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Command::test_command; 2 | use Mojo::Base 'Mojolicious::Command'; 3 | 4 | use Mojo::Util qw(getopt); 5 | 6 | sub run { 7 | my ($self, @args) = @_; 8 | getopt \@args, ['default'], 'too' => \my $too; 9 | return $too ? 'works too!' : 'works!'; 10 | } 11 | 12 | 1; 13 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Controller/Foo/Bar.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Controller::Foo::Bar; 2 | use Mojolicious::Controller -base; 3 | 4 | sub index {1} 5 | 6 | sub test { shift->stash(msg => 'works') } 7 | 8 | 1; 9 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Exceptional.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Exceptional; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub this_one_dies { die "doh!\n" } 5 | 6 | sub this_one_might_die { 7 | die "double doh!\n" unless shift->req->headers->header('X-DoNotDie'); 8 | 1; 9 | } 10 | 11 | 1; 12 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Foo.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Foo; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub DESTROY { shift->stash->{destroyed} = 1 } 5 | 6 | sub config { 7 | my $self = shift; 8 | $self->render(text => $self->stash('config')->{test}); 9 | } 10 | 11 | sub fun { shift->render } 12 | 13 | sub joy { shift->render } 14 | 15 | sub index { 16 | my $self = shift; 17 | $self->layout('default'); 18 | $self->stash(handler => 'xpl', msg => 'Hello World!'); 19 | } 20 | 21 | sub longpoll { 22 | my $self = shift; 23 | $self->on(finish => sub { shift->stash->{finished} = 1 }); 24 | $self->write_chunk( 25 | 'P' => sub { 26 | shift->write_chunk('oll!' => sub { shift->write_chunk('') }); 27 | } 28 | ); 29 | } 30 | 31 | sub plugin_camel_case { 32 | my $self = shift; 33 | $self->render(text => $self->some_plugin); 34 | } 35 | 36 | sub plugin_upper_case { 37 | my $self = shift; 38 | $self->render(text => $self->upper_case_test_plugin); 39 | } 40 | 41 | sub session_domain { 42 | my $self = shift; 43 | $self->session(user => 'Bender'); 44 | $self->render(text => 'Bender rockzzz!'); 45 | } 46 | 47 | sub something { 48 | my $self = shift; 49 | $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); 50 | $self->render(text => $self->url_for('something', something => '42')); 51 | } 52 | 53 | sub stage1 { 54 | my $self = shift; 55 | 56 | # Authenticated 57 | return 1 if $self->req->headers->header('X-Pass'); 58 | 59 | # Fail 60 | $self->render(text => 'Go away!'); 61 | return undef; 62 | } 63 | 64 | sub stage2 { return shift->some_plugin } 65 | 66 | sub suspended { 67 | my $self = shift; 68 | 69 | $self->res->headers->append('X-Suspended' => $self->match->position); 70 | Mojo::IOLoop->next_tick(sub { 71 | $self->res->headers->append('X-Suspended' => $self->match->position); 72 | $self->continue; 73 | }); 74 | 75 | return 0; 76 | } 77 | 78 | sub syntaxerror { shift->render('syntaxerror', format => 'html') } 79 | 80 | sub templateless { shift->render(handler => 'test') } 81 | 82 | sub test { 83 | my $self = shift; 84 | $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); 85 | $self->render(text => $self->url_for(foo => 'bar')); 86 | } 87 | 88 | sub url_for_missing { 89 | my $self = shift; 90 | $self->render(text => $self->url_for('does_not_exist', something => '42')); 91 | } 92 | 93 | sub willdie { die 'for some reason' } 94 | 95 | sub withBlock { shift->render(template => 'withblock') } 96 | 97 | sub withlayout { shift->stash(template => 'WithGreenLayout') } 98 | 99 | 1; 100 | __DATA__ 101 | 102 | @@ foo/fun.html.ep 103 |

Have fun!

\ 104 | 105 | @@ foo/joy.html.ep 106 |

Joy for all!

\ 107 | 108 | @@ just/some/template.html.epl 109 | Development template with high precedence. 110 | 111 | @@ some/static/file.txt 112 | Development static file with high precedence. 113 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/PODTest.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::PODTest; 2 | 3 | 1; 4 | 5 | =head1 One 6 | 7 | PODTest 8 | 9 | =head2 Two 10 | 11 | my $foo = 'bar'; 12 | 13 | =head3 Three 14 | 15 | Hello 16 | 17 | =head4 Four 18 | 19 | World! 20 | 21 | =cut 22 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Plugin/DeploymentPlugin.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Plugin::DeploymentPlugin; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app, $config) = @_; 6 | my $name = $config->{name} // 'deployment_helper'; 7 | my $msg = $config->{message} // 'deployment plugins work!'; 8 | $app->helper($name => sub {$msg}); 9 | } 10 | 11 | 1; 12 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Plugin/Test/SomePlugin2.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Plugin::Test::SomePlugin2; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app) = @_; 6 | 7 | # Add "some_plugin" helper 8 | $app->helper(some_plugin => sub {'Welcome aboard!'}); 9 | } 10 | 11 | 1; 12 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/Plugin/UPPERCASETestPlugin.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::Plugin::UPPERCASETestPlugin; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app) = @_; 6 | 7 | # Add "upper_case_test_plugin" helper 8 | $app->helper(upper_case_test_plugin => sub {'WELCOME aboard!'}); 9 | } 10 | 11 | 1; 12 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/SideEffects/Test.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::SideEffects::Test; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub index { shift->render(text => 'pass') } 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest/SyntaxError.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest::SyntaxError; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub foo { 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest2/Foo.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest2::Foo; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub test { 5 | my $self = shift; 6 | $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); 7 | $self->render(text => $self->url_for); 8 | } 9 | 10 | 1; 11 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest3/Bar.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest3::Bar; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub index { 5 | my $self = shift; 6 | $self->render(text => 'Development namespace works!'); 7 | } 8 | 9 | 1; 10 | -------------------------------------------------------------------------------- /t/mojolicious/lib/MojoliciousTest3/Baz.pm: -------------------------------------------------------------------------------- 1 | package MojoliciousTest3::Baz; 2 | use Mojo::Base 'MojoliciousTest::Baz'; 3 | 4 | sub index { shift->render(text => 'Development namespace has high precedence!') } 5 | 6 | 1; 7 | -------------------------------------------------------------------------------- /t/mojolicious/lib/PluginWithEmbeddedApp.pm: -------------------------------------------------------------------------------- 1 | package PluginWithEmbeddedApp; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app) = @_; 6 | $app->routes->any('/plugin')->partial(1)->to(PluginWithEmbeddedApp::App::app()); 7 | } 8 | 9 | package PluginWithEmbeddedApp::App; 10 | use Mojolicious::Lite; 11 | 12 | get '/foo'; 13 | 14 | 1; 15 | __DATA__ 16 | @@ foo.html.ep 17 | plugin works!\ 18 | -------------------------------------------------------------------------------- /t/mojolicious/lib/PluginWithTemplate.pm: -------------------------------------------------------------------------------- 1 | package PluginWithTemplate; 2 | use Mojo::Base 'Mojolicious::Plugin'; 3 | 4 | sub register { 5 | my ($self, $app) = @_; 6 | push @{$app->renderer->classes}, __PACKAGE__; 7 | $app->routes->any('/plugin_with_template')->to(cb => sub { shift->render('plugin_with_template') }); 8 | } 9 | 10 | 1; 11 | __DATA__ 12 | 13 | @@ plugin_with_template.html.ep 14 | % layout 'plugin_with_template'; 15 | with template 16 | -------------------------------------------------------------------------------- /t/mojolicious/lib/SingleFileTestApp.pm: -------------------------------------------------------------------------------- 1 | package SingleFileTestApp; 2 | use Mojo::Base 'Mojolicious'; 3 | 4 | sub startup { 5 | my $self = shift; 6 | 7 | # Only log errors to STDERR 8 | $self->log->level('fatal'); 9 | 10 | # Plugins 11 | $self->plugin('PluginWithEmbeddedApp'); 12 | $self->plugin('MojoliciousTest::Plugin::Test::SomePlugin2'); 13 | $self->plugin('Config'); 14 | 15 | # DATA classes 16 | push @{$self->renderer->classes}, 'SingleFileTestApp::Foo'; 17 | push @{$self->static->classes}, 'SingleFileTestApp::Foo'; 18 | 19 | # Helper route 20 | $self->routes->any('/helper')->to( 21 | cb => sub { 22 | my $c = shift; 23 | $c->render(text => $c->some_plugin); 24 | } 25 | ); 26 | 27 | # The default route 28 | my $r = $self->routes; 29 | $r->any('/foo')->to('Foo#index'); 30 | $r->any('/foo/conf')->to('Foo#conf'); 31 | $r->any('/foo/data_static')->to('Foo#data_static'); 32 | $r->any('/foo/data_template')->to('Foo#data_template'); 33 | $r->any('/foo/data_template2')->to('Foo#data_template2'); 34 | $r->any('/foo/routes')->to('Foo#routes'); 35 | $r->any('/redispatch')->to('Redispatch#index'); 36 | $r->any('/redispatch/render')->to('Redispatch#render'); 37 | $r->any('/redispatch/secret')->to('Redispatch#secret'); 38 | } 39 | 40 | package SingleFileTestApp::Redispatch; 41 | use Mojo::Base 'Mojolicious'; 42 | 43 | sub handler { 44 | my ($self, $c) = @_; 45 | return secret($c) if $c->param('rly'); 46 | return render($c) if $c->stash('action') eq 'render'; 47 | $c->render(text => 'Redispatch!'); 48 | } 49 | 50 | sub render { 51 | my $c = shift; 52 | $c->render(text => 'Render!'); 53 | } 54 | 55 | sub secret { 56 | my $c = shift; 57 | $c->render(text => 'Secret!'); 58 | } 59 | 60 | package SingleFileTestApp::Foo; 61 | use Mojo::Base 'Mojolicious::Controller'; 62 | 63 | sub conf { 64 | my $self = shift; 65 | $self->render(text => $self->config->{single_file}); 66 | } 67 | 68 | sub data_template { shift->render('index') } 69 | 70 | sub data_template2 { shift->stash(template => 'too') } 71 | 72 | sub data_static { shift->reply->static('singlefiletestapp/foo.txt') } 73 | 74 | sub index { 75 | shift->stash(template => 'WithGreenLayout', msg => 'works great!'); 76 | } 77 | 78 | sub routes { 79 | my $self = shift; 80 | $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); 81 | $self->render(text => $self->url_for); 82 | } 83 | 84 | 1; 85 | __DATA__ 86 | @@ index.html.epl 87 | <%= 20 + 3 %> works! 88 | @@ too.html.epl 89 | This one works too! 90 | @@ singlefiletestapp/foo.txt 91 | And this one... ALL GLORY TO THE HYPNOTOAD! 92 | -------------------------------------------------------------------------------- /t/mojolicious/log_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojolicious::Lite; 7 | use Mojo::Log; 8 | use Test::Mojo; 9 | 10 | hook before_dispatch => sub { 11 | my $c = shift; 12 | $c->req->request_id('17a60115'); 13 | }; 14 | 15 | get '/simple' => sub { 16 | my $c = shift; 17 | $c->log->debug('First!'); 18 | $c->log->info('Second!', 'Third!'); 19 | $c->app->log->debug('No context!'); 20 | $c->log->warn(sub { 'Fourth!', 'Fifth!' }); 21 | $c->render(text => 'Simple!'); 22 | }; 23 | 24 | my $t = Test::Mojo->new; 25 | 26 | subtest 'Simple log messages with and without context' => sub { 27 | my $buffer = ''; 28 | open my $handle, '>', \$buffer; 29 | $t->app->log(Mojo::Log->new(handle => $handle)); 30 | $t->get_ok('/simple')->status_is(200)->content_is('Simple!'); 31 | like $buffer, qr/First.*Second.*Third.*No context!.*Fourth.*Fifth/s, 'right order'; 32 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] \[17a60115\] First!/, 'message with request id'; 33 | like $buffer, qr/\[.+\] \[\d+\] \[info\] \[17a60115\] Second! Third!/s, 'message with request id'; 34 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] No context!/, 'message without request id'; 35 | like $buffer, qr/\[.+\] \[\d+\] \[warn\] \[17a60115\] Fourth! Fifth!/s, 'message with request id'; 36 | }; 37 | 38 | subtest 'Concurrent requests' => sub { 39 | my $buffer = ''; 40 | open my $handle, '>', \$buffer; 41 | $t->app->log(Mojo::Log->new(handle => $handle)); 42 | my $first = $t->app->build_controller; 43 | $first->req->request_id('123-first'); 44 | my $second = $t->app->build_controller; 45 | $second->req->request_id('123-second'); 46 | $first->log->debug('First!'); 47 | $second->log->debug('Second!'); 48 | $first->log->debug('Third!'); 49 | $second->log->debug('Fourth!'); 50 | $t->app->log->debug('Fifth!'); 51 | like $buffer, qr/First.*Second.*Third.*Fourth.*Fifth/s, 'right order'; 52 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] \[123-first\] First!/, 'message with request id'; 53 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] \[123-second\] Second!/, 'message with request id'; 54 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] \[123-first\] Third!/, 'message with request id'; 55 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] \[123-second\] Fourth!/, 'message with request id'; 56 | like $buffer, qr/\[.+\] \[\d+\] \[debug\] Fifth!/, 'message without request id'; 57 | }; 58 | 59 | done_testing(); 60 | -------------------------------------------------------------------------------- /t/mojolicious/mojolicious_config_test.whatever.conf: -------------------------------------------------------------------------------- 1 | {it => 'works'}; 2 | -------------------------------------------------------------------------------- /t/mojolicious/multipath_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::Mojo; 6 | use Test::More; 7 | use Mojolicious::Lite; 8 | 9 | # More paths with higher precedence 10 | unshift @{app->renderer->paths}, app->home->child('templates2'); 11 | unshift @{app->static->paths}, app->home->child('public2'); 12 | 13 | get '/twenty_three' => '23'; 14 | 15 | get '/fourty_two' => '42'; 16 | 17 | get '/fourty_two_again' => {template => '42', variant => 'test'}; 18 | 19 | get '/yada' => {template => 'foo/yada'}; 20 | 21 | my $t = Test::Mojo->new; 22 | 23 | subtest '"templates" directory' => sub { 24 | $t->get_ok('/twenty_three')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')->content_is("23\n"); 25 | }; 26 | 27 | subtest '"templates2" directory' => sub { 28 | $t->get_ok('/fourty_two') 29 | ->status_is(200) 30 | ->header_is(Server => 'Mojolicious (Perl)') 31 | ->content_is("The answer is 42.\n"); 32 | }; 33 | 34 | subtest '"templates2" directory (variant)' => sub { 35 | $t->get_ok('/fourty_two_again') 36 | ->status_is(200) 37 | ->header_is(Server => 'Mojolicious (Perl)') 38 | ->content_is("The answer is 43!\n"); 39 | }; 40 | 41 | subtest '"public2" directory' => sub { 42 | $t->get_ok('/hello.txt') 43 | ->status_is(200) 44 | ->header_is(Server => 'Mojolicious (Perl)') 45 | ->content_is("Also higher precedence!\n"); 46 | }; 47 | 48 | subtest '"public" directory' => sub { 49 | $t->get_ok('/hello2.txt')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')->content_is("X"); 50 | }; 51 | 52 | subtest '"public2" directory' => sub { 53 | $t->get_ok('/hello3.txt') 54 | ->status_is(200) 55 | ->header_is(Server => 'Mojolicious (Perl)') 56 | ->content_is("Hello Mojo from... ALL GLORY TO THE HYPNOTOAD!\n"); 57 | }; 58 | 59 | subtest '"templates2" directory' => sub { 60 | $t->get_ok('/yada')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')->content_is("Higher precedence!\n"); 61 | }; 62 | 63 | done_testing(); 64 | -------------------------------------------------------------------------------- /t/mojolicious/ojo.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'development'; 5 | $ENV{MOJO_PROXY} = 0; 6 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 7 | } 8 | 9 | use Test::More; 10 | use ojo; 11 | use File::Basename qw(basename); 12 | 13 | subtest 'Application' => sub { 14 | a('/' => sub { $_->render(data => $_->req->method . $_->req->body) })->secrets(['foobarbaz']); 15 | is a->secrets->[0], 'foobarbaz', 'right secret'; 16 | }; 17 | 18 | subtest 'Requests' => sub { 19 | is g('/')->body, 'GET', 'right content'; 20 | is h('/')->body, '', 'no content'; 21 | is o('/')->body, 'OPTIONS', 'right content'; 22 | is t('/')->body, 'PATCH', 'right content'; 23 | is p('/')->body, 'POST', 'right content'; 24 | is u('/')->body, 'PUT', 'right content'; 25 | is d('/')->body, 'DELETE', 'right content'; 26 | is p('/' => form => {foo => 'bar'})->body, 'POSTfoo=bar', 'right content'; 27 | is p('/' => json => {foo => 'bar'})->body, 'POST{"foo":"bar"}', 'right content'; 28 | }; 29 | 30 | subtest 'Mojolicious::Lite' => sub { 31 | get '/test' => {text => 'pass'}; 32 | is app->ua->get('/test')->res->body, 'pass', 'right content'; 33 | }; 34 | 35 | subtest 'Parse XML' => sub { 36 | is x ('works')->at('title')->text, 'works', 'right text'; 37 | }; 38 | 39 | subtest 'JSON' => sub { 40 | is j([1, 2]), '[1,2]', 'right result'; 41 | is_deeply j('[1,2]'), [1, 2], 'right structure'; 42 | is j({foo => 'bar'}), '{"foo":"bar"}', 'right result'; 43 | is_deeply j('{"foo":"bar"}'), {foo => 'bar'}, 'right structure'; 44 | }; 45 | 46 | subtest 'ByteStream' => sub { 47 | is b('')->url_escape, '%3Cfoo%3E', 'right result'; 48 | }; 49 | 50 | subtest 'Collection' => sub { 51 | is c(1, 2, 3)->join('-'), '1-2-3', 'right result'; 52 | }; 53 | 54 | subtest 'File' => sub { 55 | is f(__FILE__)->basename, basename(__FILE__), 'right result'; 56 | }; 57 | 58 | subtest 'Dumper' => sub { 59 | is r([1, 2]), "[\n 1,\n 2\n]\n", 'right result'; 60 | }; 61 | 62 | subtest 'Benchmark' => sub { 63 | my $buffer = ''; 64 | open my $handle, '>', \$buffer; 65 | local *STDERR = $handle; 66 | my $i = 0; 67 | n { ++$i }; 68 | is $i, 1, 'block has been executed once'; 69 | like $buffer, qr/wallclock/, 'right output'; 70 | n { $i++ } 10; 71 | is $i, 11, 'block has been executed ten times'; 72 | like $buffer, qr/wallclock.*wallclock/s, 'right output'; 73 | }; 74 | 75 | subtest 'Link' => sub { 76 | is l("http://mojolicious.org/")->path, '/', 'right result'; 77 | }; 78 | 79 | done_testing(); 80 | -------------------------------------------------------------------------------- /t/mojolicious/ojo_signatures.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_PROXY} = 0; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::More; 9 | 10 | BEGIN { plan skip_all => 'Perl 5.20+ required for this test!' if $] < 5.020 } 11 | 12 | use ojo; 13 | 14 | my $app = a('/' => sub ($c) { $_->render(data => 'signatures work') }); 15 | my $tx = $app->ua->get('/'); 16 | ok !$tx->error, 'no error'; 17 | is $tx->res->code, 200, 'right status'; 18 | is $tx->res->body, 'signatures work', 'right content'; 19 | 20 | done_testing(); 21 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo.ab1234cd5678ef.css: -------------------------------------------------------------------------------- 1 | /* 2 | * foo.css asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo.ab1234cd5678ef.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo.js asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo/bar.321.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo/bar.js asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo/bar/baz.123.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo/bar/baz.js asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo/bar/baz.development.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo/bar/baz.js development asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo/bar/test.ab1234cd5678ef.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo/bar/test.min.js asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/assets/foo/bar/yada.css: -------------------------------------------------------------------------------- 1 | /* 2 | * foo/bar/yada.css asset 3 | */ 4 | -------------------------------------------------------------------------------- /t/mojolicious/public/hello.txt: -------------------------------------------------------------------------------- 1 | Hello Mojo from a static file! 2 | -------------------------------------------------------------------------------- /t/mojolicious/public/hello2.txt: -------------------------------------------------------------------------------- 1 | X -------------------------------------------------------------------------------- /t/mojolicious/public/hello4.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/t/mojolicious/public/hello4.txt -------------------------------------------------------------------------------- /t/mojolicious/public2/hello.txt: -------------------------------------------------------------------------------- 1 | Also higher precedence! 2 | -------------------------------------------------------------------------------- /t/mojolicious/public2/hello3.txt: -------------------------------------------------------------------------------- 1 | Hello Mojo from... ALL GLORY TO THE HYPNOTOAD! 2 | -------------------------------------------------------------------------------- /t/mojolicious/public_dev/another/file: -------------------------------------------------------------------------------- 1 | Hello Mojolicious! 2 | -------------------------------------------------------------------------------- /t/mojolicious/public_dev/hello.txt: -------------------------------------------------------------------------------- 1 | Hello Mojo from a development static file! 2 | -------------------------------------------------------------------------------- /t/mojolicious/secret.txt: -------------------------------------------------------------------------------- 1 | Secret file outside of the web root. 2 | -------------------------------------------------------------------------------- /t/mojolicious/signatures_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::Mojo; 6 | use Test::More; 7 | 8 | BEGIN { plan skip_all => 'Perl 5.20+ required for this test!' if $] < 5.020 } 9 | 10 | use Mojolicious::Lite -signatures; 11 | 12 | helper send_json => sub ($c, $hash) { $c->send({json => $hash}) }; 13 | 14 | get '/' => sub ($c) { 15 | $c->render(text => 'works!'); 16 | }; 17 | 18 | websocket '/json' => sub ($c) { 19 | $c->on( 20 | json => sub ($c, $hash) { 21 | $c->send_json($hash); 22 | } 23 | ); 24 | }; 25 | 26 | my $t = Test::Mojo->new; 27 | 28 | subtest 'Plain action' => sub { 29 | $t->get_ok('/')->status_is(200)->content_is('works!'); 30 | }; 31 | 32 | subtest 'WebSocket' => sub { 33 | $t->websocket_ok('/json')->send_ok({json => {snowman => '☃'}})->message_ok->json_message_is('' => {snowman => '☃'}) 34 | ->finish_ok; 35 | }; 36 | 37 | done_testing(); 38 | -------------------------------------------------------------------------------- /t/mojolicious/single_file_test_app.conf: -------------------------------------------------------------------------------- 1 | {single_file => 'works!'}; 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/23.html.epl: -------------------------------------------------------------------------------- 1 | %= 20 + 3 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/WithGreenLayout.html.epl: -------------------------------------------------------------------------------- 1 | % shift->stash(layout => 'green'); 2 | Seems to work! 3 | -------------------------------------------------------------------------------- /t/mojolicious/templates/dies_too.html.ep: -------------------------------------------------------------------------------- 1 | tset 2 | % die; 3 | 321 4 | -------------------------------------------------------------------------------- /t/mojolicious/templates/encoding.koi8-r.ep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojolicious/mojo/8f5bca94ef660bca2b67215f6ed2051912e75ba0/t/mojolicious/templates/encoding.koi8-r.ep -------------------------------------------------------------------------------- /t/mojolicious/templates/exception.html.epl: -------------------------------------------------------------------------------- 1 | % my $self = shift; 2 | % if ($self->app->mode eq 'development') { 3 | %= $self->stash('exception')->message 4 | % } 5 | % else { 6 | Not development mode error! 7 | % } 8 | -------------------------------------------------------------------------------- /t/mojolicious/templates/exception.testing.html.ep: -------------------------------------------------------------------------------- 1 | Testing <%= $exception->message %>! 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/foo/bar.rss.ep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/foo/bar/index.html.epl: -------------------------------------------------------------------------------- 1 | % my $c = shift; 2 | Hello Mojo from the other template <%= $c->url_for %>! 3 | -------------------------------------------------------------------------------- /t/mojolicious/templates/foo/bar/test.html.ep: -------------------------------------------------------------------------------- 1 | Class <%= $msg %>! -------------------------------------------------------------------------------- /t/mojolicious/templates/foo/index.html.xpl: -------------------------------------------------------------------------------- 1 | % my $c = shift; 2 | % $c->stash(handler => 'epl'); 3 | Hello Mojo from the template <%= $c->url_for %>! <%= $c->stash('msg') %> 4 | -------------------------------------------------------------------------------- /t/mojolicious/templates/foo/yada.html.ep: -------------------------------------------------------------------------------- 1 | look ma! no action! 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/layouts/default.html.epl: -------------------------------------------------------------------------------- 1 | % my $self = shift; 2 | 3 | 4 | 5 | 6 | <%= $self->render_to_string(template => '23') %><%= $self->content %> 7 | 8 | 9 | -------------------------------------------------------------------------------- /t/mojolicious/templates/layouts/green.html.epl: -------------------------------------------------------------------------------- 1 | % my $self = shift; 2 | 3 | 4 | 5 | 6 | Same old in green <%= $self->content %> 7 | 8 | 9 | -------------------------------------------------------------------------------- /t/mojolicious/templates/not_found.testing.html.ep: -------------------------------------------------------------------------------- 1 | Testing not found! 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/simple.html.pod: -------------------------------------------------------------------------------- 1 | =head1 Test123 2 | 3 | It C! 4 | 5 | =cut 6 | -------------------------------------------------------------------------------- /t/mojolicious/templates/syntaxerror.html.epl: -------------------------------------------------------------------------------- 1 | % { 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates/withblock.txt.epl: -------------------------------------------------------------------------------- 1 | <% my $block = begin %> 2 | <% my $name = shift; =%> 3 | Hello <%= $name %>. 4 | <% end %> 5 | %= $block->('Baerbel') 6 | %= $block->('Wolfgang') 7 | -------------------------------------------------------------------------------- /t/mojolicious/templates2/42.html+test.ep: -------------------------------------------------------------------------------- 1 | The answer is <%= 40 + 3 %>! 2 | -------------------------------------------------------------------------------- /t/mojolicious/templates2/42.html.ep: -------------------------------------------------------------------------------- 1 | The answer is <%= 40 + 2 %>. -------------------------------------------------------------------------------- /t/mojolicious/templates2/foo/yada.html.epl: -------------------------------------------------------------------------------- 1 | Higher precedence! 2 | -------------------------------------------------------------------------------- /t/mojolicious/testing_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { 4 | $ENV{MOJO_MODE} = 'testing'; 5 | $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; 6 | } 7 | 8 | use Test::Mojo; 9 | use Test::More; 10 | 11 | use Mojo::File qw(curfile); 12 | use lib curfile->sibling('lib')->to_string; 13 | 14 | my $t = Test::Mojo->new('MojoliciousTest'); 15 | my $success = ''; 16 | $t->or(sub { $success .= 'one' }) 17 | ->success(1) 18 | ->or(sub { $success .= 'two' }) 19 | ->success(!1) 20 | ->or(sub { $success .= shift->app->mode }); 21 | is $success, 'onetesting', 'two callbacks have been executed'; 22 | ok $t->get_ok('/')->success, 'test was successful'; 23 | 24 | # SyntaxError::foo in testing mode (syntax error in controller) 25 | $t->get_ok('/syntax_error/foo') 26 | ->status_is(500) 27 | ->header_is(Server => 'Mojolicious (Perl)') 28 | ->content_like(qr/Testing Missing/); 29 | 30 | # Foo::syntaxerror in testing mode (syntax error in template) 31 | $t->get_ok('/foo/syntaxerror') 32 | ->status_is(500) 33 | ->header_is(Server => 'Mojolicious (Perl)') 34 | ->content_like(qr/Testing Missing/); 35 | 36 | # Static file /hello.txt in testing mode 37 | $t->get_ok('/hello.txt') 38 | ->status_is(200) 39 | ->header_is(Server => 'Mojolicious (Perl)') 40 | ->content_like(qr/Hello Mojo from a static file!/); 41 | 42 | # Foo::bar in testing mode (missing action) 43 | $t->get_ok('/foo/baz')->status_is(500)->header_is(Server => 'Mojolicious (Perl)')->content_like(qr/Testing/); 44 | 45 | # Try to access a file which is not under the web root via path traversal in 46 | # testing mode 47 | $t->get_ok('/../../mojolicious/secret.txt') 48 | ->status_is(404) 49 | ->header_is(Server => 'Mojolicious (Perl)') 50 | ->content_like(qr/Testing not found/); 51 | 52 | # Try to access a file which is not under the web root via path traversal in 53 | # testing mode (goes back and forth one directory) 54 | $t->get_ok('/another/../../../mojolicious/secret.txt') 55 | ->status_is(404) 56 | ->header_is(Server => 'Mojolicious (Perl)') 57 | ->content_like(qr/Testing not found/); 58 | 59 | # Try to access a file which is not under the web root via path traversal in 60 | # testing mode (triple dot) 61 | $t->get_ok('/.../mojolicious/secret.txt') 62 | ->status_is(404) 63 | ->header_is(Server => 'Mojolicious (Perl)') 64 | ->content_like(qr/Testing not found/); 65 | 66 | # Try to access a file which is not under the web root via path traversal in 67 | # testing mode (backslashes) 68 | $t->get_ok('/..\\..\\mojolicious\\secret.txt') 69 | ->status_is(404) 70 | ->header_is(Server => 'Mojolicious (Perl)') 71 | ->content_like(qr/Testing not found/); 72 | 73 | # Try to access a file which is not under the web root via path traversal in 74 | # testing mode (escaped backslashes) 75 | $t->get_ok('/..%5C..%5Cmojolicious%5Csecret.txt') 76 | ->status_is(404) 77 | ->header_is(Server => 'Mojolicious (Perl)') 78 | ->content_like(qr/Testing not found/); 79 | 80 | # Check that backslashes in query or fragment parts don't block access in 81 | # testing mode 82 | $t->get_ok('/hello.txt?one=\\1#two=\\2')->status_is(200)->content_like(qr/Hello Mojo from a static file!/); 83 | 84 | done_testing(); 85 | -------------------------------------------------------------------------------- /t/mojolicious/tls_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::More; 6 | use Mojo::IOLoop::TLS; 7 | 8 | plan skip_all => 'set TEST_TLS to enable this test (developer only!)' unless $ENV{TEST_TLS} || $ENV{TEST_ALL}; 9 | plan skip_all => 'IO::Socket::SSL 2.009+ required for this test!' unless Mojo::IOLoop::TLS->can_tls; 10 | 11 | use Mojo::IOLoop; 12 | use Mojo::UserAgent; 13 | use Mojolicious::Lite; 14 | use Test::Mojo; 15 | 16 | # Silence 17 | app->log->level('fatal'); 18 | 19 | # Secure sessions 20 | app->sessions->secure(1); 21 | 22 | get '/login' => sub { 23 | my $c = shift; 24 | my $name = $c->param('name') || 'anonymous'; 25 | $c->session(name => $name); 26 | $c->render(text => "Welcome $name!"); 27 | }; 28 | 29 | get '/again' => sub { 30 | my $c = shift; 31 | my $name = $c->session('name') || 'anonymous'; 32 | $c->render(text => "Welcome back $name!"); 33 | }; 34 | 35 | get '/logout' => sub { 36 | my $c = shift; 37 | $c->session(expires => 1); 38 | $c->redirect_to('login'); 39 | }; 40 | 41 | # Use HTTPS 42 | my $t = Test::Mojo->new; 43 | $t->ua->max_redirects(5); 44 | $t->reset_session->ua->server->url('https'); 45 | 46 | # Login 47 | $t->get_ok('/login?name=sri' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome sri!'); 48 | ok $t->tx->res->cookie('mojolicious')->expires, 'session cookie expires'; 49 | ok $t->tx->res->cookie('mojolicious')->secure, 'session cookie is secure'; 50 | 51 | # Return 52 | $t->get_ok('/again' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome back sri!'); 53 | 54 | # Logout 55 | $t->get_ok('/logout' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome anonymous!'); 56 | 57 | # Expired session 58 | $t->get_ok('/again' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome back anonymous!'); 59 | 60 | # No session 61 | $t->get_ok('/logout' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome anonymous!'); 62 | 63 | # Use HTTP 64 | $t->reset_session->ua->server->url('http'); 65 | 66 | # Login again 67 | $t->reset_session->get_ok('/login?name=sri')->status_is(200)->content_is('Welcome sri!'); 68 | 69 | # Return 70 | $t->get_ok('/again')->status_is(200)->content_is('Welcome back anonymous!'); 71 | 72 | # Use HTTPS again (without expiration) 73 | $t->reset_session->ua->server->url('https'); 74 | app->sessions->default_expiration(0); 75 | 76 | # Login again 77 | $t->get_ok('/login?name=sri' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome sri!'); 78 | ok !$t->tx->res->cookie('mojolicious')->expires, 'session cookie does not expire'; 79 | ok $t->tx->res->cookie('mojolicious')->secure, 'session cookie is secure'; 80 | 81 | # Return 82 | $t->get_ok('/again' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome back sri!'); 83 | 84 | # Logout 85 | $t->get_ok('/logout' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome anonymous!'); 86 | 87 | # Expired session 88 | $t->get_ok('/again' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome back anonymous!'); 89 | 90 | # No session 91 | $t->get_ok('/logout' => {'X-Forwarded-Proto' => 'https'})->status_is(200)->content_is('Welcome anonymous!'); 92 | 93 | done_testing(); 94 | -------------------------------------------------------------------------------- /t/mojolicious/twinkle_lite_app.conf: -------------------------------------------------------------------------------- 1 | . my $bar = $foo; 2 | { 3 | "test": *** $bar ** 4 | } -------------------------------------------------------------------------------- /t/mojolicious/twinkle_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::Mojo; 6 | use Test::More; 7 | use Mojolicious::Lite; 8 | use Mojo::Util; 9 | 10 | # Custom format 11 | app->renderer->default_format('foo'); 12 | 13 | # Twinkle template syntax 14 | my $twinkle = { 15 | append => '$self->res->headers->header("X-Append" => $prepended);', 16 | auto_escape => 0, 17 | capture_end => '-', 18 | capture_start => '+', 19 | escape => sub { 20 | my $str = shift; 21 | $str =~ s/ '*', 25 | expression_mark => '*', 26 | line_start => '.', 27 | namespace => 'TwinkleSandBoxTest', 28 | prepend => 'my $prepended = $self->config("foo");', 29 | tag_end => '**', 30 | tag_start => '**', 31 | trim_mark => '*' 32 | }; 33 | 34 | # Renderer 35 | plugin EPRenderer => {name => 'twinkle', template => $twinkle}; 36 | 37 | subtest 'Configuration' => sub { 38 | app->defaults(foo_test => 23); 39 | my $config = plugin JSONConfig => { 40 | default => {foo => 'bar'}, 41 | ext => 'conf', 42 | template => 43 | {%$twinkle, append => '$app->defaults(foo_test => 24)', prepend => 'my $foo = app->defaults("foo_test");'} 44 | }; 45 | is $config->{foo}, 'bar', 'right value'; 46 | is $config->{test}, 23, 'right value'; 47 | is app->defaults('foo_test'), 24, 'right value'; 48 | }; 49 | 50 | get '/' => {name => ''} => 'index'; 51 | 52 | get '/advanced' => 'advanced'; 53 | 54 | get '/rest' => [format => 'html'] => {format => undef} => sub { 55 | shift->respond_to(foo => {text => 'foo works!'}, html => {text => 'html works!'}) 56 | ->res->headers->header('X-Rest' => 1); 57 | }; 58 | 59 | get '/dead' => sub {die}; 60 | 61 | my $t = Test::Mojo->new; 62 | 63 | subtest 'Basic template with "twinkle" syntax and "ep" layout' => sub { 64 | $t->get_ok('/') 65 | ->status_is(200) 66 | ->header_is('X-Append' => 'bar') 67 | ->content_like(qr/testHello !bar TwinkleSandBoxTest123/); 68 | }; 69 | 70 | subtest 'Advanced template with "twinkle" syntax' => sub { 71 | $t->get_ok('/advanced')->status_is(200)->header_is('X-Append' => 'bar')->content_is("<escape me>\n123423"); 72 | }; 73 | 74 | subtest 'REST request for "foo" format' => sub { 75 | $t->get_ok('/rest')->status_is(200)->header_is('X-Rest' => 1)->content_is('foo works!'); 76 | }; 77 | 78 | subtest 'REST request for "html" format' => sub { 79 | $t->get_ok('/rest.html')->status_is(200)->header_is('X-Rest' => 1)->content_is('html works!'); 80 | }; 81 | 82 | subtest 'Exception template with custom format' => sub { 83 | $t->get_ok('/dead')->status_is(500)->content_is("foo exception!\n"); 84 | }; 85 | 86 | done_testing(); 87 | 88 | __DATA__ 89 | @@ index.foo.twinkle 90 | . layout 'twinkle'; 91 | Hello *** $name **!\ 92 | *** $prepended ** *** __PACKAGE__ **\ 93 | 94 | @@ layouts/twinkle.foo.ep 95 | test<%= content %>123\ 96 | 97 | @@ advanced.foo.twinkle 98 | .** "" 99 | . my $numbers = [1 .. 4]; 100 | ** for my $i (@$numbers) { *** 101 | *** $i *** 102 | ** } *** 103 | ** my $foo = (+*** 23 **-)->();*** *** $foo *** 104 | 105 | @@ exception.foo.ep 106 | foo exception! 107 | 108 | @@ not_found.foo.ep 109 | foo not found! 110 | -------------------------------------------------------------------------------- /t/mojolicious/upload_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::Mojo; 6 | use Test::More; 7 | use Mojo::Asset::File; 8 | use Mojo::Content::MultiPart; 9 | use Mojo::Content::Single; 10 | use Mojolicious::Lite; 11 | 12 | get '/request_size' => sub { 13 | my $c = shift; 14 | $c->render(text => $c->req->max_message_size); 15 | }; 16 | 17 | post '/upload' => sub { 18 | my $c = shift; 19 | my $file = $c->param('file'); 20 | my $headers = $file->headers; 21 | $c->render(text => $file->filename 22 | . $file->asset->slurp 23 | . $c->param('test') 24 | . ($headers->content_type // '') 25 | . ($headers->header('X-X') // '')); 26 | }; 27 | 28 | post '/multi' => sub { 29 | my $c = shift; 30 | my @uploads = map { @{$c->every_param($_)} } @{$c->every_param('name')}; 31 | $c->render(text => join '', map { $_->filename, $_->asset->slurp } @uploads); 32 | }; 33 | 34 | my $t = Test::Mojo->new; 35 | 36 | subtest 'Request size limit' => sub { 37 | $t->get_ok('/request_size')->status_is(200)->content_is(16777216); 38 | $t->app->max_request_size(0); 39 | $t->get_ok('/request_size')->status_is(200)->content_is(0); 40 | }; 41 | 42 | subtest 'Asset and filename' => sub { 43 | my $file = Mojo::Asset::File->new->add_chunk('lalala'); 44 | $t->post_ok('/upload' => form => {file => {file => $file, filename => 'x'}, test => 'tset'}) 45 | ->status_is(200) 46 | ->content_is('xlalalatset'); 47 | }; 48 | 49 | subtest 'Path' => sub { 50 | my $file = Mojo::Asset::File->new->add_chunk('lalala'); 51 | $t->post_ok('/upload' => form => {file => {file => $file->path}, test => 'foo'}) 52 | ->status_is(200) 53 | ->content_like(qr!lalalafoo$!); 54 | }; 55 | 56 | subtest 'Memory' => sub { 57 | $t->post_ok('/upload' => form => {file => {content => 'alalal'}, test => 'tset'}) 58 | ->status_is(200) 59 | ->content_is('filealalaltset'); 60 | }; 61 | 62 | subtest 'Memory with headers' => sub { 63 | my $hash = {content => 'alalal', 'Content-Type' => 'foo/bar', 'X-X' => 'Y'}; 64 | $t->post_ok('/upload' => form => {file => $hash, test => 'tset'}) 65 | ->status_is(200) 66 | ->content_is('filealalaltsetfoo/barY'); 67 | }; 68 | 69 | subtest 'Multiple file uploads' => sub { 70 | $t->post_ok( 71 | '/multi?name=file1&name=file2' => form => {file1 => {content => '1111'}, file2 => {content => '11112222'}}) 72 | ->status_is(200) 73 | ->content_is('file11111file211112222'); 74 | }; 75 | 76 | subtest 'Multiple file uploads reverse' => sub { 77 | $t->post_ok( 78 | '/multi?name=file2&name=file1' => form => {file1 => {content => '1111'}, file2 => {content => '11112222'}}) 79 | ->status_is(200) 80 | ->content_is('file211112222file11111'); 81 | }; 82 | 83 | subtest 'Multiple file uploads with same name' => sub { 84 | $t->post_ok('/multi?name=file' => form => 85 | {file => [{content => 'just', filename => 'one.txt'}, {content => 'works', filename => 'two.txt'}]}) 86 | ->status_is(200) 87 | ->content_is('one.txtjusttwo.txtworks'); 88 | }; 89 | 90 | done_testing(); 91 | -------------------------------------------------------------------------------- /t/mojolicious/upload_stream_lite_app.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } 4 | 5 | use Test::Mojo; 6 | use Test::More; 7 | use Mojolicious::Lite; 8 | use Scalar::Util qw(weaken); 9 | 10 | # Stream multipart uploads into cache 11 | my %cache; 12 | hook after_build_tx => sub { 13 | my $tx = shift; 14 | 15 | weaken $tx; 16 | $tx->req->content->on( 17 | upgrade => sub { 18 | my ($single, $multi) = @_; 19 | 20 | return unless $tx->req->url->path->contains('/upload'); 21 | 22 | my $id = $tx->req->url->query->param('id'); 23 | $multi->on( 24 | part => sub { 25 | my ($multi, $single) = @_; 26 | $single->on( 27 | body => sub { 28 | my $single = shift; 29 | return unless $single->headers->content_disposition =~ /my_file/; 30 | $single->unsubscribe('read'); 31 | $single->on(read => sub { $cache{$id} .= pop }); 32 | } 33 | ); 34 | } 35 | ); 36 | } 37 | ); 38 | }; 39 | 40 | post '/upload' => sub { 41 | my $c = shift; 42 | my $id = $c->param('id'); 43 | $c->render(data => $cache{$id}); 44 | }; 45 | 46 | get '/download' => sub { 47 | my $c = shift; 48 | $c->render(data => $cache{$c->param('id')}); 49 | }; 50 | 51 | my $t = Test::Mojo->new; 52 | 53 | subtest 'Small upload' => sub { 54 | $t->post_ok('/upload?id=23' => form => {my_file => {content => 'whatever'}})->status_is(200)->content_is('whatever'); 55 | }; 56 | 57 | subtest 'Small download' => sub { 58 | $t->get_ok('/download?id=23')->status_is(200)->content_is('whatever'); 59 | }; 60 | 61 | subtest 'Big upload' => sub { 62 | $t->post_ok('/upload?id=24' => form => {my_file => {content => '1234' x 131072}}) 63 | ->status_is(200) 64 | ->content_is('1234' x 131072); 65 | }; 66 | 67 | subtest 'Big download' => sub { 68 | $t->get_ok('/download?id=24')->status_is(200)->content_is('1234' x 131072); 69 | }; 70 | 71 | subtest 'Small download again' => sub { 72 | $t->get_ok('/download?id=23')->status_is(200)->content_is('whatever'); 73 | }; 74 | 75 | done_testing(); 76 | -------------------------------------------------------------------------------- /t/mojolicious/yaml_config_lite_app.yaml: -------------------------------------------------------------------------------- 1 | foo: yada 2 | utf8: утф 3 | -------------------------------------------------------------------------------- /t/mojolicious/yaml_config_lite_app.yml: -------------------------------------------------------------------------------- 1 | %# Just a comment 2 | foo: barbaz 3 | utf: утф 4 | plugins: 5 | - MojoliciousTest::Plugin::DeploymentPlugin: {} 6 | - MojoliciousTest::Plugin::DeploymentPlugin: 7 | name: another_helper 8 | message: works too! 9 | -------------------------------------------------------------------------------- /t/mojolicious/yaml_config_lite_app_abs.development.yml: -------------------------------------------------------------------------------- 1 | --- 2 | absolute_dev: dev works too <%= app->moniker %>!!! 3 | -------------------------------------------------------------------------------- /t/mojolicious/yaml_config_lite_app_abs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | absolute: works <%= 'too' %>!!! 3 | -------------------------------------------------------------------------------- /t/pod.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | plan skip_all => 'set TEST_POD to enable this test (developer only!)' unless $ENV{TEST_POD} || $ENV{TEST_ALL}; 6 | plan skip_all => 'Test::Pod 1.14+ required for this test!' unless eval 'use Test::Pod 1.14; 1'; 7 | 8 | all_pod_files_ok(); 9 | -------------------------------------------------------------------------------- /t/pod_coverage.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | plan skip_all => 'set TEST_POD to enable this test (developer only!)' unless $ENV{TEST_POD} || $ENV{TEST_ALL}; 6 | plan skip_all => 'Test::Pod::Coverage 1.04+ required for this test!' unless eval 'use Test::Pod::Coverage 1.04; 1'; 7 | 8 | # async/await hooks 9 | my @await = ( 10 | qw(AWAIT_CHAIN_CANCEL AWAIT_CLONE AWAIT_DONE AWAIT_FAIL AWAIT_GET AWAIT_IS_CANCELLED AWAIT_IS_READY AWAIT_NEW_DONE), 11 | qw(AWAIT_NEW_FAIL AWAIT_ON_CANCEL AWAIT_ON_READY AWAIT_WAIT) 12 | ); 13 | 14 | # These are base utils only to be used in Mojo::Base and not elsewhere 15 | my @base_utils = (qw(class_to_path monkey_patch)); 16 | 17 | all_pod_coverage_ok({also_private => ['BUILD_DYNAMIC', @await, @base_utils, 'spurt']}); 18 | --------------------------------------------------------------------------------