Giter VIP home page Giter VIP logo

modlib's Introduction

modlib

modlib is a small library that makes it easy to write 'mod' plugins for Erlang's httpd server.

Motivation

The standard Erlang language environment ships with a powerful set of network tools in the 'inets' OTP application. This includes a full featured HTTP server in the ``httpd module``_.

httpd supports a plugin model that's similar to Apache's 'mod' API. It also ships with a number of useful mods, including support for authentication, authorization, URL aliases, and several others.

modlib makes it drop dead simple to write extensions for httpd, including Erlang based web apps.

Sample Code

A simple hello world app:

-module(hello_http).

-include_lib("modlib/include/webapp.hrl").

-export([start/1, request/3]).

start(Port) ->
    modlib:start([{port, Port}, {modules, [?MODULE]}]).

request("GET", _Path, _Info) ->
    {ok, {html, "Hello modlib!"}};
request(_Method, _Path, _Info) ->
    {error, "Bad Request"}.

A more complex app (illustrates more modlib features and how Erlang can be used to elegantly structure your HTTP handlers):

-module(echo_http).

-include_lib("modlib/include/webapp.hrl").

-export([start/1, request/3]).

-define(TITLE, "Echo").

start(Port) ->
    modlib:start([{port, Port}, {modules, [?MODULE]}]).

request(Method, Path, Info) ->
    {ok,
     {html,
      ["<html>",
       "<head><title>", ?TITLE, "</title></head>",
       "<body>",
       request_line(Method, Path),
       query_params(Info),
       post_params(Info),
       headers(Info),
       "</body>",
       "</html>"]
     }}.

request_line(Method, Path) ->
    ["<p>", Method, " ", Path, "</p>"].

query_params(Info) ->
    case modlib:parse_qs(Info) of
        [] -> [];
        Params ->
            ["<h4>Query Params</h4>",
             [["<div><b>", Name, "</b>: ", Value, "</div>"]
              || {Name, Value} <- Params]]
    end.

post_params(Info) ->
    case modlib:parse_body(Info) of
        {error, content_type} -> [];
        {ok, []} -> [];
        {ok, Params} ->
            ["<h4>Post Params</h4>",
             [["<div><b>", Name, "</b>: ", Value, "</div>"]
              || {Name, Value} <- Params]]
    end.

headers(Info) ->
    ["<h4>Headers</h4>",
     [["<div><b>", Name, "</b>: ", Value, "</div>"]
      || {Name, Value} <- modlib:headers(Info)]].

Important notes:

  • You must include "webapp.hr" in your webapp modules (see above)
  • modlib doesn't come with template support - but erlydtl is recommended as it's outstanding
  • modlib wraps the httpd mod interface - while you don't need to know httpd inside and out, it won't hurt to read the docs
  • Refer to "Webapp Cheat Sheet" below for more details

Webapp Cheat Sheet

A "web app" is an httpd mod that implements request/3, which is called with the HTTP request details and returns the HTTP response.

The only "magic" here is that the required include of webapp.hrl uses a parse transform to wrap the rather complex modlib API.

The request method looks like this:

@spec
request(Method, Path, Info) -> Response
    Method = "GET" | "POST" | "PUT" | "DELETE" | "HEAD"
    Path = string()
    Info = #mod{}
    Response = {ok, Content} |
               {error, Content} |
               {not_found, Content} |
               {redirect, Location} |
               {Code, Content} |
               {Code, Headers, Content} |
               not_handled
    Content = iolist() | {Type, iolist()}
    Type = text | html | xml | json
    Location = string()
    Code = integer()
    Headers = [{string(), string()}]
@end

Refer to include/httpd.hrl for details on the mod record (typically not used, but needed for some cases).

The Path does not contain query string or reference elements. The original request URL is in the request_url element of Info#mod.

Use modlib:parse_qs(Info) to return a proplist of query string params.

Use modlib:parse_body(Info) to return a proplist of form-urlencoded body params. (Note that the content type in the request headers must be "application/x-www-form-urlencoded" otherwise parse_body/1 will return {error, content_type}.)

Return not_handled from request/3 to let httpd continue processing the request with downstream modules.

modlib applications can be started using modlib:start/1 or (more typically) by starting the modlib OTP application with the appropriate config.

Here's a sample modlib config for a non-SSL app:

{modlib,
 [{servers,
   [{8080,
     [{server_root, "/some/dir"},
      {document_root, "/some/dir/htdocs"},
      {modules, [my_modlib_webapp, mod_get]},
      {mime_types, [{"css", "text/css"},
                    {"js", "text/javascript"},
                    {"gif", "image/gif"},
                    {"jpeg", "image/jpeg"},
                    {"png", "image/png"}]}]}]}]}

Here's a sample modlib config for an SSL enabled app (also includes the use of mod_auth for protecting a directory with basic auth):

{modlib,
 [{servers,
   [{443,
     [{server_root, "/some/dir"},
      {document_root, "/some/dir/htdocs"},
      {socket_type, essl},
      {ssl_certificate_file, "/some/dir/server.crt"},
      {ssl_certificate_key_file, "/some/dir/server.key"},
      {ssl_ca_certificate_file, "/some/dir/ca.crt"},
      {modules,[mod_auth, my_modlib_webapp, mod_get2]},
      {directory, {"/protected",
                  [{auth_type, plain},
                   {auth_user_file, "/some/dir/users"},
                   {auth_group_file, "/some/dir/groups"},
                   {auth_name, "My Modlib Webapp"},
                   {require_group, ["users"]}]}},
      {mime_types,[{"css", "text/css"},
                   {"js", "text/javascript"},
                   {"gif", "image/gif"},
                   {"jpeg", "image/jpeg"},
                   {"png", "image/png"}]}]}]}]}

The configuration proplist for the server port is identical to the httpd configration documented in http://www.erlang.org/doc/man/httpd.html.

mod_get2 is a copy of mod_get with support for etags.

For template support, use the excellent Django language implementation at https://github.com/evanmiller/erlydtl.

To Do

  • Need a "catch all" handler that doesn't barf with 500 when httpd runs out of modules (e.g. there's a single webapp that returns not_handled as its catch all).
  • If modlib_webapp is declared as a behavior, it breaks the wrapping that's done by the webapp include.
  • Remove mochiweb util module dependencies, lazy!
  • Support for easy direct-to-socket responses (i.e. already_sent pattern)
  • How is keep-alive / long running connections supported?

inets Wish List

  • Simplify the list of the options that are needed in inets:start/2

modlib's People

Stargazers

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

Watchers

 avatar  avatar  avatar

modlib's Issues

Query string weirdness

When I run the sample code with a query string like "?parm1:1;parm2:2;parm3", I get this kind of response:

[{"parm1:1", []}, "parm2:2", []}, {"parm3", []}]

rather than the expected:

[{"parm1", [1]}, "parm2", [2]}, {"parm3", []}]

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.