如何使用 Erlang 制作 Web 服务器

How to make a web server using Erlang

我是 Erlang 新手

我尝试用 Erlang 制作一个网络服务器。如何用 Erlang 做到这一点? 我正在使用此代码制作本地:

-module(test). 
-export([start/0,service/3]). 

start() ->
   inets:start(httpd, [ 
      {modules, [ 
         mod_auth, 
         mod_esi, 
         mod_actions, 
         mod_cgi, 
         mod_dir,
         mod_get, 
         mod_head, 
         mod_log, 
         mod_disk_log 
      ]}, 

      {port,8082}, 
      {server_name,"helloworld"}, 
      {server_root,"C://xampp//tmp"}, 
      {document_root,"C://xampp//htdocs"}, 
      {erl_script_alias, {"/erl", [test]}}, 
      {error_log, "error.log"}, 
      {security_log, "security.log"}, 
      {transfer_log, "transfer.log"}, 

      {mime_types,[ 
         {"html","text/html"}, {"css","text/css"}, {"js","application/x-javascript"} ]} 
   ]). 

service(SessionID, _Env, _Input) -> mod_esi:deliver(SessionID, [ 
   "Content-Type: text/html\r\n\r\n", 
   "<DOCTYPE html>
   <head>
   <meta charset='utf-8'>
   <meta http-equiv='X-UA-Compatible' content='IE=edge'>
   <meta name='viewport' content='width=device-width, initial-scale=1'>
   <title>HTML1</title>
   <script
     src='https://code.jquery.com/jquery-3.2.1.js'
     integrity='sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE='
     crossorigin='anonymous'></script>
   <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet'/>
   <link href='css/test1.css' rel='stylesheet'/>
   <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
   </head>
      <html>
      <body>Ham oc cho!
         <div class='header'>
            <ul class='first'>
               <li class='col-md-4'><a href='#' >Tai khoan cua toi</a></li>
               <li class='col-md-4'><a href='#' >Trang thai don hang</a></li>
               <li class='col-md-4'><a href='#' >Danh sach ua thich</a></li>
               <li class='col-md-4'><a href='#' >Gio hang</a></li>
               <li class='col-md-4'><a href='#' >Dang nhap</a></li>
               <li class='col-md-4'><a href='#' >Dang ky</a></li>
            </ul>
         </div>
      </body>
      </html>" ]).

但我没有看到任何添加 css-js 文件的方法,也不知道如何为此编写后端。

如果你们有一些例子或文档请分享给我

有一些有用的 Erlang 工具,例如 Cowboy, Mochiweb, Chicagoboss and YAWS 可用于处理 Web 协议。

您可能会发现完成 sws、我的 Erlang simple web server 很有启发性。它展示了如何处理连接、从套接字读取 HTTP 请求以及使用 Erlang 的 built-in 套接字支持和 HTTP 支持发送回复。

Web 服务器通过使用 Erlang 的 built-in 对 HTTP 请求解析的支持来接受传入连接和解析传入请求来工作 — 请参阅第 29 行:

ok = inet:setopts(S, [{packet,http_bin}]),

{packet, http_bin} socket option 告诉 Erlang 尝试将传入的套接字数据解析为 HTTP。在第 36 行的 serve/3 函数中,出于流量控制和背压的目的,我们将套接字保持在 {active, once} 模式,这也意味着 sws 接收来自 Erlang 的传入数据作为消息——参见第 37 行-41:

ok = inet:setopts(S, [{active, once}]),
HttpMsg = receive
              {http, S, Msg} -> Msg;
              _ -> gen_tcp:close(S)
          end,

serve/3 函数是递归的,接收 HTTP 请求数据,直到我们得到一个完整的请求或一个错误。一旦 serve/3 有一个完整的请求,它将把它传递给一个处理函数,您应该在调用 sws:start/1,2 时提供该函数。处理程序应 return HTTP 状态、HTTP 回复 headers 和 HTTP 回复 body 的三元组,其中 headers 或 body 可以是空取决于 return 状态。

例如,这是一个简单的 "Hello, World!" 应用程序 运行 在 Erlang shell:

1> c(sws).
{ok,sws}
2> Sws = spawn(sws, start, [fun(_,_) -> {200, [], <<"Hello, World!">>} end]).
<0.73.0>

这里,作为处理程序传递的 fun 总是 return 的 HTTP 状态 200,没有回复 headers,以及回复的二进制字符串 body。从 Unix shell 通过 curl 访问服务器显示预期的回复:

$ curl http://localhost:8000
Hello, World!

如果我们将 -v 传递给 curl 以显示更多详细信息,我们会看到:

$ curl -v http://localhost:8000
* Rebuilt URL to: http://localhost:8000/
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.51.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200
<
* Curl_http_done: called premature == 0
* Closing connection 0
Hello, World!

首先 curl 尝试通过 IPv6 连接,但由于 sws 不支持它而失败(虽然它可以),因此它通过 IPv4 重试,并成功。然后,Curl 发送 GET/ 资源的请求。当 curl 看到回复时,它会显示 200 状态代码,还要注意它看到回复是 HTTP 1.0,因此正确地假设连接将在 body 发送后关闭,因此在收到回复后它会关闭它的侧面也是。

您提供的处理函数有两个参数:客户端套接字和一个请求 object,它是一个 属性 列表,由 2 元组组成,其中第一个元组元素是一个原子,用于标识它的关联数据。例如,处理程序可以通过使用 lists:keyfind/3:

Request 参数中查找 method 元组来确定调用的 HTTP 方法
{method, Method} = lists:keyfind(method, 1, Request),

对于我们上面的示例,Method 将具有 'GET'(一个原子)的值。可以像这样发现的请求的其他属性是:

  • uri 请求的资源
  • version 客户端 HTTP 版本
  • headers 请求中的 HTTP headers 列表

您提供的处理函数可以简单也可以复杂。请注意,如果您的处理程序失败并导致异常,sws 会捕获它并且 returns HTTP 状态代码 500。

要停止 Web 服务器,回到 Erlang shell 我们向生成的 sws 进程发送一条 stop 消息:

3> Sws ! stop.
stop
=ERROR REPORT==== 19-Jul-2017::11:17:05 ===
Error in process <0.77.0> with exit value:
{{badmatch,{error,closed}},[{sws,accept,2,[{file,"sws.erl"},{line,28}]}]}

此处显示的错误(可以忽略)仅仅是因为 sws 始终假定 gen_tcp:accept/1 成功 — 参见第 28 行:

{ok, S} = gen_tcp:accept(LS),

将其改为 case 表达式并处理错误 return 会很容易。

请注意,sws 用于演示和学习,因此它的效率不是特别高,因为它仅支持 HTTP 1.0 并且每个连接仅处理一个请求。

考虑使用 http://phoenixframework.org/ 它使用在 Erlang VM 上运行的 elixir。