Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Secure Programming for Li...6507 
 Linux Kernel 2.6...5280 
 Trees...1119 
 Максвелл 3...1051 
 William Gropp...990 
 Go Web ...963 
 Ethreal 3...930 
 Ethreal 4...918 
 Gary V.Vaughan-> Libtool...914 
 Ext4 FS...905 
 Clickhouse...902 
 Rodriguez 6...900 
 Ethreal 1...897 
 Steve Pate 1...886 
 C++ Patterns 3...864 
 Assembler...854 
 Ulrich Drepper...844 
 DevFS...788 
 MySQL & PosgreSQL...774 
 Стивенс 9...758 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Веб-программирование в Erlang

Нам понадобится веб-сервер YAWS (Yet Another Web Server). Обладатели Убунты могут пакетно установить yaws:
 apt-get build-dep erlang yaws
В большинстве линуксовых дистрибутивов yaws прийдется ставить из исходников, которые лежат тут.

YAWS имеет следуюшие достоинства:

  скорость
  масштабируемость
  генерация статического и динамического контента
  виртуальный хостинг
  отладчик
  кеширование
  ssl
  аутентификация
  сессии
  модульная архитектура
  веб-сокеты
 
Когда вы установите yaws из исходников с помощью команды make install, конфиг будет установлен по умолчанию в /usr/local/etc/yaws/yaws.conf. Лог файлы лежат в каталоге logdir = /usr/local/var/log/yaws. Дополнительные бинарные сборки можно складывать в ebin_dir = /usr/local/lib/yaws/examples/ebin. Для файлов с расширением .hrl определен каталог include_dir = /usr/local/lib/yaws/examples/include. Конфигурация самого сервера
 <server localhost>
 port = 8000
 listen = 127.0.0.1
 docroot = /home/yaws/www
 </server>
Запуск сервера в интерактивном режиме:
 yaws -i
Динамический контент в yaws реализован с помощью расширения .yaws. Страница с таким расширением может содержать как статический html-код, так и динамический эрланговский, обрамленный с помощью тегов <erl> ... </erl>, внутри которого обязательно должна быть эрланговская функция out. Есть два варианта вызова функции out. Первый - она возвращает кортеж .{html, String}, где String - это html-код:
 <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>
Такая страница выводит что-то типа:
  Example 1
 You say that you are running "Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0"  
Второй вариант вызова out - функция out возвращает кортеж{ehtml, EHTML}:
 <erl>
 {ehtml, {table, [{bgcolor, grey}],
     [
       {tr, [],
       [
 	{td, [], "1"},
 	{td, [], "2"},
 	{td, [], "3"}
       ]
     },
     {tr, [],
 [{td, [{colspan, "3"}], "444"}]}]}}.
 <erl>
Такой код вернет следующий html:
 <table bgcolor="grey">
   <tr>
     <td> 1 </td
     <td> 2 </td>
     <td> 3 </td>
   </tr>
   <tr>
     <td colspan="3"> 444 </td>
   </tr>
 </table>
Во втором случае возможен дополнительный вызов других эрланговских функций.

Параметр #arg содержит в себе многие подробности о клиентском запросе - заголовки, данные, пути и т.д. - вот полный список:

 -record(arg, {
 	clisock,	%	the socket leading to the peer client
 	client_ip_port,	%	{ClientIp, ClientPort} tuple
 	headers,	%	headers
 	req,	        %	request
 	orig_req,	%	original request
 	clidata,	%	The client data (as a binary in POST requests)
 	server_path,	%	The normalized server path
                        % 	(pre-querystring part of URI)
 	querydata,	%	For URIs of the form ...?querydata
 	                %	equiv of cgi QUERY_STRING
 	appmoddata,	%	(deprecated - use pathinfo instead) the remainder
 	                %	of the path leading up to the query
 	docroot,	%	Physical base location of data for this request
 	docroot_mount,	%	virtual directory e.g /myapp/ that the docroot
 	                %	refers to.
         fullpath,	%	full deep 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 worker process
 	opaque, 	% useful to pass static data
 	appmod_prepath, % (deprecated - use prepath instead) path in front
 	                % of: 
 	prepath,	% Path prior to ’dynamic’ segment of URI.
 	                % or .yaws,.php,.cgi,.fcgi etc script file.
         pathinfo	% Set to ’/d/e’ when calling c.yaws for the request
 	                % http://some.host/a/b/c.yaws/d/e
 	                % equiv of cgi PATH_INFO
 	}).
 
 
 -record(http_request,{method,
 	path,
 	version}).
 	
 -record(headers, {
       connection,
       accept,
       host,
       if_modified_since,
       if_match,
       if_none_match,
       if_range,
       if_unmodified_since,
       range,
       referer,
       user_agent,
       accept_ranges,
       cookie = [],
       keep_alive,
       location,
       content_length,
       content_type,
       content_encoding,
       authorization,
       transfer_encoding,
       x_forwarded_for,
       other = []
       % misc other headers
       }). 
Вывести это на экран с помощью ehtml можно так:
 <html>
 <h2> The Arg </h2>
 <p>This page displays the Arg #argument structure
 supplied to the out/1 function.
 
 <erl>
 
 out(A) ->
 
     Peer = A#arg.client_ip_port,
     Req = A#arg.req,
     H = yaws_api:reformat_header(A#arg.headers),
     {ehtml,
      [{h5,[], "The headers passed to us were:"},
       {hr,[],[]},
       {ol, [],lists:map(fun(S) -> {li,[], {p,[],S}} end,H)},
 
       {h5, [], "The request"},
       {ul,[],
        [{li,[], f("method: ~s", [Req#http_request.method])},
         {li,[], f("path: ~p", [Req#http_request.path])},
         {li,[], f("version: ~p", [Req#http_request.version])}]},
 
       {hr,[],[]},
       {h5, [], "Other items"},
       {ul,[],
        [{li, [], f("Peer: ~p", [Peer])},
         {li,[], f("docroot: ~s", [A#arg.docroot])},
         {li,[], f("fullpath: ~s", [A#arg.fullpath])}]},
       {hr,[],[]},
       {h5, [], "Parsed query data"},
       {pre,[], f("~p", [yaws_api:parse_query(A)])},
       {hr,[],[]},
       {h5,[], "Parsed POST data "},
       {pre,[],  f("~p", [yaws_api:parse_post(A)])}]}.
 
 </erl>
Здесь используются несколько функций из модуля yaws_api. Распарсить GET-запрос можно с помощью функции yaws_api:parse_query. Распарсить POST-запрос можно так:
 out(A) ->
   L = yaws_api:parse_post(A),
   {html, f("~p", [L])}
Сделать upload файла на сервер можно так:
 out(A) ->
   Form =
     {form, [{enctype, "multipart/form-data"},
 	{method, post},
 	{action, "file_upload_form.yaws"}],
 	[{input, [{type, submit}, {value, "Upload"}]},
 	{input, [{type,file}, {width, "50"}, {name, foo}]}]},
   {ehtml, {html,[], [{h2,[], "A simple file upload page"},
       Form]}}.
 
 
yaws поддерживает куки, которые лежат в основе сессий. Для работы с сессиями есть несколько функций:
  yaws_api:new_cookie_session(Opaque) - создает сессию
  yaws_api:cookieval_to_opaque(Cookie)
  yaws_api:replace_cookie_session(Cookie, NewOpaque) 
  yaws_api:delete_cookie_session(Cookie) - удаляет сессию
 
Проверку можно сделать так:
 -record(session, {user,
     passwd,
     udata = []}).
     
 get_cookie_val(CookieName, Arg) ->
     H = Arg#arg.headers,
     yaws_api:find_cookie_val(CookieName, H#headers.cookie).
     
 check_cookie(A, CookieName) ->
     case get_cookie_val(CookieName, A) of
       [] ->
 	  {error, "not logged in"};
       Cookie ->
 	  yaws_api:cookieval_to_opaque(Cookie)
     end.
Редирект на другую страицу делается так:
 <erl>
 out(Arg) ->
 URL = "http://www.erlang.org",
 {redirect, URL}.
 </erl>
Аутентификацию можно организовать следующим образом: имеется стартовая страница с формой, на которой пользователь набирает логин с паролем, после чего перенаправляется на другую страницу, которая обрабатывает запрос. Стартовая страница login.yaws:
 <erl>
 out(A) ->
   {ehtml,
     {html,[],
       [{h2, [], "Login page"},
       {hr},
       {form, [{action,"/login_post.yaws"},{method,post}],
       [{p,[], "Username"}, {input, [{type,text},{name,uname}]},
       {p,[],"Password"}, {input, [{type,password},{name,passwd}]},
       {input, [{type,submit},{value,"Login"}]},
       {input, [{type,hidden},{name,url}, {value, A#arg.state}]}]}]}}.
 </erl>
Вторая страница login_post.yaws, обрабатывающая запрос:
 <erl>
 
 -include("myapp.hrl").
 %% myapp.hrl нужно прописать в yaws.conf
 
 kv(K,L) -> 
   {value, {K, V}} = lists:keysearch(K,1,L), V.
 
 out(A) ->
   L = yaws_api:parse_post(A),
   User = kv(user, L),
   Pwd = kv(passwd, L),
   case myapp:auth(User, Pwd) of
       ok ->
 	  S = #session{user = User,
 		       passwd = Pwd,
 		       udata = []},
 	  %% создаем новую сессию
 	  Cookie = yaws_api:new_cookie_session(S),
 	  [{redirect_local, kv(url, L)},
 	  yaws_api:set_cookie("myapp_sid",Cookie,[])]
       Err ->
 	  {ehtml,
 	  {html, [],
 	  {p, [], f("Bad login: ~p",[Err])}}}
     end.
 </erl>
Appmods позволяет в yaws контролировать адресную строку и делать редирект. Работает это следующим образом: допустим, на сервере загружается адрес:
   http://localhost:8000/my-path/my_url.html
Если мы в конфиге пропишем секцию appmods:
 <server localhost>
 	...
         appmods = <my_path, my_appmod>
 </server>
то вместо загрузки страницы my_url.html будет вызвана функция my_appmod:out. Нужно положить рядом со страницей my_url.html эрланговский модуль my_appmod.erl, в котором нужно реализовать функцияю out:
 -module(my_appmod).
 -compile(export_all).
 
 out(A) ->
     {ehtml,
      [{p,[],
      ...
Если мы хотим обрабатывать все урлы:
 <server localhost>
 	...
         appmods = </, my_appmod >
 </server>
Если мы хотим для некоторых путей оставить обычную обработку:
 <server localhost>
 	...
         appmods = </, my_appmod exclude_paths icons js top/static>
 </server>

Пример

Резюмируя все вышесказанное, рассмотрим простое веб-приложение, которое можно найти в исходниках yaws - оно называется shoppingcart. Стартовая страница index.yaws веб-приложения содержит форму авторизации, после прохождения которой вы попадаете на страницу, предлагающую вам купить товар. В приложении всего 4 страницы:
 index.yaws
 loginpost.yaws
 logout.yaws
 shopcart_form.yaws
На всех страницах контент генерится динамически, и все страницы выглядят похожим образом - в них совершенно отсутствует статический html-код:
 <erl>
 
 out(A) ->
     case shopcart:top(A) of
         ok ->
             shopcart:index(A);
         X ->
             X
     end.
 
 </erl>
И рядом лежит эрланговский модуль shopcart.erl, в котором реализована вся эта динамика. На каждой странице вначале всегда вызывается функция top(), которая проверяет куки, и если не находит их, то перенаправляет пользователя на страницу авторизации:
 top(A) ->
     case check_cookie(A) of
         {ok, _Session, _Cookie} ->
             ok;
         {error, _Reason} ->
             login(A)
     end.
Функция Login, в которой используется Ehtml:
 login(A) ->
     CSS = css_head("Shopcart"),
     Head = head_status("Not lgged in"),
     Top = toprow(),
     Login =
         {ehtml,
          [{h2, [], "Shopcart login"},
           {form, [{method, get},
                   {action, "loginpost.yaws"}],
            [
             {p, [], "Username"},
             {input, [{name, user},
                      {type, text},
                      {value, "Joe Junk shopper"},
                      {size, "48"}]},
 
 
             {p, [], "Password"},
             {input, [{name, password},
                      {type, text},
                      {value, "xyz123"},
                      {size, "48"}]},
 
             {input, [{type, submit},
                      {value, "Login"}]},
 
             {input, [{name, url},
                      {type, hidden},
                      {value, xpath((A#arg.req)#http_request.path, A)}]}
            ]
            }
          ]},
     [CSS, Head, Top, Login, bot(), break].

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье