Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
      Languages 
      Kernels 
      Packages 
      Books 
      Tests 
      OS 
      Forum 
      Математика 
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
 Assembler...3017 
 Advanced Bash Scripting G...2677 
 Ethreal 4...2232 
 Secure Programming for Li...2018 
 CPAN-> FAQ...1810 
 Trees...1786 
 Alg1...1724 
 Ethreal 1...1666 
 Тренажёр...1637 
 Intel 386...1635 
 Go Web ...1629 
 Максвелл 3...1616 
 Ext4 FS...1609 
 William Gropp...1587 
 Rust...1571 
 Benchmark...1406 
 Go...1401 
 C + UNIX...1390 
 System...1277 
 Mod_parrot...1256 
 
  01.01.2025 : 3803065 посещений 

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

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

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

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