0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 20:30:23 +00:00

Merge branch 'add_rest_example' of git://github.com/acammack/cowboy

This commit is contained in:
Loïc Hoguin 2013-02-07 18:34:49 +01:00
commit efbd913326
11 changed files with 303 additions and 0 deletions

View file

@ -28,6 +28,9 @@ Cowboy Examples
* [rest_hello_world](./examples/rest_hello_world):
return the data type that matches the request type (ex: html, text, json)
* [rest_pastebin](./examples/rest_pastebin):
create text objects and return the data type that matches the request type (html, text)
* [static_world](./examples/static_world):
static file handler

View file

@ -0,0 +1,52 @@
Cowboy Rest Hello World
=======================
To compile this example you need rebar in your PATH.
Type the following command:
```
$ rebar get-deps compile
```
You can then start the Erlang node with the following command:
```
./start.sh
```
Then run any given command or point your browser to the indicated URL.
Examples
--------
To upload something to the paste application, you can use curl like:
```
<command> | curl -i --data-urlencode paste@- localhost:8080
```
or to upload my_file:
```
curl -i --data-urlencode paste@my_file localhost:8080
```
The URL of your data will be in the location header. Alternately, you can visit
http://localhost:8080 with your favorite web browser and submit your paste via
the form.
Code that has been pasted can be highlighted with ?lang=<language> option if
you have [highlight](http://www.andre-simon.de/doku/highlight/en/highlight.html)
installed (although pygments or any other should work just fine). For example:
```
curl -i --data-urlencode paste@priv/index.html localhost:8080
curl <url from location header>
```
Will show the text of the html file. If your terminal supports color
sequences and highlight is installed:
```
curl <url from location header>?lang=html
```
Will show a syntax highlighted version of the source file. If you open the
same URL in your web browser and your web browser tells cowboy that it prefers
html files, you will see the file highlighted with html/css markup. Firefox is
known to work.

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>Simple Pastebin</title>
</head>
<body>
<h1>Simple Pastebin</h1>
<p>
You can paste your text into the text field to submit, or you can
capture the output of a command with:
</p>
<code>
<i>command</i> | curl -i --data-urlencode paste@- localhost:8080
</code>
<form action="/" method="post">
<textarea cols="80" rows="15" name="paste"></textarea>
<div>
<button type="submit">Upload your code</button>
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,7 @@
Simple Pastebin
---------------
You can paste your text into the text field to submit, or you can capture the
output of a command with:
<command> | curl -i --data-urlencode paste@- localhost:8080

View file

@ -0,0 +1,4 @@
{deps, [
{cowboy, ".*",
{git, "git://github.com/extend/cowboy.git", "master"}}
]}.

View file

@ -0,0 +1,15 @@
%% Feel free to use, reuse and abuse the code in this file.
{application, rest_pastebin, [
{description, "Cowboy REST Pastebin example inspired by sprunge."},
{vsn, "1"},
{modules, []},
{registered, []},
{applications, [
kernel,
stdlib,
cowboy
]},
{mod, {rest_pastebin_app, []}},
{env, []}
]}.

View file

@ -0,0 +1,14 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(rest_pastebin).
%% API.
-export([start/0]).
%% API.
start() ->
ok = application:start(crypto),
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(rest_pastebin).

View file

@ -0,0 +1,25 @@
%% Feel free to use, reuse and abuse the code in this file.
%% @private
-module(rest_pastebin_app).
-behaviour(application).
%% API.
-export([start/2]).
-export([stop/1]).
%% API.
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/[:paste_id]", toppage_handler, []}
]}
]),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{env, [{dispatch, Dispatch}]}
]),
rest_pastebin_sup:start_link().
stop(_State) ->
ok.

View file

@ -0,0 +1,23 @@
%% Feel free to use, reuse and abuse the code in this file.
%% @private
-module(rest_pastebin_sup).
-behaviour(supervisor).
%% API.
-export([start_link/0]).
%% supervisor.
-export([init/1]).
%% API.
-spec start_link() -> {ok, pid()}.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% supervisor.
init([]) ->
Procs = [],
{ok, {{one_for_one, 10, 10}, Procs}}.

View file

@ -0,0 +1,132 @@
%% Feel free to use, reuse and abuse the code in this file.
%% @doc Pastebin handler.
-module(toppage_handler).
%% REST Callbacks
-export([init/3]).
-export([allowed_methods/2]).
-export([content_types_provided/2]).
-export([content_types_accepted/2]).
-export([resource_exists/2]).
-export([post_is_create/2]).
-export([create_path/2]).
%% Callback Callbacks
-export([create_paste/2]).
-export([paste_html/2]).
-export([paste_text/2]).
init(_Transport, _Req, []) ->
% For the random number generator:
{X, Y, Z} = now(),
random:seed(X, Y, Z),
{upgrade, protocol, cowboy_rest}.
allowed_methods(Req, State) ->
{[<<"GET">>, <<"POST">>], Req, State}.
content_types_provided(Req, State) ->
{[
{{<<"text">>, <<"plain">>, []}, paste_text},
{{<<"text">>, <<"html">>, []}, paste_html}
], Req, State}.
content_types_accepted(Req, State) ->
{[{{<<"application">>, <<"x-www-form-urlencoded">>, []}, create_paste}],
Req, State}.
resource_exists(Req, _State) ->
case cowboy_req:binding(paste_id, Req) of
{undefined, Req2} ->
{true, Req2, index};
{PasteID, Req2} ->
case valid_path(PasteID) and file_exists(PasteID) of
true -> {true, Req2, PasteID};
false -> {false, Req2, PasteID}
end
end.
post_is_create(Req, State) ->
{true, Req, State}.
create_path(Req, State) ->
{<<$/, (new_paste_id())/binary>>, Req, State}.
create_paste(Req, State) ->
{<<$/, PasteID/binary>>, Req2} = cowboy_req:meta(put_path, Req),
{ok, [{<<"paste">>, Paste}], Req3} = cowboy_req:body_qs(Req2),
ok = file:write_file(full_path(PasteID), Paste),
{true, Req3, State}.
paste_html(Req, index) ->
{read_file("index.html"), Req, index};
paste_html(Req, Paste) ->
{Style, Req2} = cowboy_req:qs_val(<<"lang">>, Req, plain),
{format_html(Paste, Style), Req2, Paste}.
paste_text(Req, index) ->
{read_file("index.txt"), Req, index};
paste_text(Req, Paste) ->
{Style, Req2} = cowboy_req:qs_val(<<"lang">>, Req, plain),
{format_text(Paste, Style), Req2, Paste}.
% Private
read_file(Name) ->
{ok, Binary} = file:read_file(full_path(Name)),
Binary.
full_path(Name) ->
{ok, Cwd} = file:get_cwd(),
filename:join([Cwd, "priv", Name]).
file_exists(Name) ->
case file:read_file_info(full_path(Name)) of
{ok, _Info} -> true;
{error, _Reason} -> false
end.
valid_path(<<>>) -> true;
valid_path(<<$., _T/binary>>) -> false;
valid_path(<<_Char, T/binary>>) -> valid_path(T).
new_paste_id() ->
Initial = random:uniform(62) - 1,
new_paste_id(<<Initial>>, 7).
new_paste_id(Bin, 0) ->
Chars = <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890">>,
<< <<(binary_part(Chars, B, 1))/binary>> || <<B>> <= Bin >>;
new_paste_id(Bin, Rem) ->
Next = random:uniform(62) - 1,
new_paste_id(<<Bin/binary, Next>>, Rem - 1).
format_html(Paste, plain) ->
Text = escape_html_chars(read_file(Paste)),
<<"<!DOCTYPE html><html>",
"<head><title>paste</title></head>",
"<body><pre><code>", Text/binary, "</code></pre></body></html>\n">>;
format_html(Paste, Lang) ->
highlight(full_path(Paste), Lang, "html").
format_text(Paste, plain) ->
read_file(Paste);
format_text(Paste, Lang) ->
highlight(full_path(Paste), Lang, "ansi").
highlight(Path, Lang, Type) ->
Path1 = binary_to_list(Path),
Lang1 = binary_to_list(Lang),
os:cmd(["highlight --syntax=", Lang1,
" --doc-title=paste ",
" --out-format=", Type,
" --include-style ", Path1]).
% Escape some HTML characters that might make a fuss
escape_html_chars(Bin) ->
<< <<(escape_html_char(B))/binary>> || <<B>> <= Bin >>.
escape_html_char($<) -> <<"&lt;">>;
escape_html_char($>) -> <<"&gt;">>;
escape_html_char($&) -> <<"&amp;">>;
escape_html_char(C) -> <<C>>.

View file

@ -0,0 +1,6 @@
#!/bin/sh
erl -pa ebin deps/*/ebin -s rest_pastebin \
-eval "io:format(\"Upload: echo foo | curl -i --data-urlencode paste@- localhost:8080~n\")." \
-eval "io:format(\"Get: curl <value of the location header>~n\")." \
-eval "io:format(\"Get with highlighting: curl <location>?lang=<language>~n\")." \
-eval "io:format(\"To get html, point your browser to http://localhost:8080~n\")."