3

To learn Erlang I am trying to implement a tiny web server based on gen_tcp. Unfortunately, my code seems to trigger some wired behaviour. To demonstrate the problem I have attached a minimised version of my implementation which is sufficient to reproduce the problem. It is just delivering a static 200 OK, no matter what the HTTP request was.

The problem arises when I try to run ab (Apache HTTP server benchmarking) against my web server (using loopback interface). Without any concurrent requests (-c) everything is running just fine. However, if I use -c 8 or -c 16, the call to gen_tcp:accept/1 seems to fail on some sockets as I see a number of request: closed lines in the shell.

What makes the whole story even weirder is, that I see different behaviours on different operating systems:

  • OS X+Erlang/OTP 18: ab reports "Connection reset by peer" almost immediately after starting.
  • Debian+Erlang R15B01: All but two of the HTTP requests seem to run through. But then, ab hangs for a few seconds and reports "The timeout specified has expired, Total of 4998 requests completed", when i run ab with -n 5000. Similarly, 14998 is reported when I run 15000 tests.

This one does not seem to be the problem. I am honestly quite lost and therefore appreciate any help! :) Thanks!

server(Port) ->
    Opt = [list, {active, false}, {reuseaddr, true}],
    case gen_tcp:listen(Port, Opt) of
        {ok, Listen} ->
            handler(Listen),
            gen_tcp:close(Listen),
            ok;
        {error, Error} ->
            io:format("init: ~w~n", [Error])
    end.

handler(Listen) ->
    case gen_tcp:accept(Listen) of
        {ok, Client} ->
            request(Client),
            handler(Listen);
        {error, Error} ->
            io:format("request: ~w~n", [Error])
    end.

request(Client) ->
    Recv = gen_tcp:recv(Client, 0),
    case Recv of
        {ok, _} ->
            Response = reply(),
            gen_tcp:send(Client, Response);
        {error, Error} ->
            io:format("request: ~w~n", [Error])
    end,
    gen_tcp:close(Client).


reply() ->
    "HTTP/1.0 200 OK\r\n" ++
    "Content-Length: 7\r\n\r\n"
    "static\n".
Community
  • 1
  • 1
aka
  • 2,723
  • 1
  • 14
  • 10
  • If you can, try this on a different platform, such as Linux. I've found in the past that `ab` on OS X is just horribly broken. I ran your code on Linux with `ab -c 100 -n 5000 http://localhost:...` and it ran just fine. – Steve Vinoski Sep 10 '15 at 22:29
  • @SteveVinoski: Thank you for your comment. I tried on a all new Ubuntu installation. You are right, it works much more stable there. With the fix presented bellow, it also runs smooth on OS X. – aka Sep 12 '15 at 09:46

1 Answers1

4

When you increase the number of concurrent requests sent with ab -c N it will immediately open multiple TCP sockets to the server.

By default a socket opened with gen_tcp:listen/2 will support only five outstanding connection requests. Increase the number of connection requests outstanding with the {backlog, N} option to gen_tcp:listen/2.

I tested your code on OS X with ab and saw this resolve the prolem with "Connection reset by peer".

Vance Shipley
  • 737
  • 4
  • 17
  • Thank you! Works form me as well on OS X at list for `N` <= 128. Have you succeeded in running with `-c 256` ? In my case there seems to problem even if i set `ulimit -n 2048`, start erlang with `+Q 2048` and backlog 2048 ports. – aka Sep 12 '15 at 09:56