├── .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("\n") << 7 | "
" 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("\n") << 7 | "
" 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 | --------------------------------------------------------------------------------