├── .gitignore
├── README.md
├── bigtable
├── LICENSE
├── Setup.hs
├── bench
├── bigtable.cabal
├── bigtable.html
├── goliath.rb
├── lighttpd.conf
├── nginx.conf
├── rack.rb
├── snap.hs
├── warp.hs
└── yesod.hs
├── create-graph.hs
├── pong
├── LICENSE
├── Setup.hs
├── bench
├── elli.sh
├── erlang
│ ├── .gitignore
│ ├── apps
│ │ └── elli_pong
│ │ │ ├── rebar.config
│ │ │ └── src
│ │ │ ├── elli_pong.app.src
│ │ │ ├── elli_pong.erl
│ │ │ ├── elli_pong_callback.erl
│ │ │ └── elli_pong_sup.erl
│ ├── bin
│ │ └── start_console
│ ├── rebar
│ ├── rebar.config
│ └── rel
│ │ ├── files
│ │ ├── app.config
│ │ ├── elli_pong
│ │ ├── erl
│ │ ├── nodetool
│ │ └── vm.args
│ │ └── reltool.config
├── goliath.sh
├── happstack.hs
├── happstack.sh
├── java
│ └── web
│ │ ├── Pong.java
│ │ ├── WEB-INF
│ │ └── web.xml
│ │ └── build.sh
├── lib.sh
├── lighttpd.conf
├── nginx.conf
├── nginx.sh
├── node.js
├── node.sh
├── passenger.sh
├── passenger
│ └── conifg.ru
├── php-fpm.conf
├── php.nginx.conf
├── php.sh
├── pong.cabal
├── pong.php
├── pong.txt
├── pong.war
├── python.nginx.conf
├── python.py
├── python.sh
├── rack.rb
├── rack.ru
├── ruby_goliath.rb
├── runall.sh
├── snap.hs
├── snap.sh
├── summary.sh
├── thin.sh
├── tornado.py
├── tornado.sh
├── unicorn.sh
├── warp.hs
├── warp.sh
├── winstone-0.9.10.jar
├── winstone.sh
├── yesod.hs
└── yesod.sh
├── ruby-install.txt
├── setup-python.sh
├── setup.sh
└── static-file
├── LICENSE
├── Setup.hs
├── bench
├── lighttpd.conf
├── nginx.conf
├── snap.hs
├── static-file.cabal
├── static-file.txt
├── warp.hs
└── yesod.hs
/.gitignore:
--------------------------------------------------------------------------------
1 | results/
2 | *.swp
3 | *.cgi
4 | *.hi
5 | *.o
6 | *.ab
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Benchmarks for Web Applications and Web Server Applications
2 |
3 | ## About
4 |
5 | We created these to compare the Warp web server and the Yesod web framework with other web servers and frameworks.
6 |
7 | Here is a blog of the PONG benchmark:
8 | http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks
9 |
10 | In our comparison, we tried to find the fastest servers in other languages. For python we used tornado. For Ruby we used Goliath. Here is an overview of [Ruby deployment options](http://blog.gregweber.info/posts/2011-06-16-high-performance-rb-part3). For Java we used winstone, although there may well be a better option.
11 |
12 | In the future we would like to benchmark against Nginx.
13 |
14 |
15 | ## Installation
16 |
17 | These benchmarks are designed to be ran on a debian/Ubuntu EC2 server. There is an existing install script for every aspect of the installation.
18 |
--------------------------------------------------------------------------------
/bigtable/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c)2010, Michael Snoyman
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Michael Snoyman nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/bigtable/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/bigtable/bench:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | httperf --hog --num-conns 10 --num-calls 10 --burst-length 20 --port 3000 --rate 10 --server localhost --uri /bigtable.html
4 |
--------------------------------------------------------------------------------
/bigtable/bigtable.cabal:
--------------------------------------------------------------------------------
1 | Name: bigtable
2 | Version: 0.1
3 | Synopsis: Bigtable benchmark
4 | License: BSD3
5 | License-file: LICENSE
6 | Author: Michael Snoyman
7 | Maintainer: michael@snoyman.com
8 | Category: Web
9 | Build-type: Simple
10 |
11 | Cabal-version: >=1.2
12 |
13 |
14 | Executable warp
15 | Main-is: warp.hs
16 | Build-depends: base > 4, wai, warp, blaze-builder, http-types >= 0.7
17 | Ghc-options: -O2 -threaded -rtsopts
18 |
19 | Executable snap
20 | Main-is: snap.hs
21 | Build-depends: base > 4, snap-server >= 0.3 && < 0.4, snap-core
22 | Ghc-options: -O2 -threaded -rtsopts
23 |
24 | Executable yesod
25 | Main-is: yesod.hs
26 | Build-depends: base > 4, yesod-core >= 1, bytestring
27 | Ghc-options: -O2 -threaded -rtsopts
28 |
--------------------------------------------------------------------------------
/bigtable/goliath.rb:
--------------------------------------------------------------------------------
1 | require 'goliath'
2 |
3 | class Hello < Goliath::API
4 | def response(env)
5 | [200, {},
6 | tableBody("
"
8 | ]
9 | end
10 |
11 | def tableBody str
12 | (1..1000).each do |n|
13 | str = tableRow(str << "#{n} | ") << "
\n"
14 | end
15 | str
16 | end
17 |
18 | def tableRow str
19 | (1..50).each do |n|
20 | str << "#{n} | "
21 | end
22 | str
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/bigtable/lighttpd.conf:
--------------------------------------------------------------------------------
1 | # Run with lighttpd -D -f lighttpd.conf
2 | server.port = 3000
3 | server.document-root = "."
4 | server.modules = ()
5 |
6 | mimetype.assign = (".html" => "text/html")
7 |
--------------------------------------------------------------------------------
/bigtable/nginx.conf:
--------------------------------------------------------------------------------
1 | daemon off;
2 | events {
3 | worker_connections 4096;
4 | }
5 | http {
6 | server {
7 | listen 3000;
8 | root /home/snoyman/haskell/benchmarks/bigtable;
9 | index bigtable.html;
10 | }
11 | types {
12 | text/html html;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/bigtable/rack.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'rack'
3 | class RackApp
4 | def call(env)
5 | Rack::Response.new(
6 | tableBody(""
8 | ).finish
9 | end
10 |
11 | def tableBody str
12 | (1..1000).each do |n|
13 | str = tableRow(str << "#{n} | ") << "
\n"
14 | end
15 | str
16 | end
17 |
18 | def tableRow str
19 | (1..50).each do |n|
20 | str << "#{n} | "
21 | end
22 | str
23 | end
24 | end
25 | run RackApp.new
26 |
--------------------------------------------------------------------------------
/bigtable/snap.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Main where
3 |
4 | import Control.Applicative
5 | import qualified Data.ByteString.Char8 as BS
6 | import Snap.Http.Server
7 | import Snap.Iteratee
8 | import Snap.Types
9 | import Snap.Util.FileServe
10 |
11 |
12 | tableRow :: Int -> Snap ()
13 | tableRow x
14 | | x <= 50 = do
15 | writeBS ""
16 | writeBS (BS.pack (show x))
17 | writeBS " | "
18 | tableRow (x+1)
19 | | otherwise = return ()
20 |
21 | tableBody :: Int -> Snap ()
22 | tableBody x
23 | | x <= 1000 = do
24 | writeBS ""
25 | writeBS (BS.pack (show x))
26 | writeBS " | "
27 | tableRow 1
28 | writeBS "
\n"
29 | tableBody (x+1)
30 | | otherwise = return ()
31 |
32 | tableServer :: Snap ()
33 | tableServer = do
34 | writeBS "\n"
35 | tableBody 1
36 | writeBS "
"
37 |
38 | main :: IO ()
39 | main =
40 | let config = addListen (ListenHttp "*" 3000) $
41 | setAccessLog Nothing $
42 | setErrorLog Nothing $
43 | defaultConfig
44 | in httpServe config tableServer
45 |
--------------------------------------------------------------------------------
/bigtable/warp.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | import Network.Wai
3 | import Network.Wai.Handler.Warp
4 | import Blaze.ByteString.Builder (copyByteString, fromLazyByteString, toLazyByteString)
5 | import Blaze.ByteString.Builder.Char8 (fromShow)
6 | import Data.Monoid (mappend, mempty)
7 | import Network.HTTP.Types (status200)
8 |
9 | main = run 3000 $ const $ return $ ResponseBuilder
10 | status200
11 | [("Content-Type", "text/html")]
12 | table
13 |
14 | table = {-fromLazyByteString $ toLazyByteString $-}
15 | copyByteString "\n"
16 | `mappend` tableBody 1
17 | `mappend` copyByteString "
"
18 |
19 | tableRow x
20 | | x <= 50 =
21 | copyByteString ""
22 | `mappend` fromShow x
23 | `mappend` copyByteString " | "
24 | `mappend` tableRow (x + 1)
25 | | otherwise = mempty
26 |
27 | tableBody x
28 | | x <= 1000 =
29 | copyByteString ""
30 | `mappend` fromShow x
31 | `mappend` copyByteString " | "
32 | `mappend` tableRow 1
33 | `mappend` copyByteString "
\n"
34 | `mappend` tableBody (x + 1)
35 | | otherwise = mempty
36 |
--------------------------------------------------------------------------------
/bigtable/yesod.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, QuasiQuotes, TypeFamilies, CPP, TemplateHaskell, MultiParamTypeClasses #-}
2 | import Yesod.Dispatch
3 | import Yesod.Core
4 | import Yesod.Content
5 | import Yesod.Handler
6 | import Data.ByteString (ByteString)
7 | import Network.Wai.Handler.Warp (run)
8 | import Blaze.ByteString.Builder (copyByteString)
9 | import Blaze.ByteString.Builder.Char8 (fromShow)
10 | import Data.Monoid (mappend, mempty)
11 |
12 | data Pong = Pong
13 | mkYesod "Pong"
14 | #if __GLASGOW_HASKELL__ >= 700
15 | [parseRoutes|
16 | #else
17 | [$parseRoutes|
18 | #endif
19 | /bigtable.html PongR GET
20 | |]
21 | instance Yesod Pong where
22 | makeSessionBackend _ = return Nothing
23 |
24 | getPongR = return $ RepHtml $ ContentBuilder table Nothing
25 |
26 | main = toWaiAppPlain Pong >>= run 3000
27 |
28 | table =
29 | copyByteString "\n"
30 | `mappend` tableBody 1
31 | `mappend` copyByteString "
"
32 |
33 | tableRow x
34 | | x <= 50 =
35 | copyByteString ""
36 | `mappend` fromShow x
37 | `mappend` copyByteString " | "
38 | `mappend` tableRow (x + 1)
39 | | otherwise = mempty
40 |
41 | tableBody x
42 | | x <= 1000 =
43 | copyByteString ""
44 | `mappend` fromShow x
45 | `mappend` copyByteString " | "
46 | `mappend` tableRow 1
47 | `mappend` copyByteString "
\n"
48 | `mappend` tableBody (x + 1)
49 | | otherwise = mempty
50 |
--------------------------------------------------------------------------------
/create-graph.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | import Data.XML.Types
3 | import Text.XML.Enumerator.Document (writeFile)
4 | import qualified Data.Map as Map
5 | import Prelude hiding (writeFile)
6 | import Data.Text (pack)
7 |
8 | largest = 900
9 |
10 | results =
11 | [ ("warp", 81700.9)
12 | , ("yesod", 64027.8)
13 | , ("happstack", 35810.8)
14 | , ("snap", 35272.2)
15 | , ("node", 18654.4)
16 | , ("winstone", 4659.6)
17 | , ("php", 3416.9)
18 | , ("tornado", 3416.0)
19 | , ("goliath", 3236.9)
20 | ]
21 | {-
22 | [ ("warp", 53924.6)
23 | , ("yesod", 49355.3)
24 | , ("happstack", 29696.2)
25 | , ("snap", 27987.8)
26 | , ("node", 13610.1)
27 | , ("winstone", 4551.7)
28 | , ("tornado", 3321.7)
29 | , ("goliath", 3007.9)
30 | , ("php", 2728.1)
31 | ]
32 | -}
33 |
34 | doctype = Doctype "svg" (Just $ PublicID "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd") []
35 |
36 | svg = Element "{http://www.w3.org/2000/svg}svg" (Map.fromList
37 | [ ("version", [ContentText "1.1"])
38 | , ("font-family", [ContentText "DejaVu Sans"])
39 | , ("font-size", [ContentText "16pt"])
40 | ]) $ map NodeElement $ concat $ zipWith mkResult [0..] $ reverse results
41 |
42 | mkResult i (name, val) =
43 | [ Element "{http://www.w3.org/2000/svg}rect" (Map.fromList
44 | [ ("width", [ContentText "50"])
45 | , ("height", [ContentText $ pack $ show $ normalize val'])
46 | , ("x", [ContentText $ pack $ show $ i * 60])
47 | , ("y", [ContentText $ pack $ show $ normalize $ largest - val'])
48 | , ("style", [ContentText $ pack $ concat
49 | [ "fill:#"
50 | , (cycle colors) !! i
51 | , ";stroke-width:1;stroke:black"
52 | ]])
53 | ]) []
54 | , Element "{http://www.w3.org/2000/svg}text" (Map.fromList
55 | [ ("x", [ContentText $ pack $ show x])
56 | , ("y", [ContentText $ pack $ show y])
57 | , ("transform", [ContentText $ pack $ concat ["rotate(45, ", show x, ", ", show y, ")"]])
58 | ]) [NodeContent $ ContentText $ pack name]
59 | , Element "{http://www.w3.org/2000/svg}text" (Map.fromList
60 | [ ("x", [ContentText $ pack $ show $ x - 10])
61 | , ("y", [ContentText $ pack $ show $ normalize (largest - val') - 5])
62 | , ("font-size", [ContentText "8pt"])
63 | ]) [NodeContent $ ContentText $ pack $ prettyNum val]
64 | ]
65 | where
66 | y = normalize largest + 20
67 | x = 20 + i * 60
68 | val' = val / 100
69 | normalize = (*) 0.4
70 |
71 | prettyNum = prettyInt . round
72 |
73 | prettyInt x
74 | | x < 1000 = show x
75 | | otherwise =
76 | let (y, z) = x `divMod` 1000
77 | in prettyInt y ++ ',' : showLead z
78 | where
79 | showLead a
80 | | a < 10 = '0' : '0' : show a
81 | | a < 100 = '0' : show a
82 | | otherwise = show a
83 |
84 | main = writeFile "benchmark2.svg" $ Document (Prologue [] (Just doctype) []) svg []
85 |
86 | colors =
87 | [ "990033"
88 | , "336699"
89 | , "FFFF00"
90 | , "669966"
91 | ]
92 |
--------------------------------------------------------------------------------
/pong/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c)2010, Michael Snoyman
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Michael Snoyman nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/pong/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/pong/bench:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | httperf --hog --num-conns 1000 --num-calls 1000 --burst-length 20 --port 3000 --rate 1000 --server localhost
4 |
--------------------------------------------------------------------------------
/pong/elli.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | ./erlang/rel/elli_pong/bin/elli_pong start
7 | /bin/sleep 600 & # just to give the benchmark function s/th to kill later
8 |
9 | benchmark
10 |
11 | ./erlang/rel/elli_pong/bin/elli_pong stop
12 |
--------------------------------------------------------------------------------
/pong/erlang/.gitignore:
--------------------------------------------------------------------------------
1 | *.dump
2 | *.beam
3 | .eunit
4 | deps
5 | ebin
6 | log
7 | /rel/elli_pong
8 |
--------------------------------------------------------------------------------
/pong/erlang/apps/elli_pong/rebar.config:
--------------------------------------------------------------------------------
1 | {erl_opts, []}.
2 | {require_otp_vsn, "R15"}.
3 | {deps, [
4 | {elli, "0.2.0", {git, "git://github.com/knutin/elli.git", {tag,"740ee98e23cd8f921c62c50a0dabae340d508b75"}}}
5 | ]}.
6 | {clean_files, ["ebin/*.beam"]}.
7 | {xref_checks, [undefined_function_calls]}.
8 |
--------------------------------------------------------------------------------
/pong/erlang/apps/elli_pong/src/elli_pong.app.src:
--------------------------------------------------------------------------------
1 | {application, elli_pong,
2 | [
3 | {description, "Pong benchmark for the Elli webserver"},
4 | {vsn, "0.0.1"},
5 | {registered, []},
6 | {applications, [
7 | kernel,
8 | stdlib,
9 | sasl
10 | ]},
11 | {mod, {elli_pong, []}},
12 | {env, []}
13 | ]
14 | }.
15 |
--------------------------------------------------------------------------------
/pong/erlang/apps/elli_pong/src/elli_pong.erl:
--------------------------------------------------------------------------------
1 | -module(elli_pong).
2 | -behaviour(application).
3 |
4 | -export([start/0, start/2, stop/1]).
5 |
6 | start() ->
7 | application:start(elli_pong).
8 |
9 | start(_StartType, _StartArgs) ->
10 | case elli_pong_sup:start_link() of
11 | {ok, Pid} -> {ok, Pid};
12 | Other -> {error, Other}
13 | end.
14 |
15 | stop(_State) ->
16 | ok.
17 |
--------------------------------------------------------------------------------
/pong/erlang/apps/elli_pong/src/elli_pong_callback.erl:
--------------------------------------------------------------------------------
1 | -module(elli_pong_callback).
2 | -behaviour(elli_handler).
3 |
4 | -export([handle/2, handle_event/3]).
5 |
6 | handle(_Req, _Args) ->
7 | {200, [{<<"Content-Type">>, <<"text/plain">>}], <<"PONG">>}.
8 |
9 | handle_event(_, _, _) ->
10 | ok.
11 |
--------------------------------------------------------------------------------
/pong/erlang/apps/elli_pong/src/elli_pong_sup.erl:
--------------------------------------------------------------------------------
1 | -module(elli_pong_sup).
2 | -behaviour(supervisor).
3 |
4 | %% API
5 | -export([start_link/0]).
6 |
7 | %% Supervisor callbacks
8 | -export([init/1]).
9 |
10 | %% ===================================================================
11 | %% API functions
12 | %% ===================================================================
13 |
14 | start_link() ->
15 | supervisor:start_link({local, ?MODULE}, ?MODULE, []).
16 |
17 | %% ===================================================================
18 | %% Supervisor callbacks
19 | %% ===================================================================
20 |
21 | init([]) ->
22 | {ok, {{one_for_all, 0, 1}, [webserver()]}}.
23 |
24 |
25 | %% ===================================================================
26 | %% Internal functions
27 | %% ===================================================================
28 |
29 | webserver() ->
30 | {webserver,
31 | {elli, start_link, [[{port, 3000},
32 | {callback, elli_pong_callback}]]},
33 | permanent, 5000, worker, [elli]}.
34 |
--------------------------------------------------------------------------------
/pong/erlang/bin/start_console:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | SCRIPTDIR=$(dirname $0)
4 | cd "$SCRIPTDIR/.."
5 |
6 | erl -pa deps/*/ebin apps/*/ebin \
7 | -boot start_sasl \
8 | -args_file rel/files/vm.args \
9 | -s elli_pong \
10 | -config rel/files/app
11 |
--------------------------------------------------------------------------------
/pong/erlang/rebar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yesodweb/benchmarks/f49720b9dcc565d7c269adbf4d6b91eeda7c323e/pong/erlang/rebar
--------------------------------------------------------------------------------
/pong/erlang/rebar.config:
--------------------------------------------------------------------------------
1 | {sub_dirs, [
2 | "apps/elli_pong",
3 | "rel"
4 | ]}.
5 | {erl_opts, []}.
6 | {require_otp_vsn, "R15"}.
7 |
--------------------------------------------------------------------------------
/pong/erlang/rel/files/app.config:
--------------------------------------------------------------------------------
1 | [
2 | {sasl, [
3 | {sasl_error_logger, {file, "log/sasl-error.log"}},
4 | {errlog_type, error},
5 | {error_logger_mf_dir, "log/sasl"}, % Log directory
6 | {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size
7 | {error_logger_mf_maxfiles, 5} % 5 files max
8 | ]}
9 | ].
10 |
--------------------------------------------------------------------------------
/pong/erlang/rel/files/elli_pong:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # -*- tab-width:4;indent-tabs-mode:nil -*-
3 | # ex: ts=4 sw=4 et
4 |
5 | RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd)
6 |
7 | RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}
8 | RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc
9 | RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log
10 | # Note the trailing slash on $PIPE_DIR/
11 | PIPE_DIR=/tmp/$RUNNER_BASE_DIR/
12 | RUNNER_USER=
13 |
14 | # Make sure this script is running as the appropriate user
15 | if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then
16 | exec sudo -u $RUNNER_USER -i $0 $@
17 | fi
18 |
19 | # Make sure CWD is set to runner base dir
20 | cd $RUNNER_BASE_DIR
21 |
22 | # Make sure log directory exists
23 | mkdir -p $RUNNER_LOG_DIR
24 |
25 | # Extract the target node name from node.args
26 | NAME_ARG=`egrep -e '^-s?name' $RUNNER_ETC_DIR/vm.args`
27 | if [ -z "$NAME_ARG" ]; then
28 | echo "vm.args needs to have either -name or -sname parameter."
29 | exit 1
30 | fi
31 |
32 | # Extract the target cookie
33 | COOKIE_ARG=`grep -e '^-setcookie' $RUNNER_ETC_DIR/vm.args`
34 | if [ -z "$COOKIE_ARG" ]; then
35 | echo "vm.args needs to have a -setcookie parameter."
36 | exit 1
37 | fi
38 |
39 | # Identify the script name
40 | SCRIPT=`basename $0`
41 |
42 | # Parse out release and erts info
43 | START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data`
44 | ERTS_VSN=${START_ERL% *}
45 | APP_VSN=${START_ERL#* }
46 |
47 | # Add ERTS bin dir to our path
48 | ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin
49 |
50 | # Setup command to control the node
51 | NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG"
52 |
53 | # Check the first argument for instructions
54 | case "$1" in
55 | start)
56 | # Make sure there is not already a node running
57 | RES=`$NODETOOL ping`
58 | if [ "$RES" = "pong" ]; then
59 | echo "Node is already running!"
60 | exit 1
61 | fi
62 | HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start"
63 | export HEART_COMMAND
64 | mkdir -p $PIPE_DIR
65 | shift # remove $1
66 | $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $@" 2>&1
67 | ;;
68 |
69 | stop)
70 | # Wait for the node to completely stop...
71 | case `uname -s` in
72 | Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD)
73 | # PID COMMAND
74 | PID=`ps ax -o pid= -o command=|\
75 | grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'`
76 | ;;
77 | SunOS)
78 | # PID COMMAND
79 | PID=`ps -ef -o pid= -o args=|\
80 | grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'`
81 | ;;
82 | CYGWIN*)
83 | # UID PID PPID TTY STIME COMMAND
84 | PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'`
85 | ;;
86 | esac
87 | $NODETOOL stop
88 | while `kill -0 $PID 2>/dev/null`;
89 | do
90 | sleep 1
91 | done
92 | ;;
93 |
94 | restart)
95 | ## Restart the VM without exiting the process
96 | $NODETOOL restart
97 | ;;
98 |
99 | reboot)
100 | ## Restart the VM completely (uses heart to restart it)
101 | $NODETOOL reboot
102 | ;;
103 |
104 | ping)
105 | ## See if the VM is alive
106 | $NODETOOL ping
107 | ;;
108 |
109 | attach)
110 | # Make sure a node IS running
111 | RES=`$NODETOOL ping`
112 | if [ "$RES" != "pong" ]; then
113 | echo "Node is not running!"
114 | exit 1
115 | fi
116 |
117 | shift
118 | $ERTS_PATH/to_erl $PIPE_DIR
119 | ;;
120 |
121 | console|console_clean)
122 | # .boot file typically just $SCRIPT (ie, the app name)
123 | # however, for debugging, sometimes start_clean.boot is useful:
124 | case "$1" in
125 | console) BOOTFILE=$SCRIPT ;;
126 | console_clean) BOOTFILE=start_clean ;;
127 | esac
128 | # Setup beam-required vars
129 | ROOTDIR=$RUNNER_BASE_DIR
130 | BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
131 | EMU=beam
132 | PROGNAME=`echo $0 | sed 's/.*\\///'`
133 | CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $RUNNER_ETC_DIR/app.config -args_file $RUNNER_ETC_DIR/vm.args -- ${1+"$@"}"
134 | export EMU
135 | export ROOTDIR
136 | export BINDIR
137 | export PROGNAME
138 |
139 | # Dump environment info for logging purposes
140 | echo "Exec: $CMD"
141 | echo "Root: $ROOTDIR"
142 |
143 | # Log the startup
144 | logger -t "$SCRIPT[$$]" "Starting up"
145 |
146 | # Start the VM
147 | exec $CMD
148 | ;;
149 |
150 | *)
151 | echo "Usage: $SCRIPT {start|stop|restart|reboot|ping|console|console_clean|attach}"
152 | exit 1
153 | ;;
154 | esac
155 |
156 | exit 0
157 |
--------------------------------------------------------------------------------
/pong/erlang/rel/files/erl:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## This script replaces the default "erl" in erts-VSN/bin. This is necessary
4 | ## as escript depends on erl and in turn, erl depends on having access to a
5 | ## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect
6 | ## of running escript -- the embedded node bypasses erl and uses erlexec directly
7 | ## (as it should).
8 | ##
9 | ## Note that this script makes the assumption that there is a start_clean.boot
10 | ## file available in $ROOTDIR/release/VSN.
11 |
12 | # Determine the abspath of where this script is executing from.
13 | ERTS_BIN_DIR=$(cd ${0%/*} && pwd)
14 |
15 | # Now determine the root directory -- this script runs from erts-VSN/bin,
16 | # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR
17 | # path.
18 | ROOTDIR=${ERTS_BIN_DIR%/*/*}
19 |
20 | # Parse out release and erts info
21 | START_ERL=`cat $ROOTDIR/releases/start_erl.data`
22 | ERTS_VSN=${START_ERL% *}
23 | APP_VSN=${START_ERL#* }
24 |
25 | BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
26 | EMU=beam
27 | PROGNAME=`echo $0 | sed 's/.*\\///'`
28 | CMD="$BINDIR/erlexec"
29 | export EMU
30 | export ROOTDIR
31 | export BINDIR
32 | export PROGNAME
33 |
34 | exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"}
--------------------------------------------------------------------------------
/pong/erlang/rel/files/nodetool:
--------------------------------------------------------------------------------
1 | %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
2 | %% ex: ft=erlang ts=4 sw=4 et
3 | %% -------------------------------------------------------------------
4 | %%
5 | %% nodetool: Helper Script for interacting with live nodes
6 | %%
7 | %% -------------------------------------------------------------------
8 |
9 | main(Args) ->
10 | ok = start_epmd(),
11 | %% Extract the args
12 | {RestArgs, TargetNode} = process_args(Args, [], undefined),
13 |
14 | %% See if the node is currently running -- if it's not, we'll bail
15 | case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of
16 | {true, pong} ->
17 | ok;
18 | {_, pang} ->
19 | io:format("Node ~p not responding to pings.\n", [TargetNode]),
20 | halt(1)
21 | end,
22 |
23 | case RestArgs of
24 | ["ping"] ->
25 | %% If we got this far, the node already responsed to a ping, so just dump
26 | %% a "pong"
27 | io:format("pong\n");
28 | ["stop"] ->
29 | io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]);
30 | ["restart"] ->
31 | io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]);
32 | ["reboot"] ->
33 | io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]);
34 | ["rpc", Module, Function | RpcArgs] ->
35 | case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
36 | [RpcArgs], 60000) of
37 | ok ->
38 | ok;
39 | {badrpc, Reason} ->
40 | io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
41 | halt(1);
42 | _ ->
43 | halt(1)
44 | end;
45 | ["rpcterms", Module, Function, ArgsAsString] ->
46 | case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
47 | consult(ArgsAsString), 60000) of
48 | {badrpc, Reason} ->
49 | io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
50 | halt(1);
51 | Other ->
52 | io:format("~p\n", [Other])
53 | end;
54 | Other ->
55 | io:format("Other: ~p\n", [Other]),
56 | io:format("Usage: nodetool {ping|stop|restart|reboot}\n")
57 | end,
58 | net_kernel:stop().
59 |
60 | process_args([], Acc, TargetNode) ->
61 | {lists:reverse(Acc), TargetNode};
62 | process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) ->
63 | erlang:set_cookie(node(), list_to_atom(Cookie)),
64 | process_args(Rest, Acc, TargetNode);
65 | process_args(["-name", TargetName | Rest], Acc, _) ->
66 | ThisNode = append_node_suffix(TargetName, "_maint_"),
67 | {ok, _} = net_kernel:start([ThisNode, longnames]),
68 | process_args(Rest, Acc, nodename(TargetName));
69 | process_args(["-sname", TargetName | Rest], Acc, _) ->
70 | ThisNode = append_node_suffix(TargetName, "_maint_"),
71 | {ok, _} = net_kernel:start([ThisNode, shortnames]),
72 | process_args(Rest, Acc, nodename(TargetName));
73 | process_args([Arg | Rest], Acc, Opts) ->
74 | process_args(Rest, [Arg | Acc], Opts).
75 |
76 |
77 | start_epmd() ->
78 | [] = os:cmd(epmd_path() ++ " -daemon"),
79 | ok.
80 |
81 | epmd_path() ->
82 | ErtsBinDir = filename:dirname(escript:script_name()),
83 | Name = "epmd",
84 | case os:find_executable(Name, ErtsBinDir) of
85 | false ->
86 | case os:find_executable(Name) of
87 | false ->
88 | io:format("Could not find epmd.~n"),
89 | halt(1);
90 | GlobalEpmd ->
91 | GlobalEpmd
92 | end;
93 | Epmd ->
94 | Epmd
95 | end.
96 |
97 |
98 | nodename(Name) ->
99 | case string:tokens(Name, "@") of
100 | [_Node, _Host] ->
101 | list_to_atom(Name);
102 | [Node] ->
103 | [_, Host] = string:tokens(atom_to_list(node()), "@"),
104 | list_to_atom(lists:concat([Node, "@", Host]))
105 | end.
106 |
107 | append_node_suffix(Name, Suffix) ->
108 | case string:tokens(Name, "@") of
109 | [Node, Host] ->
110 | list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host]));
111 | [Node] ->
112 | list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
113 | end.
114 |
115 |
116 | %%
117 | %% Given a string or binary, parse it into a list of terms, ala file:consult/0
118 | %%
119 | consult(Str) when is_list(Str) ->
120 | consult([], Str, []);
121 | consult(Bin) when is_binary(Bin)->
122 | consult([], binary_to_list(Bin), []).
123 |
124 | consult(Cont, Str, Acc) ->
125 | case erl_scan:tokens(Cont, Str, 0) of
126 | {done, Result, Remaining} ->
127 | case Result of
128 | {ok, Tokens, _} ->
129 | {ok, Term} = erl_parse:parse_term(Tokens),
130 | consult([], Remaining, [Term | Acc]);
131 | {eof, _Other} ->
132 | lists:reverse(Acc);
133 | {error, Info, _} ->
134 | {error, Info}
135 | end;
136 | {more, Cont1} ->
137 | consult(Cont1, eof, Acc)
138 | end.
139 |
--------------------------------------------------------------------------------
/pong/erlang/rel/files/vm.args:
--------------------------------------------------------------------------------
1 | ## Name of the node
2 | -sname elli_pong
3 |
4 | ## Cookie for distributed erlang
5 | -setcookie elli_pong
6 |
7 | ## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
8 | ## (Disabled by default..use with caution!)
9 | ##-heart
10 |
11 | ## increase the maximum number of allowed processes inside the VM
12 | +P 200000
13 |
14 | ## Enable kernel poll and a async threads
15 | +K true
16 | +A 2
17 |
--------------------------------------------------------------------------------
/pong/erlang/rel/reltool.config:
--------------------------------------------------------------------------------
1 | {sys, [
2 | {lib_dirs, ["../apps",
3 | "../deps"
4 | ]},
5 | {rel, "elli_pong", "1",
6 | [
7 | kernel,
8 | stdlib,
9 | sasl,
10 | elli_pong
11 | ]},
12 | {rel, "start_clean", "",
13 | [
14 | kernel,
15 | stdlib
16 | ]},
17 | {boot_rel, "elli_pong"},
18 | {profile, embedded},
19 | {excl_sys_filters, ["^bin/.*",
20 | "^erts.*/bin/(dialyzer|typer)"]},
21 | {excl_archive_filters, [".*"]},
22 | {app, kernel, [{incl_cond, include}]},
23 | {app, stdlib, [{incl_cond, include}]},
24 | {app, sasl, [{incl_cond, include}]},
25 | {app, elli, [{incl_cond, include}]},
26 | {app, elli_pong, [{incl_cond, include}]}
27 | ]}.
28 |
29 | {target_dir, "elli_pong"}.
30 |
31 | {overlay, [
32 | {mkdir, "log/sasl"},
33 | {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
34 | {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
35 | {copy, "files/elli_pong", "bin/elli_pong"},
36 | {copy, "files/app.config", "etc/app.config"},
37 | {copy, "files/vm.args", "etc/vm.args"}
38 | ]}.
39 |
--------------------------------------------------------------------------------
/pong/goliath.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | source ~/.rvm/scripts/rvm
7 | rvm use 1.9.2
8 | ruby ruby_goliath.rb -e production -p 3000 &
9 |
10 | benchmark
11 |
--------------------------------------------------------------------------------
/pong/happstack.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | import Happstack.Server
4 |
5 | main = simpleHTTP nullConf
6 | { logAccess = Nothing
7 | , port = 3000
8 | } $ ok "PONG"
9 |
--------------------------------------------------------------------------------
/pong/happstack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | ./dist/build/happstack/happstack $* &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/java/web/Pong.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import java.io.*;
4 |
5 | import javax.servlet.http.*;
6 | import javax.servlet.*;
7 |
8 | public class Pong extends HttpServlet {
9 | public void doGet (HttpServletRequest req,
10 | HttpServletResponse res)
11 | throws ServletException, IOException
12 | {
13 | PrintWriter out = res.getWriter();
14 |
15 | out.println("PONG");
16 | out.close();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pong/java/web/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | pong
10 | test.Pong
11 |
12 |
13 |
14 | pong
15 | /pong
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/pong/java/web/build.sh:
--------------------------------------------------------------------------------
1 | rm -f ../../pong.war WEB-INF/lib/pong.jar && javac Pong.java -cp ../../winstone-0.9.10.jar && mkdir -p test && cp Pong.class test && mkdir -p WEB-INF/lib && jar cfv WEB-INF/lib/pong.jar test && jar cfv pong.war WEB-INF/ && mv pong.war ../..
2 |
--------------------------------------------------------------------------------
/pong/lib.sh:
--------------------------------------------------------------------------------
1 | benchmark(){
2 | sleep 5
3 | nx=$!
4 | httperf --hog --server=localhost --port=3000 --uri=/ --rate=1000 --num-conns=1000 --num-calls=1000 --burst-length=20 > results/`basename $0 .sh`
5 | kill $nx
6 | sleep 2
7 | }
8 |
--------------------------------------------------------------------------------
/pong/lighttpd.conf:
--------------------------------------------------------------------------------
1 | # Run with lighttpd -D -f lighttpd.conf
2 | server.port = 3000
3 | server.document-root = "."
4 | server.modules = ("mod_rewrite")
5 |
6 | url.rewrite-once = (
7 | "(.*)" => "/pong.txt"
8 | )
9 |
10 | mimetype.assign = (".txt" => "text/plain")
11 |
--------------------------------------------------------------------------------
/pong/nginx.conf:
--------------------------------------------------------------------------------
1 | daemon off;
2 | events {
3 | worker_connections 4096;
4 | }
5 | http {
6 | server {
7 | listen 0.0.0.0:3000;
8 | server_name _;
9 | root /home/ubuntu/benchmarks/pong;
10 | index pong.txt;
11 | }
12 | types {
13 | text/plain txt;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pong/nginx.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 |
3 | d=$(readlink -f $(dirname $0))
4 |
5 | sudo /usr/local/nginx/sbin/nginx -c $d/nginx.conf &
6 | nx=$!
7 |
8 | httperf --hog --server=localhost --port=3000 --uri=/ --rate=1000 --num-conns=200 --num-calls=100 --burst-length=20 > results/nginx
9 | sudo kill $nx
10 |
--------------------------------------------------------------------------------
/pong/node.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var cluster = require('cluster');
3 |
4 | if(cluster.isMaster) {
5 | for(var i = 0; i < 3; i++) {
6 | cluster.fork();
7 | }
8 | } else {
9 | http.createServer(function (req, res) {
10 | res.writeHead(200, {'Content-Type': 'text/plain'});
11 | res.end('PONG');
12 | }).listen(3000, "127.0.0.1");
13 | }
14 |
--------------------------------------------------------------------------------
/pong/node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | node node.js &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/passenger.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | mkdir -p passenger/public
7 | mkdir -p passenger/tmp
8 |
9 | sudo /opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf
10 |
11 | benchmark
12 |
--------------------------------------------------------------------------------
/pong/passenger/conifg.ru:
--------------------------------------------------------------------------------
1 | class RackApp
2 | def call(env)
3 | [200, {'Content-Type' => 'text/plain'}, ["PONG"]]
4 | end
5 | end
6 | run RackApp.new
7 |
--------------------------------------------------------------------------------
/pong/php-fpm.conf:
--------------------------------------------------------------------------------
1 | [global]
2 | error_log = /dev/null
3 | log_level = alert
4 | daemonize = no
5 |
6 | [www]
7 | listen = 127.0.0.1:9000
8 | user = nobody
9 | group = nobody
10 | pm = static
11 | pm.max_children = 20
12 |
--------------------------------------------------------------------------------
/pong/php.nginx.conf:
--------------------------------------------------------------------------------
1 | daemon off;
2 | events {
3 | worker_connections 4096;
4 | }
5 | http {
6 | server {
7 | listen 3000;
8 | server_name localhost;
9 | access_log off; #/var/log/nginx/localhost.access.log;
10 |
11 | ## Default location
12 | location / {
13 | root /home/ubuntu/benchmarks/pong;
14 | index pong.php;
15 | }
16 |
17 | ## Images and static content is treated different
18 |
19 | ## Parse all .php file in the /var/www directory
20 | location ~ .php$ {
21 | fastcgi_split_path_info ^(.+\.php)(.*)$;
22 | fastcgi_pass backend;
23 | fastcgi_index pong.php;
24 | fastcgi_param SCRIPT_FILENAME /home/ubuntu/benchmarks/pong$fastcgi_script_name;
25 | include /usr/local/nginx/conf/fastcgi_params;
26 | fastcgi_intercept_errors on;
27 | fastcgi_ignore_client_abort off;
28 | fastcgi_connect_timeout 60;
29 | fastcgi_send_timeout 180;
30 | fastcgi_read_timeout 180;
31 | fastcgi_buffer_size 128k;
32 | fastcgi_buffers 4 256k;
33 | fastcgi_busy_buffers_size 256k;
34 | fastcgi_temp_file_write_size 256k;
35 | }
36 |
37 | }
38 | upstream backend {
39 | server 127.0.0.1:9000;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/pong/php.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | php-fpm -n -y php-fpm.conf &
7 |
8 | /usr/local/nginx/sbin/nginx -c $d/php.nginx.conf &
9 |
10 | benchmark
11 |
12 | killall -KILL php-fpm
13 |
--------------------------------------------------------------------------------
/pong/pong.cabal:
--------------------------------------------------------------------------------
1 | Name: pong
2 | Version: 0.1
3 | Synopsis: Pong benchmark
4 | License: BSD3
5 | License-file: LICENSE
6 | Author: Michael Snoyman
7 | Maintainer: michael@snoyman.com
8 | Category: Web
9 | Build-type: Simple
10 |
11 | Cabal-version: >=1.2
12 |
13 |
14 | Executable warp
15 | Main-is: warp.hs
16 | Build-depends: base > 4, wai, warp >= 0.3.2.1, blaze-builder, http-types >= 0.7
17 | Ghc-options: -O2 -threaded -rtsopts
18 |
19 | Executable snap
20 | Main-is: snap.hs
21 | Build-depends: base > 4, snap-server >= 0.4 && < 0.5, snap-core
22 | Ghc-options: -O2 -threaded -rtsopts
23 |
24 | Executable happstack
25 | Main-is: happstack.hs
26 | Build-depends: base > 4, happstack-server
27 | Ghc-options: -O2 -threaded -rtsopts
28 |
29 | Executable yesod
30 | Main-is: yesod.hs
31 | Build-depends: base > 4, yesod-core, bytestring
32 | Ghc-options: -O2 -threaded -rtsopts
33 |
--------------------------------------------------------------------------------
/pong/pong.php:
--------------------------------------------------------------------------------
1 | PONG
2 |
--------------------------------------------------------------------------------
/pong/pong.txt:
--------------------------------------------------------------------------------
1 | PONG
--------------------------------------------------------------------------------
/pong/pong.war:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yesodweb/benchmarks/f49720b9dcc565d7c269adbf4d6b91eeda7c323e/pong/pong.war
--------------------------------------------------------------------------------
/pong/python.nginx.conf:
--------------------------------------------------------------------------------
1 | daemon off;
2 | events {
3 | worker_connections 4096;
4 | }
5 |
6 | http {
7 | server {
8 | listen 3000;
9 | server_name _;
10 |
11 | location / {
12 | # host and port to fastcgi server
13 | fastcgi_pass 127.0.0.1:9000;
14 | fastcgi_param SERVER_NAME $server_name;
15 | fastcgi_param SERVER_PORT $server_port;
16 | fastcgi_param SERVER_PROTOCOL $server_protocol;
17 | fastcgi_param PATH_INFO $fastcgi_script_name;
18 | fastcgi_param REQUEST_METHOD $request_method;
19 | fastcgi_param QUERY_STRING $query_string;
20 | fastcgi_param CONTENT_TYPE $content_type;
21 | fastcgi_param CONTENT_LENGTH $content_length;
22 | }
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pong/python.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | def myapp(environ, start_response):
4 | start_response('200 OK', [('Content-Type', 'text/plain')])
5 | return ['PONG']
6 |
7 | from flup.server.fcgi import WSGIServer
8 | WSGIServer(myapp).run()
9 |
--------------------------------------------------------------------------------
/pong/python.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 |
3 | d=$(readlink -f $(dirname $0))
4 |
5 | spawn-fcgi -p 9000 -n -- $d/python.py &
6 | py=$!
7 |
8 | sleep 1
9 |
10 | sudo /usr/local/nginx/sbin/nginx -c $d/python.nginx.conf &
11 | nx=$!
12 |
13 | sleep 1
14 |
15 | httperf --hog --server=localhost --port=3000 --uri=/ --rate=1000 --num-conns=200 --num-calls=100 --burst-length=20 > results/python
16 | sudo kill $nx
17 | kill $py
18 |
--------------------------------------------------------------------------------
/pong/rack.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'rack'
3 | class RackApp
4 | def call(env)
5 | [200, {} ["PONG"]]
6 | end
7 | end
8 | run RackApp.new
9 |
--------------------------------------------------------------------------------
/pong/rack.ru:
--------------------------------------------------------------------------------
1 | class RackApp
2 | def call(env)
3 | [200, {'Content-Type' => 'text/plain'}, ["PONG"]]
4 | end
5 | end
6 | run RackApp.new
7 |
--------------------------------------------------------------------------------
/pong/ruby_goliath.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'goliath'
3 |
4 | class RubyGoliath < Goliath::API
5 | def response(env)
6 | [200, {}, "PONG"]
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/pong/runall.sh:
--------------------------------------------------------------------------------
1 | rm -rf results
2 | mkdir -p results
3 |
4 | ./snap.sh $*
5 | ./warp.sh $*
6 | ./happstack.sh $*
7 | ./yesod.sh $*
8 | ./elli.sh
9 | sudo ./php.sh
10 | ./tornado.sh
11 | ./node.sh
12 | ./goliath.sh
13 | ./winstone.sh
14 |
--------------------------------------------------------------------------------
/pong/snap.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Main where
3 |
4 | import Control.Applicative
5 | import Snap.Http.Server
6 | import Snap.Http.Server.Config
7 | import Snap.Iteratee
8 | import Snap.Types
9 | import Snap.Util.FileServe
10 |
11 | site :: Snap ()
12 | site = writeBS "PONG"
13 |
14 | main :: IO ()
15 | main =
16 | let config = addListen (ListenHttp "*" 3000) $
17 | setAccessLog Nothing $
18 | setErrorLog Nothing $
19 | defaultConfig
20 | in httpServe config site
21 |
--------------------------------------------------------------------------------
/pong/snap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | ./dist/build/snap/snap $* &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/summary.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | grep 'Request rate' -rn results/ | sed 's@results/\([^:]*\).*rate: \(.*\) req/s.*@\2 \1@' | sort -n | tac | tee results-summary
4 |
--------------------------------------------------------------------------------
/pong/thin.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | rvm use 1.9.2
7 | thin -p 3000 -R rack.ru -e production start &
8 |
9 | benchmark
10 |
11 | killall -KILL thin
12 |
--------------------------------------------------------------------------------
/pong/tornado.py:
--------------------------------------------------------------------------------
1 | #import tornado.ioloop
2 | import tornado.web
3 |
4 | class MainHandler(tornado.web.RequestHandler):
5 | def get(self):
6 | self.write("PONG")
7 |
8 | application = tornado.web.Application([
9 | (r"/", MainHandler),
10 | ])
11 |
12 | if __name__ == "__main__":
13 | application.listen(3000)
14 | tornado.ioloop.IOLoop.instance().start()
15 |
--------------------------------------------------------------------------------
/pong/tornado.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | python tornado.py &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/unicorn.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | rvm use 1.9.2
7 | unicorn -p 3000 -E production rack.ru &
8 |
9 | benchmark
10 |
--------------------------------------------------------------------------------
/pong/warp.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | import Network.Wai
3 | import Network.Wai.Handler.Warp
4 | import Blaze.ByteString.Builder (fromByteString)
5 | import Network.HTTP.Types (status200)
6 |
7 | main = run 3000 $ const $ return $ ResponseBuilder
8 | status200
9 | [("Content-Type", "text/plain"), ("Content-Length", "4")]
10 | $ fromByteString "PONG"
11 |
--------------------------------------------------------------------------------
/pong/warp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | ./dist/build/warp/warp $* &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/winstone-0.9.10.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yesodweb/benchmarks/f49720b9dcc565d7c269adbf4d6b91eeda7c323e/pong/winstone-0.9.10.jar
--------------------------------------------------------------------------------
/pong/winstone.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | java -jar winstone-0.9.10.jar --warfile pong.war --httpPort=3000 &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/pong/yesod.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
2 | import Yesod.Dispatch
3 | import Yesod.Core
4 | import Yesod.Content
5 | import Data.ByteString (ByteString)
6 | import Network.Wai.Handler.Warp (run)
7 |
8 | data Pong = Pong
9 | mkYesod "Pong" [parseRoutes|
10 | / PongR GET
11 | |]
12 |
13 | instance Yesod Pong where
14 | makeSessionBackend _ = return Nothing
15 |
16 | getPongR = return $ RepPlain $ toContent ("PONG" :: ByteString)
17 |
18 | main = toWaiAppPlain Pong >>= run 3000
19 |
--------------------------------------------------------------------------------
/pong/yesod.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | DIR="$( cd "$( dirname "$0" )" && pwd )"
3 | d=$(readlink -f $DIR)
4 | source lib.sh
5 |
6 | ./dist/build/yesod/yesod $* &
7 |
8 | benchmark
9 |
--------------------------------------------------------------------------------
/ruby-install.txt:
--------------------------------------------------------------------------------
1 | # requirements already installed
2 | # build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev
3 |
4 | # install rvm
5 | bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
6 | echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc
7 | echo 'export rvm_pretty_print_flag=1' >> ~/.rvmrc.
8 | source ~/.bashrc
9 |
10 | # requirements to compile ruby1.9
11 | sudo apt-get install -y libssl-dev libreadline5-dev
12 |
13 | rvm install 1.9.2 # ruby-1.9.2-p180 [ x86_64 ]
14 | gem install goliath thin unicorn
15 |
16 | rvm install ree # ree-1.8.7-2011.03 [ x86_64 ]
17 | # rvm install rbx # rbx-head (Sat Mar 12 09:05:08 PST 2011)
18 |
19 | rvm use ree
20 | gem install passenger --no-rdoc --no-ri
21 |
22 | # install libs needed for passenger
23 | sudo apt-get install -y libcurl4-openssl-dev
24 |
25 | # the automated installer (passenger-install-nginx-module)
26 | # had problems finding passenger files as root or installing with permission as user
27 | # this can be fixed by installing ree as the system ruby instead of through rvm
28 | #
29 | # it ends up doing the following steps, recompiling and configuring nginx with passenger module
30 | # note that we are installing to /opt/nginx, giving us a separate nginx install there
31 | cd nginx-0.8.54
32 | sh ./configure --prefix='/opt/nginx' --with-http_ssl_module --add-module='/home/ubuntu/.rvm/gems/ree-1.8.7-2011.03/gems/passenger-3.0.5/ext/nginx'
33 | sudo make install
34 |
35 | # Change config file: /opt/nginx/conf/nginx.conf
36 | # use nginx config as per: http://pastie.org/867442
37 | #
38 | # add to nginx config the output of passenger-config --root
39 | passenger_root /home/ubuntu/.rvm/gems/ree-1.8.7-2011.03/gems/passenger-3.0.5
40 | # change server config
41 | server {
42 | root /home/ubuntu/benchmarks/passenger;
43 | passenger_enabled on;
44 | listen 80;
45 | }
46 |
--------------------------------------------------------------------------------
/setup-python.sh:
--------------------------------------------------------------------------------
1 | wget http://www.saddi.com/software/flup/dist/flup-1.0.2.tar.gz
2 | tar zxfv flup-1.0.2.tar.gz
3 | cd flup-1.0.2/
4 | python setup.py install
5 |
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script is intended to be run on a 64-bit Ubuntu server install or a
4 | # 64-bit Ubuntu EC2 instance. The command I use to create that instance is:
5 |
6 | # ec2-run-instances ami-cef405a7 -k ec2-keypair -t c1.xlarge
7 |
8 | sudo apt-get update
9 |
10 | sudo apt-get install -y build-essential libgmp3-dev zlib1g-dev git-core curl httperf libbsd-dev libpcre3-dev openssl libssl-dev libxml2-dev python-setuptools python-pycurl openjdk-6-jre-headless libncurses5-dev
11 |
12 | # Haskell
13 |
14 | ## GHC
15 | cd ~
16 | wget http://haskell.org/ghc/dist/7.0.2/ghc-7.0.2-x86_64-unknown-linux.tar.bz2
17 | tar jxfv ghc-7.0.2-x86_64-unknown-linux.tar.bz2
18 | cd ghc-7.0.2
19 | ./configure
20 | sudo make install
21 |
22 | ## cabal
23 | cd ~
24 | wget http://hackage.haskell.org/packages/archive/cabal-install/0.10.2/cabal-install-0.10.2.tar.gz
25 | tar zxfv cabal-install-0.10.2.tar.gz
26 | cd cabal-install-0.10.2
27 | sh bootstrap.sh
28 |
29 | # nginx (for PHP)
30 | cd ~
31 | wget http://sysoev.ru/nginx/nginx-0.8.54.tar.gz
32 | tar zxfv nginx-0.8.54.tar.gz
33 | cd nginx-0.8.54
34 | ./configure
35 | make
36 | sudo make install
37 |
38 | # PHP
39 | cd ~
40 | wget http://www.php.net/get/php-5.3.5.tar.bz2/from/de.php.net/mirror
41 | mv mirror php-5.3.5.tar.bz2
42 | tar jxfv php-5.3.5.tar.bz2
43 | cd php-5.3.5
44 | ./configure --enable-fpm
45 | make all
46 | sudo make install
47 | sudo sudo groupadd nobody
48 |
49 | # Ruby
50 |
51 | ## RVM
52 | bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
53 | echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc
54 | source ~/.rvm/scripts/rvm
55 | rvm install 1.9.2
56 | rvm use 1.9.2
57 |
58 | ## Goliath
59 | gem install goliath
60 |
61 | # Python
62 |
63 | ## Tornado
64 | cd ~
65 | wget http://github.com/downloads/facebook/tornado/tornado-1.2.1.tar.gz --no-check-certificate
66 | tar zxfv tornado-1.2.1.tar.gz
67 | cd tornado-1.2.1
68 | sudo python setup.py install
69 |
70 | # node.js
71 | cd ~
72 | wget http://nodejs.org/dist/node-v0.4.2.tar.gz
73 | tar zxfv node-v0.4.2.tar.gz
74 | cd node-v0.4.2
75 | ./configure --without-ssl
76 | make
77 | sudo make install
78 |
79 | # libev for Snap
80 | cd ~
81 | wget http://dist.schmorp.de/libev/libev-4.04.tar.gz
82 | tar zxfv libev-4.04.tar.gz
83 | cd libev-4.04
84 | ./configure
85 | make
86 | sudo make install
87 | sudo ldconfig
88 |
89 | # Erlang/OTP
90 | cd ~
91 | wget http://www.erlang.org/download/otp_src_R15B02.tar.gz
92 | tar xzf otp_src_R15B02.tar.gz
93 | cd otp_src_R15B02
94 | ./configure && make && sudo make install
95 |
96 |
97 | # Run the benchmarks themselves
98 |
99 | ## Fetch benchmarks repo
100 | cd ~
101 | git clone https://github.com/yesodweb/benchmarks.git
102 |
103 | ## Build Erlang code
104 | cd ~/benchmarks/pong/erlang
105 | ./rebar get-deps compile generate -f
106 |
107 | ## Compile Haskell code
108 | cd ~/benchmarks/pong
109 | ~/.cabal/bin/cabal update
110 | ~/.cabal/bin/cabal install snap-server -flibev
111 | ~/.cabal/bin/cabal install
112 |
113 | ## Link in tornado
114 | ln -s ~/tornado-1.2.1/tornado
115 |
116 | ## Run all benchmarks
117 | ./runall.sh +RTS -A4M -N3
118 | ./summary.sh
119 | grep 'Request rate' -rn results/ | sed 's@results/\([^:]*\).*rate: \(.*\) req/s.*@\1 \2@' > results-summary
120 |
--------------------------------------------------------------------------------
/static-file/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c)2010, Michael Snoyman
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Michael Snoyman nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/static-file/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/static-file/bench:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | httperf --hog --num-conns 1000 --num-calls 1000 --burst-length 20 --port 3000 --rate 1000 --server localhost --uri /static-file.txt
4 |
--------------------------------------------------------------------------------
/static-file/lighttpd.conf:
--------------------------------------------------------------------------------
1 | # Run with lighttpd -D -f lighttpd.conf
2 | server.port = 3000
3 | server.document-root = "."
4 | server.modules = ()
5 |
6 | mimetype.assign = (".txt" => "text/plain")
7 |
--------------------------------------------------------------------------------
/static-file/nginx.conf:
--------------------------------------------------------------------------------
1 | daemon off;
2 | events {
3 | worker_connections 4096;
4 | }
5 | http {
6 | server {
7 | listen 3000;
8 | root /home/snoyman/haskell/benchmarks/static-file;
9 | index static-file.txt;
10 | }
11 | types {
12 | text/plain txt;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/static-file/snap.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Main where
3 |
4 | import Control.Applicative
5 | import Snap.Http.Server
6 | import Snap.Http.Server.Config
7 | import Snap.Iteratee
8 | import Snap.Types
9 | import Snap.Util.FileServe
10 |
11 | site :: Snap ()
12 | site = fileServeSingle' "text/plain" "static-file.txt"
13 |
14 | main :: IO ()
15 | main =
16 | let config = addListen (ListenHttp "*" 3000) $
17 | setAccessLog Nothing $
18 | setErrorLog Nothing $
19 | defaultConfig
20 | in httpServe config site
21 |
--------------------------------------------------------------------------------
/static-file/static-file.cabal:
--------------------------------------------------------------------------------
1 | Name: static-file
2 | Version: 0.1
3 | Synopsis: Static file benchmark
4 | License: BSD3
5 | License-file: LICENSE
6 | Author: Michael Snoyman
7 | Maintainer: michael@snoyman.com
8 | Category: Web
9 | Build-type: Simple
10 |
11 | Cabal-version: >=1.2
12 |
13 |
14 | Executable warp
15 | Main-is: warp.hs
16 | Build-depends: base > 4, wai, warp, blaze-builder
17 | Ghc-options: -O2 -threaded -rtsopts
18 |
19 | Executable snap
20 | Main-is: snap.hs
21 | Build-depends: base > 4, snap-server >= 0.3 && < 0.4, snap-core
22 | Ghc-options: -O2 -threaded -rtsopts
23 |
24 | Executable yesod
25 | Main-is: yesod.hs
26 | Build-depends: base > 4, yesod-core >= 0.7 && < 0.8, bytestring
27 | Ghc-options: -O2 -threaded -rtsopts
28 |
--------------------------------------------------------------------------------
/static-file/static-file.txt:
--------------------------------------------------------------------------------
1 | This is a static file.
2 |
--------------------------------------------------------------------------------
/static-file/warp.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | import Network.Wai
3 | import Network.Wai.Handler.Warp
4 | import Blaze.ByteString.Builder (fromByteString)
5 | import Network.HTTP.Types (status200)
6 |
7 | main = run 3000 $ const $ return $ ResponseFile
8 | status200
9 | [("Content-Type", "text/plain"), ("Content-Length", "23")]
10 | "static-file.txt"
11 | Nothing
12 |
--------------------------------------------------------------------------------
/static-file/yesod.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, QuasiQuotes, TypeFamilies, CPP #-}
2 | import Yesod.Dispatch
3 | import Yesod.Core
4 | import Yesod.Content
5 | import Yesod.Handler
6 | import Data.ByteString (ByteString)
7 | import Network.Wai.Handler.Warp (run)
8 |
9 | data Pong = Pong
10 | mkYesod "Pong"
11 | #if __GLASGOW_HASKELL__ >= 700
12 | [parseRoutes|
13 | #else
14 | [$parseRoutes|
15 | #endif
16 | /static-file.txt PongR GET
17 | |]
18 | instance Yesod Pong where
19 | approot _ = ""
20 | encryptKey _ = return Nothing
21 | getPongR = sendFile "text/plain" "static-file.txt" >> return ()
22 |
23 | main = toWaiAppPlain Pong >>= run 3000
24 |
--------------------------------------------------------------------------------