Coder Social home page Coder Social logo

web-app's Introduction

Web -- A Web Application foundation for Raku Build Status

Introduction

Web is a simple web application library set for Raku, that uses the PSGI interface. It is based on work done for the original Web.pm project, as well as WebRequest, and November.

It consists of a few libraries, the most important of which are:

  • Web::Request

    Contains information about the HTTP request.

  • Web::Response

    Builds a PSGI compliant response.

  • Web::App

    A minimal web framework, uses backend engines (see below).

  • Web::App::Dispatch

    An extension of Web::App support advanced dispatch rules.

Web::Request

Web::Request is similar to CGI.pm or Plack::Request from Perl 5.

It supports P6SGI 0.7Draft (recommended), P6SGI 0.4Draft, PSGI Classic, SCGI standalone, FastCGI standalone, and mod-perl6. It can be forced to use standard CGI, but that's really not recommended. Currently only supports GET and non-multipart POST. We are planning on adding multi-part POST including file uploads, and some optional magic parameters similar to the ones in PHP.

Web::Response

An easy to use object that builds a P6SGI/PSGI compliant response. Supports some quick methods such as content-type() and redirect() to automatically create appropriate headers.

Web::App

Puts the above two together, along with a backend engine, and a context helper object, and makes building web apps really easy.

It supports any backend engine that provides a P6SGI/PSGI compliant interface, and a handle() method that takes a subroutine as a parameter (the subroutine must take a hash representing the environment), or an app() method that takes the aforementioned subroutine as a parameter, and a run() method to start processing requests.

See the list below for details of which libraries to use.

The context helper object provides wrappers to the Request and Response objects, including some magic functions that enable features otherwise not possible, such as a far more advanced redirect() method.

Web::App::Dispatch

Web::App::Dispatch is an extension of Web::App, that also supports advanced action dispatch based on rules.

Rather than supporting a single handler, you can have multiple rules, which will perform specific actions, including running handlers, based on environment variables such as the URL path, host, or protocol.

Actions can include redirection, setting content-type, adding headers, or calling a handler (either a code block, or an object with a handle() method.) A default handler can be called if no rules are matched.

Related Projects and Extensions

  • Web::App::MVC

    A MVC web framework built upon Web::App::Dispatch.

  • Web::App::Ballet

    A Dancer-like interface to Web::App::Dispatch. NOTE: This project will be merged with Bailador in the near future.

Requirements

Connector Engine Modules

None of the connector modules are required by default, so you'll need to install them yourself whichever one you want.

  • SCGI

    Offers the best integration with existing web servers, such as Apache, lighttpd, etc. It's like FastCGI, only simpler and faster.

  • FastCGI

    A complex and comprehensive protocol, the Raku implementation is considerably slower than SCGI, but offers more advanced features.

  • HTTP::Easy

    Web::App supports the HTTP::Easy::PSGI adapter, which provides a nice clean standalone HTTP server with PSGI application support. This provides GET and POST support including multipart/form-data.

  • HTTP::Server::Simple

    This library has not been tested, but Web::App should be able to work with the HTTP::Server::Simple::PSGI interface without any modifications.

Examples

Example 1

This is an example of the use of Web::App and it's wrapper magic. Handlers for Web::App are sent a special Web::App::Context object which wraps the Request and Response, and provides some extra magic that makes life a lot easier.

  use SCGI;
  use Web::App;

  my $scgi = SCGI.new(:port(8118));
  my $app = Web::App.new($scgi);

  my $handler = sub ($context) {
    given $context.path {
      when '/' {
        $context.content-type('text/plain');
        $context.send("Request parameters:");
        $context.send($context.req.params.fmt('%s: %s', "\n"));
        my $name = $context.get('name');
        if $name {
          $context.send("Hello $name");
        }
      }
      default {
        ## We don't support anything else, send them home.
        $context.redirect('/');
      }
    }
  }

  $app.run: $handler;

  ## End of script.

Example 2

This example is using Web::App::Dispatch and some of its many rules.

  class RedirectHandler {
    has $.site;
    method handle ($context) {
      $context.redirect($.site);
    }
  }

  use SCGI;
  use Web::App::Dispatch;
  
  my $scgi = SCGI.new(:port(8118));
  my $app  = Web::App::Dispatch.new($scgi);

  my $main = sub ($context) {
    $context.set-status(200);
    $context.content-type('text/plain');
    my $name = $context.get(:default<World>, 'name');
    $context.send("Hello $name");
  }

  $app.add(:handler($main), :default); ## Gets called if no other rules match.

  ## Let's add an object-based handler on the '/test' URL path.
  my $test = RedirectHandler.new(:site<http://huri.net>);
  $app.add(:path</test>, :handler($test));

  ## Another form of redirect, using an action rule.
  $app.add(:proto<http>, :redirect<https>);

  ## A slurp handler.
  $app.add(:path</slurp>, :slurp<./webroot/hello.text>);

  ## Send a file to the client browser.
  $app.add(:path</file>, :sendfile<./webroot/data.zip>);

  ## Okay, let's run the app.
  $app.run;

  ## End of script.

Example 3

This is an example of using Web::Request and Web::Response together with HTTP::Easy's PSGI adapter, without using Web::App as a wrapper.

  use HTTP::Easy::PSGI;
  use Web::Request;
  use Web::Response;

  my $http = HTTP::Easy::PSGI.new(); ## Default port is 8080.

  my $handler = sub (%env) {
    my $req = Web::Request.new(%env);
    my $res = Web::Response.new();
    $res.set-status(200);
    $res.add-header('Content-Type' => 'text/plain');
    $res.send("Request parameters:");
    $res.send($req.params.fmt('%s: %s', "\n"));
    my $name = $req.get('name');
    if $name {
      $res.send("Hello $name");
    }
    return $res.response;
  }

  $http.handle: $handler;

  ## End of script.

Further Examples

For more examples, including using other backends, more dispatch rules, and lots of other cool stuff, see the examples in the 'test' folder.

Install

Install directly from this repo using

zef install Web

or download this repo and do

zef install --deps-only .

if you want to try and contribute to it.

TODO

  • Finish testing framework, and write some tests.
  • Fix binary uploads. They need to use Buf instead of Str.
  • Add more pre-canned headers and automation to Web::Response. Sending files back to the client should be made easy.
  • Add more useful helpers to Web::App::Context.
  • I'm planning on refactoring Web::App::Dispatch into a collection of smaller components, with a more rubust routing system.

Authors

License

Artistic License 2.0

web-app's People

Contributors

davepagurek avatar frankgard avatar jj avatar jjatria avatar lizmat avatar retupmoca avatar softmoth avatar supernovus avatar tbrownaw avatar timo avatar tszypenbejl avatar zoffixznet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

web-app's Issues

P6SGI Support

The Perl 6 PSGI library supports the newer P6SGI specification. This library needs to be updated to use it if available.

Cannot assign to an immutable value

I can't get example1 to work. (Even after adding the missing closing curly brace.) It gives me an error:

Cannot assign to an immutable value
  in method redirect at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/703BE3353C110F46FB547F2908922DE8094D6066 line 43
  in block  at example1.pl6 line 20
  in sub  at example1.pl6 line 8
  in sub  at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/FA7ACBEA2597BAC9ACB1EFC4E0E5C472983C6F4D line 46
  in block  at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/BA579BD0E7D8681BD6671CBF035B1CFCC192A674 line 70
  in method handle at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/BA579BD0E7D8681BD6671CBF035B1CFCC192A674 line 64
  in method _dispatch at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/FA7ACBEA2597BAC9ACB1EFC4E0E5C472983C6F4D line 66
  in method run at /home/ec2-user/.rakudobrew/moar-nom/install/share/perl6/site/sources/FA7ACBEA2597BAC9ACB1EFC4E0E5C472983C6F4D line 58
  in block <unit> at example1.pl6 line 25

My guess is that "method redirect line 43" is actually in lib/Web/App/Context.pm6.

I downloaded this module with panda.

Instantiating WWW::Request hangs on POST/PUT w/o body data

When attempting to instantiate an instance of WWW::Request for POST or PUT requests w/o body content the new() method hangs forever. Here are some oneliners that show the issue.

Interestingly enough, on a running server, after you submit a POST/PUT request with body data, submitting one w/o body data does not cause new() to hang. I haven't had a chance to investigate exactly what is going wrong yet but wanted to get this bug report in.

_GET REQUESTS WORK_
new-host:~ bob$ perl6 -e 'use WWW::Request; say "going to create"; my $r = WWW::Request.new({"SERVER_PROTOCOL" => "HTTP/1.1", "REQUEST_METHOD" => "GET", "QUERY_STRING" => "", "PATH_INFO" => "/addSystem", "REQUEST_URI" => "/addSystem", "SERVER_NAME" => "localhost", "SERVER_PORT" => 8080, "HTTP_USER_AGENT" => "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5", "HTTP_HOST" => "localhost:8080", "HTTP_ACCEPT" => "/", "psgi.version" => [1, 0], "psgi.url_scheme" => "http", "psgi.multithread" => Bool::False, "psgi.multiprocess" => Bool::False, "psgi.input" => Buf.new(), "psgi.errors" => IO.new(ins => 0, chomp => Bool::True, path => Any), "psgi.run_once" => Bool::False, "psgi.nonblocking" => Bool::False, "psgi.streaming" => Bool::False}); say "created!"'
going to create
created!

_POST HANGS_
new-host:~ bob$ perl6 -e 'use WWW::Request; say "going to create"; my $r = WWW::Request.new({"SERVER_PROTOCOL" => "HTTP/1.1", "REQUEST_METHOD" => "POST", "QUERY_STRING" => "", "PATH_INFO" => "/addSystem", "REQUEST_URI" => "/addSystem", "SERVER_NAME" => "localhost", "SERVER_PORT" => 8080, "HTTP_USER_AGENT" => "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5", "HTTP_HOST" => "localhost:8080", "HTTP_ACCEPT" => "/", "psgi.version" => [1, 0], "psgi.url_scheme" => "http", "psgi.multithread" => Bool::False, "psgi.multiprocess" => Bool::False, "psgi.input" => Buf.new(), "psgi.errors" => IO.new(ins => 0, chomp => Bool::True, path => Any), "psgi.run_once" => Bool::False, "psgi.nonblocking" => Bool::False, "psgi.streaming" => Bool::False}); say "created!"'
going to create

_PUT HANGS_
new-host:~ bob$ perl6 -e 'use WWW::Request; say "going to create"; my $r = WWW::Request.new({"SERVER_PROTOCOL" => "HTTP/1.1", "REQUEST_METHOD" => "PUT", "QUERY_STRING" => "", "PATH_INFO" => "/addSystem", "REQUEST_URI" => "/addSystem", "SERVER_NAME" => "localhost", "SERVER_PORT" => 8080, "HTTP_USER_AGENT" => "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5", "HTTP_HOST" => "localhost:8080", "HTTP_ACCEPT" => "/", "psgi.version" => [1, 0], "psgi.url_scheme" => "http", "psgi.multithread" => Bool::False, "psgi.multiprocess" => Bool::False, "psgi.input" => Buf.new(), "psgi.errors" => IO.new(ins => 0, chomp => Bool::True, path => Any), "psgi.run_once" => Bool::False, "psgi.nonblocking" => Bool::False, "psgi.streaming" => Bool::False}); say "created!"'
going to create

Proper tests

The current "tests" in the test/ folder are actually examples of how to use the app, not tests. However they are being run as tests in Travis CI which fails since they don't return TAP output (and in many cases the dependencies aren't installed for them.)

I want to rename the test/ folder to examples/ and write some actual tests using a web testing framework I've been planning for a while.

As I'm not heavily active with my Perl 6 projects at the moment, this may take a while to get done. However given this is already being tested in Travis CI, this is a slightly higher priority than other issues.

Rakuify

Change to Raku wherever. After that is done, re-release, right not it's relying on a redirection.

Failure to install

Seen in Blin, it can't be installed due to missing Netstring.

[Netstring] ===SORRY!=== Error while compiling /home/jmerelo/.zef/store/perl6-netstring.git/2104c2e6b081feb704bf5f14ff8815bae478ceab/t/01-encoding.t
[Netstring] Could not find Netstring in:
[Netstring]     file#/home/jmerelo/.zef/store/perl6-netstring.git/2104c2e6b081feb704bf5f14ff8815bae478ceab
[Netstring]     inst#/home/jmerelo/.raku
[Netstring]     inst#/home/jmerelo/.rakudobrew/moar-2020.08.2/install/share/perl6/site
[Netstring]     inst#/home/jmerelo/.rakudobrew/moar-2020.08.2/install/share/perl6/vendor
[Netstring]     inst#/home/jmerelo/.rakudobrew/moar-2020.08.2/install/share/perl6/core
[Netstring]     ap#
[Netstring]     nqp#
[Netstring]     perl5#
[Netstring] at /home/jmerelo/.zef/store/perl6-netstring.git/2104c2e6b081feb704bf5f14ff8815bae478ceab/t/01-encoding.t:6

Deprecated call in Web::Request

Saw 1 call to deprecated code during execution.

%new = itemized hash called at:
lib/Web/Request.pm6, line 33
Deprecated since v2014.7, will be removed with release v2015.7!

Please use %new = %(itemized hash) instead.

Please contact the author to have these calls to deprecated code adapted,
so that this message will disappear!

Please note that ALL deprecated features will be removed at the release
of Perl 6.0.0 (expected sometime in 2015).

Web::Request does not parse %-encoded GET params

I have Perl6 installed just 2 days ago with rakudobrew and Web module installed with panda (so I guess everything is up-to-date).

There seems to be a problem with parsing %-encoded GET parameters with Web::Request. You can reproduce the problem like that:

use Web::Request;
my $r = Web::Request.new(Hash.new);
$r.parse-params('url=https%3A%2F%2Fwww.youtube.com');

Perl6 will likely complain about line 223 of Request.pm (sub decode_urlencoded_utf8):
Lists on either side of non-dwimmy hyperop of infix:<+&> are not of the same length

It seems that the problem is caused by the preceding line:

    my @mask = $mask, 0x3f xx $bytes-1;

I managed to work around the problem by replacing that line with:

    my @mask  = $mask;
    @mask.push(0x3f xx $bytes-1) if $bytes > 1;

There is probably a prettier way to fix it but I am not a perl6 expert.

There also seems to be another problem (that will surface once the 1st problem is fixed) in sub percent_hack_start: Parameter '$str' expected a writable container, but got Str value.

I have no idea how to fix this properly, but using substr-rw method in sub unescape works for me:

sub unescape($string is copy) {
  $string ~~ s:g/'+'/ /;
  while $string ~~ / ( [ '%' <[0..9A..F]>**2 ]+ ) / {
    $string .= subst( $0.Str,
      percent_hack_start(decode_urlencoded_utf8($0.Str).substr-rw(0))
    );
  }
  return percent_hack_end($string.substr-rw(0));
}

There definitely has to be a better way to correct that problem (but again, I am new to Perl6).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.