Просмотр исходного кода

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

Loïc Hoguin 12 лет назад
Родитель
Сommit
efbd913326

+ 3 - 0
examples/README.md

@@ -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
 

+ 52 - 0
examples/rest_pastebin/README.md

@@ -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.
+

+ 22 - 0
examples/rest_pastebin/priv/index.html

@@ -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>

+ 7 - 0
examples/rest_pastebin/priv/index.txt

@@ -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

+ 4 - 0
examples/rest_pastebin/rebar.config

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

+ 15 - 0
examples/rest_pastebin/src/rest_pastebin.app.src

@@ -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, []}
+]}.

+ 14 - 0
examples/rest_pastebin/src/rest_pastebin.erl

@@ -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).

+ 25 - 0
examples/rest_pastebin/src/rest_pastebin_app.erl

@@ -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.

+ 23 - 0
examples/rest_pastebin/src/rest_pastebin_sup.erl

@@ -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}}.

+ 132 - 0
examples/rest_pastebin/src/toppage_handler.erl

@@ -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>>.

+ 6 - 0
examples/rest_pastebin/start.sh

@@ -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\")."