最近想用cowboy做一个echo服务器,经过几天的辛苦探索终于实现了,下面记录一下过程,源码等下会上传到Github。
系统:Ubuntu16.04
Erlang版本:20
cowboy版本:2.0.0
1、使用erlang.mk构建OTP release
简单列出一下命令,详细过程可以参考下面的链接。
$ mkdir hello_joe
$ cd hello_joe
$ wget https://erlang.mk/erlang.mk
make -f erlang.mk bootstrap bootstrap-rel
make
如果编译提示在目录./.erlang.mk/下找不到relx文件,可以再make一下,第一次make会下载relx。
到这里已经构建好了一个基础的OTP release,其他工作可以在此基础上进行。
参考:https://erlang.mk/guide/getting_started.html
https://ninenines.eu/docs/en/cowboy/2.0/guide/getting_started/
2、给构建好的OTP release配置cowboy依赖
修改Makefile成下面这样:
PROJECT = echo_server
PROJECT_DESCRIPTION = New project
PROJECT_VERSION = 0.1.0
DEPS = cowboy
dep_cowboy_commit = master
DEP_PLUGINS = cowboy
include erlang.mk
键入make run,会自动下载cowboy及相关依赖到deps目录。
3、监听连接
修改src/echo_server_app.erl:
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
%% {URIHost, list({URIPath, Handler, Opts})}
{'_', [
{"/test2.html", cowboy_static, {priv_file, echo_server, "test2.html"}},
{"/cgi", cgi_web_server, []}
]}
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch}}
),
echo_server_sup:start_link().
这里最重要的是配置路由表,路由表决定URI路径会映射到哪个处理模块。
cowboy_static是提供一个静态内容,这里将”/test2.html”映射到echo_server的priv目录下的test2.html。
cgi_web_server是我自己定义请求处理模块。
参考http://blog.imaou.com/erlang/2015/02/24/build_web_service_with_cowboy_and_relx_1.html
4、处理请求
用模板生成处理模块:
$ make new t=cowboy.http n=cgi_web_server
然后修改刚生成的处理模块:
cgi_web_server.erl:
-module(cgi_web_server).
-behavior(cowboy_handler).
-export([init/2]).
init(Req, State) ->
Path = cowboy_req:path(Req),
handle1(Path, Req, State).
handle1(<<"/cgi">>, Req, State) ->
ModFunBin = cowboy_req:parse_qs(Req),
{ok, Bin, Req2} = cowboy_req:read_body(Req),
Val = mochijson2:decode(Bin),
Response = call(ModFunBin, Val),
Json = mochijson2:encode(Response),
Req3 = cowboy_req:reply(200,
#{<<"content-type">> => <<"text/plain">>},
Json, Req2),
{ok, Req3, State};
handle1(Path, Req, State) ->
Response = read_file(Path),
Req1 = cowboy_req:reply(200,
#{<<"content-type">> => <<"text/plain">>},
Response, Req),
{ok, Req1, State}.
call([{<<"mod">>,MB},{<<"func">>,FB}], X) ->
Mod = list_to_atom(binary_to_list(MB)),
Func = list_to_atom(binary_to_list(FB)),
apply(Mod, Func, [X]).
read_file(Path) ->
File = [$.|binary_to_list(Path)],
case file:read_file(File) of
{ok, Bin} -> Bin;
_ -> ["<pre>cannot read:", File, "</pre>"]
end.
键入make run启动服务器,然后在浏览器输入localhost:8080/test2.html就可以看到如下显示(没有下面的json文本),点击click显示json格式文本。
说明浏览器收到了来自Erlang发回的数据。
最后附上test2.html的内容:
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<h1>Test2</h1>
<button id="button1">click</button>
<div id="result"></div>
<script>
$(document).ready(go);
var data = {int:1234,
string:"abcd",
array:[1,2,3,'abc'],
map:{one:'abc', two:1, three:"abc"}};
function go(){
$("#button1").click(test);
}
function test(){
$.ajax({url:"cgi?mod=echo&func=me",
type:'POST',
data:JSON.stringify(data),
success:function(str){
var ret = JSON.parse(str);
$("#result").html("<pre>"+
JSON.stringify(ret, undefined, 4) +
"</pre>");
}});
}
</script>
总结:模仿《Erlang程序设计(第二版)》上第25.4节“用cowboy构建嵌入式Web服务器”的内容,但书里的代码有一些已经过时了,在网上查了很多资料,也加深了自己对cowboy的理解。这个项目等于是更新了书里的echo服务器。希望能对大家有所帮助。