Permalink
Browse files

""

git-svn-id: https://erlyaws.svn.sourceforge.net/svnroot/erlyaws/trunk/yaws@252 9fbdc01b-0d2c-0410-bfb7-fb27d70d8b52
  • Loading branch information...
klacke committed Nov 7, 2002
1 parent c3386b0 commit 814eb0435d92164ce1aba77bd5512aac8ba61a38
Showing with 291 additions and 17 deletions.
  1. +111 −0 doc/yaws.tex
  2. +2 −2 include/yaws_api.hrl
  3. +5 −0 man/yaws_api.5
  4. +106 −1 src/yaws_api.erl
  5. +22 −14 src/yaws_server.erl
  6. +45 −0 www/arg.yaws
View
@@ -73,6 +73,12 @@ \chapter{Introduction}
\item Embedded mode
\end{enumerate}
+\section{Prerequisites}
+This document requires that the reader:
+\begin{itemize}
+\item Is well acquainted with the Erlang programming language
+\item Understands basic Web technologies.
+\end{itemize}
\section{A tiny example}
@@ -379,9 +385,114 @@ \chapter{Dynamic content}
Most large sites on the Web today make heavy use of dynamic pages.
+
\section{Introduction}
+
+When the client GETs a a page that has a .yaws suffix. The yaws server
+will read that page from the hard disk and divide it in parts
+that consist of HTML code and Erlang code. Each chunk of erlang code
+will be compiled into a module. The chunk of Erlang code must contain
+a function \verb+out/1+. If it doesn't the yaws server will insert a
+proper error message into the generated HTML output.
+
+When the Yaws server ships a .yaws page it will ship chunk by chunk
+through the .yaws file. If it is HTML code, the server will ship that
+as is whereas if it is Erlang code, the yaws server will invoke the
+\verb+out/1+ function in that code and insert the output into the stream
+of HTML that is being shipped to the client.
+
+Yaws will (of cource) cache the result of the compilation
+and the next time a client requests the same .yaws page Yaws will
+be able to invoke the already compiled modules directly.
+
+
\section{EHTML}
+There are two ways to make the \verb+out/1+ function generate HTML
+output. The first and most easy to understand is by returning a tuple
+\verb+{html, String}+ where \verb+String+ then is regular HTML data
+(possibly as a deep list of strings and/or binaries) which will simply
+be inserted into the output stream.
+An example:
+
+\begin{verbatim}
+<html>
+<h1> Example 1 </h1>
+
+<erl>
+out(A) ->
+ Headers = A#arg.headers,
+ {html, io_lib:format("You say that you're running ~p",
+ [Headers#headers.user_agent])}.
+
+</erl>
+
+</html>
+
+\end{verbatim}
+
+
+The second way to generate output is by returning a tuple
+\verb+{ehtml, EHTML}+. The term \verb+EHTML+ must adhere to the
+following structure:
+
+$EHTML = [EHTML] | {HTMLTAG, Attrs, Body} | {HTMLTAG, Attrs} | {HTMLTAG} |
+ binary() | character()$
+
+$HTMLTAG = atom()$
+
+$Attrs = [{HtmlAttribute, Value}]$
+
+$HtmlAttribute = atom()$
+
+$Value = string() | atom()$
+
+$Body = EHTML$
+
+We give an example to show what we mean:
+The tuple
+\begin{verbatim}
+{ehtml, {table, [{bgcolor, grey}],
+ [
+ {tr, [],
+ [
+ {td, [], "1"},
+ {td, [], "2"},
+ {td, [], "3"}
+ ],
+ {tr, [],
+ [{td, [{colspan, "3"}], "444"}]}}]}}.
+\end{verbatim}
+
+Would be expanded into the following HTML code
+\begin{verbatim}
+<table bgcolor="grey">
+ <tr>
+ <td> 1 </td
+ <td> 2 </td>
+ <td> 3 </td>
+ </tr>
+ <tr>
+ <td colspan="3"> 444 </td>
+ </tr>
+</table>
+
+\end{verbatim}
+
+At a first glance it may appears if the HTML code is more
+beautiful than the Erlang tuple. That may very well be the
+case from a purely aesthetic point of view. However the
+Erlang code has the advantage of being perfectly indented by editors
+that have syntax support for Erlang (read Emacs). Furthermore, the erlang
+code is easier to manipulate from an Erlang program.
+
+As an example of some more interesting ehtml we could have
+an \verb+out/1+ function that prints some of the HTTP headers
+We have:
+
+
+
+
\chapter{Mode of operation}
View
@@ -19,8 +19,8 @@
fullpath, %% full path to yaws file
cont, %% Continuation for chunked multipart uploads
state, %% State for use by users of the out/1 callback
- pid, %% pid of the yaws process
- opaque %% useful for embedded mode to pass static data
+ pid, %% pid of the yaws worker process
+ opaque %% useful to pass static data
}).
View
@@ -300,6 +300,11 @@ character.
Url-encodes a string. All URLs in HTML documents must be URL encoded.
+.TP
+\freformat_header(H)\fR
+Returns a list of reformated header values from a #header{}
+record. The return list is suitable for retransmit.
+
.SH RETURN VALUES from out/1
.PP
View
@@ -33,7 +33,7 @@
cookieval_to_opaque/1,
print_cookie_sessions/0,
replace_cookie_session/2, delete_cookie_session/1]).
--export([setconf/2, set_status_code/1]).
+-export([setconf/2, set_status_code/1, reformat_header/1]).
%% these are a bunch of function that are useful inside
%% yaws scripts
@@ -777,3 +777,108 @@ setconf(GC, Groups) ->
set_status_code(Code) ->
put(status_code, Code).
+
+
+%% returns [ Header1, Header2 .....]
+reformat_header(H) ->
+ lists:zf(fun({Hname, Str}) ->
+ I = lists:flatten(io_lib:format("~s: ~s",[Hname, Str])),
+ {true, I};
+ (undefined) ->
+ false
+ end,
+ [
+ if H#headers.connection == undefined ->
+ undefined;
+ true ->
+ {"Connection", H#headers.connection}
+ end,
+
+ if H#headers.accept == undefined ->
+ undefined;
+ true ->
+ {"Accept", H#headers.accept}
+ end,
+ if H#headers.host == undefined ->
+ undefined;
+ true ->
+ {"Host", H#headers.host}
+ end,
+ if H#headers.if_modified_since == undefined ->
+ undefined;
+ true ->
+ {"If-Modified-Since", H#headers.if_modified_since}
+ end,
+ if H#headers.if_match == undefined ->
+ undefined;
+ true ->
+ {"If-Match", H#headers.if_match}
+ end,
+ if H#headers.if_none_match == undefined ->
+ undefined;
+ true ->
+ {"If-None-Match", H#headers.if_none_match}
+ end,
+
+
+ if H#headers.if_range == undefined ->
+ undefined;
+ true ->
+ {"If-Range", H#headers.if_range}
+ end,
+ if H#headers.if_unmodified_since == undefined ->
+ undefined;
+ true ->
+ {"If-Unmodified-Since", H#headers.if_unmodified_since}
+ end,
+ if H#headers.range == undefined ->
+ undefined;
+ true ->
+ {"Range", H#headers.range}
+ end,
+ if H#headers.referer == undefined ->
+ undefined;
+ true ->
+ {"Referer", H#headers.referer}
+ end,
+ if H#headers.user_agent == undefined ->
+ undefined;
+ true ->
+ {"User-Agent", H#headers.user_agent}
+ end,
+ if H#headers.accept_ranges == undefined ->
+ undefined;
+ true ->
+ {"Accept-Ranges", H#headers.accept_ranges}
+ end,
+ if H#headers.cookie == undefined ->
+ undefined;
+ true ->
+ {"Cookie", H#headers.cookie}
+ end,
+ if H#headers.keep_alive == undefined ->
+ undefined;
+ true ->
+ {"Keep-Alive", H#headers.keep_alive}
+ end,
+ if H#headers.content_length == undefined ->
+ undefined;
+ true ->
+ {"Content-Length", H#headers.content_length}
+ end,
+ if H#headers.content_type == undefined ->
+ undefined;
+ true ->
+ {"Content-Type", H#headers.content_type}
+ end,
+
+ if H#headers.authorization == undefined ->
+ undefined;
+ true ->
+ {"Authorization", H#headers.authorization}
+ end]
+ ) ++
+ lists:map(
+ fun({http_header,_,K,_,V}) ->
+ lists:flatten(io_lib:format("~s: ~s",[K,V]))
+ end, H#headers.other).
View
@@ -1381,8 +1381,13 @@ handle_out_reply(L, DCC, LineNo, YawsFile, SC, A) when list (L) ->
handle_out_reply({html, Html}, DCC, _LineNo, _YawsFile, _SC, _A) ->
accumulate_chunk(DCC, Html);
-handle_out_reply({ehtml, E}, DCC, _LineNo, _YawsFile, _SC, _A) ->
- accumulate_chunk(DCC, safe_ehtml_expand(E));
+handle_out_reply({ehtml, E}, DCC, _LineNo, _YawsFile, SC, A) ->
+ case safe_ehtml_expand(E) of
+ {ok, Val} ->
+ accumulate_chunk(DCC, Val);
+ {error, ErrStr} ->
+ handle_crash(A,DCC,ErrStr,SC)
+ end;
handle_out_reply({content, MimeType, Cont}, DCC, _LineNo,_YawsFile, _SC, _A) ->
put(content_type, MimeType),
@@ -1425,7 +1430,8 @@ handle_out_reply({redirect, URL}, _DCC, _LineNo, _YawsFile, _SC, A) ->
handle_out_reply(ok, _DCC, _LineNo, _YawsFile, _SC, _A) ->
ok;
-handle_out_reply({'EXIT', Err}, DCC, LineNo, YawsFile, SC, A) ->
+handle_out_reply({'EXIT', Err}, DCC, LineNo, YawsFile, SC, ArgL) ->
+ A = hd(ArgL),
L = ?F("~n~nERROR erlang code crashed:~n "
"File: ~s:~w~n"
"Reason: ~p~nReq: ~p~n",
@@ -1435,7 +1441,8 @@ handle_out_reply({'EXIT', Err}, DCC, LineNo, YawsFile, SC, A) ->
handle_out_reply({get_more, Cont, State}, _DCC, _LineNo, _YawsFile, _SC, _A) ->
{get_more, Cont, State};
-handle_out_reply(Reply, DCC, LineNo, YawsFile, SC, A) ->
+handle_out_reply(Reply, DCC, LineNo, YawsFile, SC, ArgL) ->
+ A = hd(ArgL),
L = ?F("yaws code at ~s:~p crashed or "
"ret bad val:~p ~nReq: ~p",
[YawsFile, LineNo, Reply, A#arg.req]),
@@ -1461,29 +1468,30 @@ handle_out_reply_l([], _DCC, _LineNo, _YawsFile, _SC, _A, Res) ->
%% actual crash or a custimized error message
handle_crash(A, DCC, L, SC) ->
+ ?Debug("handle_crash(~p)~n", [L]),
yaws:elog("~s", [L]),
case catch apply(SC#sconf.errormod_crash, crashmsg, [A, SC, L]) of
{html, Str} ->
accumulate_chunk(DCC, Str),
break;
{ehtml, Term} ->
- case catch safe_ehtml_expand(Term) of
- {'EXIT', Reason} ->
- yaws:elog("~p", [Reason]),
+ case safe_ehtml_expand(Term) of
+ {error, Reason} ->
+ yaws:elog("~s", [Reason]),
%% Aghhh, yet another user crash :-(
- T2 = [{h2, "Internal error"}, {hr},
- {p, "Customized crash display code crashed !!!"}],
- accumulate_chunk(DCC, safe_ehtml_expand(T2)),
+ T2 = [{h2, [], "Internal error"}, {hr},
+ {p, [], "Customized crash display code crashed !!!"}],
+ accumulate_chunk(DCC, ehtml_expand(T2)),
break;
- Out ->
+ {ok, Out} ->
accumulate_chunk(DCC, Out),
break
end;
Other ->
yaws:elog("Bad return value from errmod_crash ~n~p~n",[Other]),
T2 = [{h2, [], "Internal error"}, {hr},
{p, [], "Customized crash display code returned bad val"}],
- accumulate_chunk(DCC, safe_ehtml_expand(T2)),
+ accumulate_chunk(DCC, ehtml_expand(T2)),
break
end.
@@ -2311,9 +2319,9 @@ d(_) -> 63.
safe_ehtml_expand(X) ->
case (catch ehtml_expand(X)) of
{'EXIT', R} ->
- io_lib:format("<pre> ~n~p~n </pre>~n", [R]);
+ {error, io_lib:format("<pre> ~n~p~n </pre>~n", [R])};
Val ->
- Val
+ {ok, Val}
end.
Oops, something went wrong.

0 comments on commit 814eb04

Please sign in to comment.