Browse Source

Merge branch 'master' of https://github.com/rayrapetyan/FrameworkBenchmarks

Robert Ayrapetyan 9 years ago
parent
commit
d0949e9636
63 changed files with 987 additions and 91 deletions
  1. 4 1
      .travis.yml
  2. 13 25
      frameworks/CSharp/nancy/NancyModules/DbModule.cs
  3. 2 0
      frameworks/CSharp/nancy/NancyModules/NancyModules.csproj
  4. 15 0
      frameworks/CSharp/nancy/NancyModules/PlainModule.cs
  5. 91 0
      frameworks/CSharp/nancy/NancyModules/QueryModule.cs
  6. 5 22
      frameworks/CSharp/nancy/benchmark_config.json
  7. 2 2
      frameworks/CSharp/nancy/nginx.conf
  8. 7 0
      frameworks/CSharp/nancy/setup_nginx.sh
  9. 7 0
      frameworks/Erlang/misultin/.gitignore
  10. 13 0
      frameworks/Erlang/misultin/Makefile
  11. 23 0
      frameworks/Erlang/misultin/benchmark_config.json
  12. 9 0
      frameworks/Erlang/misultin/priv/app.config
  13. 5 0
      frameworks/Erlang/misultin/rebar.config
  14. 11 0
      frameworks/Erlang/misultin/setup.sh
  15. 9 0
      frameworks/Erlang/misultin/src/misultin_bench.app.src
  16. 16 0
      frameworks/Erlang/misultin/src/misultin_bench_app.erl
  17. 18 0
      frameworks/Erlang/misultin/src/misultin_bench_sup.erl
  18. 53 0
      frameworks/Erlang/misultin/src/web_handler.erl
  19. 7 0
      frameworks/Erlang/misultin/start-dev.sh
  20. 7 0
      frameworks/Erlang/mochiweb/.gitignore
  21. 13 0
      frameworks/Erlang/mochiweb/Makefile
  22. 23 0
      frameworks/Erlang/mochiweb/benchmark_config.json
  23. 9 0
      frameworks/Erlang/mochiweb/priv/app.config
  24. 5 0
      frameworks/Erlang/mochiweb/rebar.config
  25. 11 0
      frameworks/Erlang/mochiweb/setup.sh
  26. 9 0
      frameworks/Erlang/mochiweb/src/mochiweb_bench.app.src
  27. 14 0
      frameworks/Erlang/mochiweb/src/mochiweb_bench_app.erl
  28. 18 0
      frameworks/Erlang/mochiweb/src/mochiweb_bench_sup.erl
  29. 59 0
      frameworks/Erlang/mochiweb/src/web_handler.erl
  30. 0 0
      frameworks/Go/fasthttp-mysql/README.md
  31. 3 3
      frameworks/Go/fasthttp-mysql/benchmark_config.json
  32. 2 0
      frameworks/Go/fasthttp-mysql/setup.bat
  33. 0 0
      frameworks/Go/fasthttp-mysql/setup.sh
  34. 0 0
      frameworks/Go/fasthttp-mysql/setup_prefork.sh
  35. 0 0
      frameworks/Go/fasthttp-mysql/source_code
  36. 0 0
      frameworks/Go/fasthttp-mysql/src/hello/hello.go
  37. 0 0
      frameworks/Go/fasthttp-mysql/templates/fortune.html
  38. 0 0
      frameworks/Go/fasthttp-mysql/templates/layout.html
  39. 16 0
      frameworks/Go/fasthttp-postgresql/README.md
  40. 51 0
      frameworks/Go/fasthttp-postgresql/benchmark_config.json
  41. 2 0
      frameworks/Go/fasthttp-postgresql/setup.bat
  42. 10 0
      frameworks/Go/fasthttp-postgresql/setup.sh
  43. 10 0
      frameworks/Go/fasthttp-postgresql/setup_prefork.sh
  44. 6 0
      frameworks/Go/fasthttp-postgresql/source_code
  45. 321 0
      frameworks/Go/fasthttp-postgresql/src/hello/hello.go
  46. 14 0
      frameworks/Go/fasthttp-postgresql/templates/fortune.html
  47. 9 0
      frameworks/Go/fasthttp-postgresql/templates/layout.html
  48. 0 2
      frameworks/Go/fasthttp/setup.bat
  49. 1 1
      frameworks/Python/asyncio/aiohttp.web/setup.sh
  50. 1 1
      frameworks/Python/asyncio/yocto_http/setup.sh
  51. 1 1
      frameworks/Python/bottle/setup_py3.sh
  52. 1 1
      frameworks/Python/cherrypy/setup_py3.sh
  53. 1 1
      frameworks/Python/falcon/setup_py3.sh
  54. 1 1
      frameworks/Python/flask/app.py
  55. 2 2
      frameworks/Python/flask/requirements.txt
  56. 1 1
      frameworks/Python/flask/setup_py3.sh
  57. 1 1
      frameworks/Python/pyramid/setup_py3.sh
  58. 1 1
      frameworks/Python/tornado/setup_py3.sh
  59. 1 1
      frameworks/Python/wheezyweb/setup_py3.sh
  60. 36 13
      toolset/benchmark/test_types/verifications.py
  61. 13 7
      toolset/setup/linux/languages/mono.sh
  62. 3 3
      toolset/setup/linux/languages/python3.sh
  63. 1 1
      toolset/setup/linux/languages/xsp.sh

+ 4 - 1
.travis.yml

@@ -50,9 +50,12 @@ env:
     - "TESTDIR=Erlang/chicagoboss"
     - "TESTDIR=Erlang/cowboy"
     - "TESTDIR=Erlang/elli"
+    - "TESTDIR=Erlang/mochiweb"
+    - "TESTDIR=Erlang/misultin"
     - "TESTDIR=Go/beego"
     - "TESTDIR=Go/falcore"
-    - "TESTDIR=Go/fasthttp"
+    - "TESTDIR=Go/fasthttp-mysql"
+    - "TESTDIR=Go/fasthttp-postgresql"
     - "TESTDIR=Go/gin"
     - "TESTDIR=Go/go-raw"
     - "TESTDIR=Go/goji"

+ 13 - 25
frameworks/CSharp/nancy/NancyModules/DbModule.cs

@@ -8,6 +8,7 @@
     using MySql.Data.MySqlClient;
     using Nancy;
 
+    
     public class DbModule : NancyModule
     {
         public static string MYSQL_CONNECTION_STRING;
@@ -16,35 +17,22 @@
         {
             MYSQL_CONNECTION_STRING = ConfigurationManager.AppSettings["ConnectionString.MySQL"];
         }
-
+        
+        /**
+         * NOTE:
+         * Return a single World objects as JSON, selected randomly from the World
+         * table.  Assume the table has 10,000 rows.
+         */
         public DbModule() : base("/db")
         {
             Get["/{queries?1}"] = paramz =>
             {
-                var queries = (int)paramz.queries;
-                
-                var random = new Random();
-                using (var db = new MySqlConnection(MYSQL_CONNECTION_STRING))
-                {
-                    db.Open();
-
-                    if (queries == 1)
-                        return GetRandomWorld(db, random);
-                    else
-                    {
-                        var worldCount = queries > 500 ? 500 : queries;
-                        worldCount = worldCount < 1 ? 1 : worldCount;
-
-                        // NOTE: Experiment with running the DB requests in parallel, on both Mono and Windows CLRs.
-                        var worlds = new World[worldCount];
-
-                        for (int i = 0; i < worldCount; ++i)
-                        {
-                            worlds[i] = GetRandomWorld(db, random);
-                        }
-                        return worlds;
-                    }
-                }
+              var random = new Random();
+              using (var db = new MySqlConnection(MYSQL_CONNECTION_STRING))
+              {
+                db.Open();
+                return Response.AsJson(GetRandomWorld(db, random));
+              }
             };
         }
 

+ 2 - 0
frameworks/CSharp/nancy/NancyModules/NancyModules.csproj

@@ -52,8 +52,10 @@
     <Reference Include="System.Data" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="PlainModule.cs" />
     <Compile Include="DbModule.cs" />
     <Compile Include="JsonModule.cs" />
+    <Compile Include="QueryModule.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>

+ 15 - 0
frameworks/CSharp/nancy/NancyModules/PlainModule.cs

@@ -0,0 +1,15 @@
+namespace NancyModules 
+{
+  using Nancy;
+
+  public class PlainModule : NancyModule
+  {
+    public PlainModule() : base("plaintext")
+    {
+      Get["/"] = x =>
+      {
+        return "Hello, World!";
+      };
+    }
+  }
+}

+ 91 - 0
frameworks/CSharp/nancy/NancyModules/QueryModule.cs

@@ -0,0 +1,91 @@
+namespace NancyModules
+{
+    using System;
+    using System.Configuration;
+    using System.Data;
+    using System.Linq;
+    using Dapper;
+    using MySql.Data.MySqlClient;
+    using Nancy;
+
+    public class QueryModule : NancyModule
+    {
+        public static string MYSQL_CONNECTION_STRING;
+        
+        static QueryModule()
+        {
+            MYSQL_CONNECTION_STRING = ConfigurationManager.AppSettings["ConnectionString.MySQL"];
+        }
+        /**
+         * Query:
+         * Return a list of World objects as JSON, selected randomly from the World
+         * table.  Assume the table has 10,000 rows.
+         */
+        public QueryModule() : base("/query")
+        {
+            Get["/{queries?1}"] = paramz =>
+            {
+                var queries = (int)paramz.queries;
+                
+                var random = new Random();
+                using (var db = new MySqlConnection(MYSQL_CONNECTION_STRING))
+                {
+                    db.Open();
+
+                    if (queries == 1)
+                        return Response.AsJson( GetRandomWorld(db, random) );
+                    else
+                    {
+                        var worldCount = queries > 500 ? 500 : queries;
+                        worldCount = worldCount < 1 ? 1 : worldCount;
+
+                        // NOTE: Experiment with running the DB requests in parallel, on both Mono and Windows CLRs.
+                        var worlds = new World[worldCount];
+
+                        for (int i = 0; i < worldCount; ++i)
+                        {
+                            worlds[i] = GetRandomWorld(db, random);
+                        }
+                        return Response.AsJson( worlds );
+                    }
+                }
+            };
+
+            Get["/{name}"] = paramz =>
+            {
+                 var queries = (int)paramz.queries;
+                
+                var random = new Random();
+                using (var db = new MySqlConnection(MYSQL_CONNECTION_STRING))
+                {
+                    db.Open();
+
+                    if (queries == 1)
+                        return Response.AsJson( GetRandomWorld(db, random) );
+                    else
+                    {
+                        var worldCount = queries > 500 ? 500 : queries;
+                        worldCount = worldCount < 1 ? 1 : worldCount;
+
+                        // NOTE: Experiment with running the DB requests in parallel, on both Mono and Windows CLRs.
+                        var worlds = new World[worldCount];
+
+                        for (int i = 0; i < worldCount; ++i)
+                        {
+                            worlds[i] = GetRandomWorld(db, random);
+                        }
+                        return Response.AsJson( worlds );
+                    }
+                }
+
+            };
+        }
+
+        private World GetRandomWorld(IDbConnection db, Random random)
+        {
+            var id = random.Next(1, 10001);
+            return db.Query<World>("SELECT id, randomNumber FROM world WHERE id = @id", new { id = id }).Single();
+        }
+    }
+
+}

+ 5 - 22
frameworks/CSharp/nancy/benchmark_config.json

@@ -3,9 +3,10 @@
   "tests": [{
     "default": {
       "setup_file": "setup_iis",
+      "plaintext_url":"/plaintext",
       "json_url": "/json",
       "db_url": "/db",
-      "query_url": "/db/",
+      "query_url": "/query/",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -23,9 +24,10 @@
     },
     "mono": {
       "setup_file": "setup_nginx",
+      "plaintext_url":"/plaintext",
       "json_url": "/json",
       "db_url": "/db",
-      "query_url": "/db/",
+      "query_url": "/query/",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -40,26 +42,7 @@
       "display_name": "nancy",
       "notes": "",
       "versus": ""
-    },
-    "libevent2": {
-      "setup_file": "setup_libevent",
-      "json_url": "/json",
-      "db_url": "/db",
-      "query_url": "/db/",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "nancy",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": "Mono",
-      "webserver": "nginx",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "nancy-libevent",
-      "notes": "",
-      "versus": ""
     }
+
   }]
 }

+ 2 - 2
frameworks/CSharp/nancy/nginx.conf

@@ -19,8 +19,8 @@ http {
 
         location / {
             fastcgi_pass mono;
-            include /usr/local/nginx/conf/fastcgi_params;
+            include nginx.osenv.conf; # read os env from this file
             fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
         }
     }
-}
+}

+ 7 - 0
frameworks/CSharp/nancy/setup_nginx.sh

@@ -19,6 +19,13 @@ for port in $(seq ${port_start} $port_end); do
 done
 conf+="}"
 
+# Store path of fastcgi_params dynamically in a file called "nginx.osenv.conf". 
+# The reason why I do this is Ngix "include" cannot recognize variables. (Well known issue of Nginx)
+# To use OS Environment at ngix.conf 3rd party library or perl module is needed
+# Current approach is one trick to solve the problem without utilize those 3rd party libraries.
+echo "include $IROOT/nginx/conf/fastcgi_params;" > $TROOT/nginx.osenv.conf
+
+
 echo -e $conf > $TROOT/nginx.upstream.conf
 ${NGINX_HOME}/sbin/nginx -c $TROOT/nginx.conf -g "worker_processes '"${MAX_THREADS}"';"
 

+ 7 - 0
frameworks/Erlang/misultin/.gitignore

@@ -0,0 +1,7 @@
+.eunit
+deps
+ebin
+*.o
+*.beam
+*.plt
+erl_crash.dump

+ 13 - 0
frameworks/Erlang/misultin/Makefile

@@ -0,0 +1,13 @@
+REBAR=./rebar
+
+all:
+	@$(REBAR) -r get-deps compile
+
+clean:
+	@$(REBAR) clean
+
+run:
+	@./start.sh
+
+stop:
+	@killall beam.smp

+ 23 - 0
frameworks/Erlang/misultin/benchmark_config.json

@@ -0,0 +1,23 @@
+{
+  "framework": "misultin",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "Misultin",
+      "language": "Erlang",
+      "orm": "Raw",
+      "platform": "Erlang",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "misultin",
+      "notes": "",
+      "versus": ""
+  }}]
+}

+ 9 - 0
frameworks/Erlang/misultin/priv/app.config

@@ -0,0 +1,9 @@
+%% -*- erlang -*-
+[{misultin_bench, [{http_port, 8080}]},
+
+ {erl_bench, [{db_pool, 256},
+              {db_user, "benchmarkdbuser"},
+              {db_password, "benchmarkdbpass"},
+              {db_host, "localhost"},
+              {db_port, 3306},
+              {db_name, "hello_world"}]}].

+ 5 - 0
frameworks/Erlang/misultin/rebar.config

@@ -0,0 +1,5 @@
+%% -*- erlang -*-
+{deps, [
+  {erl_bench, "0.0.1", {git, "git://github.com/b0oh/erl_bench.git", {tag, "v0.0.1"}}},
+  {misultin, "0.9", {git, "git://github.com/ostinelli/misultin.git", {tag, "misultin-0.9"}}}
+]}.

+ 11 - 0
frameworks/Erlang/misultin/setup.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+sed -i 's|"benchmarkdbpass", ".*", 3306|"benchmarkdbpass", "'"${DBHOST}"'", 3306|g' src/misultin_bench_sup.erl
+
+fw_depends erlang
+
+rm -rf deps/* ebin/*
+rebar get-deps
+rebar compile
+
+erl +K true +sbwt very_long +swt very_low -pa ebin deps/*/ebin -boot start_sasl -config priv/app.config -s misultin_bench_app -noshell -detached

+ 9 - 0
frameworks/Erlang/misultin/src/misultin_bench.app.src

@@ -0,0 +1,9 @@
+%% -*- erlang -*-
+{application, misultin_bench,
+ [{description, ""},
+  {vsn, "0.1"},
+  {modules, []},
+  {registered, []},
+  {mod, {misultin_bench_app, []}},
+  {env, []},
+  {applications, [kernel, stdlib, crypto, erl_bench]}]}.

+ 16 - 0
frameworks/Erlang/misultin/src/misultin_bench_app.erl

@@ -0,0 +1,16 @@
+-module(misultin_bench_app).
+-behaviour(application).
+-export([start/0]).
+-export([start/2, stop/1]).
+
+start() ->
+    application:ensure_all_started(misultin_bench).
+
+start(_Type, _StartArgs) ->
+    {ok, Port} = application:get_env(misultin_bench, http_port),
+    Options = [{loop, fun(Req) -> web_handler:dispatch(Req) end},
+               {port, Port}],
+    misultin_bench_sup:start_link(Options).
+
+stop(_State) ->
+    ok.

+ 18 - 0
frameworks/Erlang/misultin/src/misultin_bench_sup.erl

@@ -0,0 +1,18 @@
+-module(misultin_bench_sup).
+-behaviour(supervisor).
+-export([start_link/1]).
+-export([init/1]).
+
+start_link(Options) ->
+    supervisor:start_link(?MODULE, [Options]).
+
+init([Options]) ->
+    Misultin = supervisor(misultin, Options),
+    Processes = [Misultin],
+    Strategy = {one_for_one, 5, 30},
+    {ok, {Strategy, Processes}}.
+
+supervisor(Mod, Options) ->
+    {Mod,
+     {Mod, start_link, [Options]},
+     permanent, infinity, supervisor, [Mod]}.

+ 53 - 0
frameworks/Erlang/misultin/src/web_handler.erl

@@ -0,0 +1,53 @@
+-module(web_handler).
+-export([dispatch/1]).
+
+dispatch(Req) ->
+    Method = Req:get(method),
+    {_UriType, Uri} = Req:get(uri),
+    Path = string:tokens(Uri, "/"),
+    handle(Method, Path, Req).
+
+%% handle
+
+handle('GET', ["json"], Req) ->
+    json(Req, erl_bench:hello_json());
+
+handle('GET', ["plaintext"], Req) ->
+    plain(Req, erl_bench:hello_plain());
+
+handle('GET', ["db"], Req) ->
+    json(Req, erl_bench:random_json());
+
+handle('GET', ["queries"], Req) ->
+    Queries = queries(Req),
+    json(Req, erl_bench:randoms_json(Queries));
+
+handle('GET', ["updates"], Req) ->
+    Queries = queries(Req),
+    json(Req, erl_bench:update_randoms_json(Queries));
+
+handle('GET', ["fortunes"], Req) ->
+    html(Req, erl_bench:fortunes_html());
+
+handle(_Method, _Path, Req) ->
+    Req:respond(404, [{"Content-Type", "text/plain"}], "Not Found").
+
+%% private
+
+json(Req, Json) ->
+    Req:ok([{"Content-Type", "application/json"}], Json).
+
+plain(Req, Text) ->
+    Req:ok([{"Content-Type", "text/plain"}], Text).
+
+html(Req, Html) ->
+    Req:ok([{"Content-Type", "text/html"}], Html).
+
+queries(Req) ->
+    Params = Req:parse_qs(),
+    Queries = (catch list_to_integer(proplists:get_value("queries", Params, "1"))),
+    case {is_number(Queries), Queries > 500} of
+        {true, true} -> 500;
+        {false, _}   -> 1;
+        _ -> Queries
+    end.

+ 7 - 0
frameworks/Erlang/misultin/start-dev.sh

@@ -0,0 +1,7 @@
+#!/bin/sh
+exec erl \
+     +K true +sbwt very_long +swt very_low \
+     -pa ebin deps/*/ebin \
+     -boot start_sasl \
+     -config priv/app.config \
+     -s misultin_bench_app

+ 7 - 0
frameworks/Erlang/mochiweb/.gitignore

@@ -0,0 +1,7 @@
+.eunit
+deps
+ebin
+*.o
+*.beam
+*.plt
+erl_crash.dump

+ 13 - 0
frameworks/Erlang/mochiweb/Makefile

@@ -0,0 +1,13 @@
+REBAR=./rebar
+
+all:
+	@$(REBAR) -r get-deps compile
+
+clean:
+	@$(REBAR) clean
+
+run:
+	@./start.sh
+
+stop:
+	@killall beam.smp

+ 23 - 0
frameworks/Erlang/mochiweb/benchmark_config.json

@@ -0,0 +1,23 @@
+{
+  "framework": "mochiweb",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "Mochiweb",
+      "language": "Erlang",
+      "orm": "Raw",
+      "platform": "Erlang",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "mochiweb",
+      "notes": "",
+      "versus": ""
+  }}]
+}

+ 9 - 0
frameworks/Erlang/mochiweb/priv/app.config

@@ -0,0 +1,9 @@
+%% -*- erlang -*-
+[{mochiweb_bench, [{http_port, 8080}]},
+
+ {erl_bench, [{db_pool, 256},
+              {db_user, "benchmarkdbuser"},
+              {db_password, "benchmarkdbpass"},
+              {db_host, "localhost"},
+              {db_port, 3306},
+              {db_name, "hello_world"}]}].

+ 5 - 0
frameworks/Erlang/mochiweb/rebar.config

@@ -0,0 +1,5 @@
+%% -*- erlang -*-
+{deps, [
+  {erl_bench, "0.0.1", {git, "git://github.com/b0oh/erl_bench.git", {tag, "v0.0.1"}}},
+  {mochiweb, "2.9.0", {git, "git://github.com/mochi/mochiweb.git", {tag, "v2.9.0"}}}
+]}.

+ 11 - 0
frameworks/Erlang/mochiweb/setup.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+sed -i 's|"benchmarkdbpass", ".*", 3306|"benchmarkdbpass", "'"${DBHOST}"'", 3306|g' src/mochiweb_bench_sup.erl
+
+fw_depends erlang
+
+rm -rf deps/* ebin/*
+rebar get-deps
+rebar compile
+
+erl +K true +sbwt very_long +swt very_low -pa ebin deps/*/ebin -boot start_sasl -config priv/app.config -s mochiweb_bench_app

+ 9 - 0
frameworks/Erlang/mochiweb/src/mochiweb_bench.app.src

@@ -0,0 +1,9 @@
+%% -*- erlang -*-
+{application, mochiweb_bench,
+ [{description, ""},
+  {vsn, "0.1"},
+  {modules, []},
+  {registered, []},
+  {mod, {mochiweb_bench_app, []}},
+  {env, []},
+  {applications, [kernel, stdlib, erl_bench]}]}.

+ 14 - 0
frameworks/Erlang/mochiweb/src/mochiweb_bench_app.erl

@@ -0,0 +1,14 @@
+-module(mochiweb_bench_app).
+-behaviour(application).
+-export([start/0]).
+-export([start/2, stop/1]).
+
+start() ->
+    application:ensure_all_started(mochiweb_bench).
+
+start(_Type, _StartArgs) ->
+    {ok, Port} = application:get_env(mochiweb_bench, http_port),
+    mochiweb_bench_sup:start_link([{port, Port}]).
+
+stop(_State) ->
+    ok.

+ 18 - 0
frameworks/Erlang/mochiweb/src/mochiweb_bench_sup.erl

@@ -0,0 +1,18 @@
+-module(mochiweb_bench_sup).
+-behaviour(supervisor).
+-export([start_link/1]).
+-export([init/1]).
+
+start_link(Options) ->
+    supervisor:start_link({local, ?MODULE}, ?MODULE, [Options]).
+
+init([Options]) ->
+    Web = worker(web_handler, Options),
+    Processes = [Web],
+    Strategy = {one_for_one, 10, 10},
+    {ok, {Strategy, Processes}}.
+
+worker(Mod, Options) ->
+    {Mod,
+     {Mod, start, [Options]},
+     permanent, 5000, worker, dynamic}.

+ 59 - 0
frameworks/Erlang/mochiweb/src/web_handler.erl

@@ -0,0 +1,59 @@
+-module(web_handler).
+-export([start/1, stop/0, dispatch/1]).
+
+start(Options) ->
+    mochiweb_http:start([{name, ?MODULE},
+                         {loop, {?MODULE, dispatch}} | Options]).
+
+stop() ->
+    mochiweb_http:stop(?MODULE).
+
+dispatch(Req) ->
+    Method = Req:get(method),
+    Path = string:tokens(Req:get(path), "/"),
+    handle(Method, Path, Req).
+
+%% handle
+
+handle('GET', ["json"], Req) ->
+    json(Req, erl_bench:hello_json());
+
+handle('GET', ["plaintext"], Req) ->
+    plain(Req, erl_bench:hello_plain());
+
+handle('GET', ["db"], Req) ->
+    json(Req, erl_bench:random_json());
+
+handle('GET', ["queries"], Req) ->
+    Queries = queries(Req),
+    json(Req, erl_bench:randoms_json(Queries));
+
+handle('GET', ["updates"], Req) ->
+    Queries = queries(Req),
+    json(Req, erl_bench:update_randoms_json(Queries));
+
+handle('GET', ["fortunes"], Req) ->
+    html(Req, erl_bench:fortunes_html());
+
+handle(_Method, _Path, Req) ->
+    Req:respond({404, [{"Content-Type", "text/plain"}], "Not Found"}).
+
+%% private
+
+json(Req, Json) ->
+    Req:ok({"application/json", Json}).
+
+plain(Req, Text) ->
+    Req:ok({"text/plain", Text}).
+
+html(Req, Html) ->
+    Req:ok({"text/html", Html}).
+
+queries(Req) ->
+    Params = Req:parse_qs(),
+    Queries = (catch list_to_integer(proplists:get_value("queries", Params, "1"))),
+    case {is_number(Queries), Queries > 500} of
+        {true, true} -> 500;
+        {false, _}   -> 1;
+        _ -> Queries
+    end.

+ 0 - 0
frameworks/Go/fasthttp/README.md → frameworks/Go/fasthttp-mysql/README.md


+ 3 - 3
frameworks/Go/fasthttp/benchmark_config.json → frameworks/Go/fasthttp-mysql/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "fasthttp",
+  "framework": "fasthttp-mysql",
   "tests": [{
     "default": {
       "setup_file": "setup",
@@ -20,7 +20,7 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "fasthttp",
+      "display_name": "fasthttp-mysql",
       "notes": "",
       "versus": "go"
     },
@@ -43,7 +43,7 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "fasthttp-prefork",
+      "display_name": "fasthttp-mysql-prefork",
       "notes": "",
       "versus": "go"
     }

+ 2 - 0
frameworks/Go/fasthttp-mysql/setup.bat

@@ -0,0 +1,2 @@
+set GOPATH=C:\FrameworkBenchmarks\Go\fasthttp-mysql
+go run src\hello\hello.go

+ 0 - 0
frameworks/Go/fasthttp/setup.sh → frameworks/Go/fasthttp-mysql/setup.sh


+ 0 - 0
frameworks/Go/fasthttp/setup_prefork.sh → frameworks/Go/fasthttp-mysql/setup_prefork.sh


+ 0 - 0
frameworks/Go/fasthttp/source_code → frameworks/Go/fasthttp-mysql/source_code


+ 0 - 0
frameworks/Go/fasthttp/src/hello/hello.go → frameworks/Go/fasthttp-mysql/src/hello/hello.go


+ 0 - 0
frameworks/Go/fasthttp/templates/fortune.html → frameworks/Go/fasthttp-mysql/templates/fortune.html


+ 0 - 0
frameworks/Go/fasthttp/templates/layout.html → frameworks/Go/fasthttp-mysql/templates/layout.html


+ 16 - 0
frameworks/Go/fasthttp-postgresql/README.md

@@ -0,0 +1,16 @@
+# [fasthttp](https://github.com/valyala/fasthttp) (GoLang) Benchmarking Test
+
+This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
+
+"Fasthttp is a fast http package for Go."
+
+# This variant uses Postgres via Jack Christensen's pgx library
+
+## Test URLs
+
+    http://localhost:8080/json
+    http://localhost:8080/db
+    http://localhost:8080/queries?queries=[1-500]
+    http://localhost:8080/fortunes
+    http://localhost:8080/updates?queries=[1-500]
+    http://localhost:8080/plaintext

+ 51 - 0
frameworks/Go/fasthttp-postgresql/benchmark_config.json

@@ -0,0 +1,51 @@
+{
+  "framework": "fasthttp-postgresql",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "fasthttp",
+      "language": "Go",
+      "orm": "Raw",
+      "platform": "Go",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "fasthttp-postgresql",
+      "notes": "",
+      "versus": "go"
+    },
+    "prefork": {
+      "setup_file": "setup_prefork",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "fasthttp",
+      "language": "Go",
+      "orm": "Raw",
+      "platform": "Go",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "fasthttp-pg-prefork",
+      "notes": "",
+      "versus": "go"
+    }
+  }]
+}

+ 2 - 0
frameworks/Go/fasthttp-postgresql/setup.bat

@@ -0,0 +1,2 @@
+set GOPATH=C:\FrameworkBenchmarks\Go\fasthttp-postgresql
+go run src\hello\hello.go

+ 10 - 0
frameworks/Go/fasthttp-postgresql/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sed -i 's|localhost|'"${DBHOST}"'|g' src/hello/hello.go
+
+fw_depends go
+
+go get -u github.com/jackc/pgx
+go get -u github.com/valyala/fasthttp
+
+go run src/hello/hello.go &

+ 10 - 0
frameworks/Go/fasthttp-postgresql/setup_prefork.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sed -i 's|localhost|'"${DBHOST}"'|g' src/hello/hello.go
+
+fw_depends go
+
+go get -u github.com/jackc/pgx
+go get -u github.com/valyala/fasthttp
+
+go run src/hello/hello.go -prefork &

+ 6 - 0
frameworks/Go/fasthttp-postgresql/source_code

@@ -0,0 +1,6 @@
+./go/src/
+./go/src/hello
+./go/src/hello/hello.go
+./go/templates/
+./go/templates/fortune.html
+./go/templates/layout.html

+ 321 - 0
frameworks/Go/fasthttp-postgresql/src/hello/hello.go

@@ -0,0 +1,321 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"html/template"
+	"log"
+	"math/rand"
+	"net"
+	"os"
+	"os/exec"
+	"runtime"
+	"sort"
+
+	"github.com/jackc/pgx"
+	"github.com/valyala/fasthttp"
+	"github.com/valyala/fasthttp/reuseport"
+)
+
+type Message struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           int32 `json:"id"`
+	RandomNumber int32 `json:"randomNumber"`
+}
+
+type Fortune struct {
+	Id      int32  `json:"id"`
+	Message string `json:"message"`
+}
+
+const (
+	worldRowCount      = 10000
+	maxConnectionCount = 256
+)
+
+var (
+	worldSelectStmt   *pgx.PreparedStatement
+	worldUpdateStmt   *pgx.PreparedStatement
+	fortuneSelectStmt *pgx.PreparedStatement
+)
+
+const helloWorldString = "Hello, World!"
+
+const largerJson = `{status:"0",message:"ok",data:[{item:"car"},{item:"house"},{item:"airplane"},{item:"train"}]}`
+
+var (
+	tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))
+
+	db *pgx.ConnPool
+
+	helloWorldBytes = []byte(helloWorldString)
+)
+
+var (
+	listenAddr = flag.String("listenAddr", ":8080", "Address to listen to")
+	prefork    = flag.Bool("prefork", false, "use prefork")
+	child      = flag.Bool("child", false, "is child proc")
+)
+
+func main() {
+	flag.Parse()
+
+	var err error
+
+	// initialize the connection pool
+	if db, err = initDatabase("localhost", "benchmarkdbuser", "benchmarkdbpass", "hello_world", 5432, maxConnectionCount); err != nil {
+		log.Fatalf("Error opening database: %s", err)
+	}
+
+	s := &fasthttp.Server{
+		Handler: mainHandler,
+		Name:    "fasthttp",
+	}
+	ln := getListener()
+	if err = s.Serve(ln); err != nil {
+		log.Fatalf("Error when serving incoming connections: %s", err)
+	}
+}
+
+func mainHandler(ctx *fasthttp.RequestCtx) {
+	path := ctx.Path()
+	switch string(path) {
+	case "/plaintext":
+		plaintextHandler(ctx)
+	case "/json":
+		jsonHandler(ctx)
+	case "/db":
+		dbHandler(ctx)
+	case "/queries":
+		queriesHandler(ctx)
+	case "/fortune":
+		fortuneHandler(ctx)
+	case "/update":
+		updateHandler(ctx)
+	default:
+		ctx.Error("unexpected path", fasthttp.StatusBadRequest)
+	}
+}
+
+// Test 1: JSON serialization
+func jsonHandler(ctx *fasthttp.RequestCtx) {
+	jsonMarshal(ctx, &Message{helloWorldString})
+}
+
+// Test 2: Single database query
+func dbHandler(ctx *fasthttp.RequestCtx) {
+	var w World
+	fetchRandomWorld(&w)
+	jsonMarshal(ctx, &w)
+}
+
+// Test 3: Multiple database queries
+func queriesHandler(ctx *fasthttp.RequestCtx) {
+	n := getQueriesCount(ctx)
+
+	worlds := make([]World, n)
+	for i := 0; i < n; i++ {
+		fetchRandomWorld(&worlds[i])
+	}
+
+	jsonMarshal(ctx, worlds)
+}
+
+// Test 4: Fortunes
+func fortuneHandler(ctx *fasthttp.RequestCtx) {
+	rows, err := db.Query("fortuneSelectStmt")
+	if err != nil {
+		log.Fatalf("Error selecting db data: %v", err)
+	}
+
+	fortunes := make([]Fortune, 0, 16)
+	for rows.Next() {
+		var f Fortune
+		if err := rows.Scan(&f.Id, &f.Message); err != nil {
+			log.Fatalf("Error scanning fortune row: %s", err)
+		}
+		fortunes = append(fortunes, f)
+	}
+	rows.Close()
+	fortunes = append(fortunes, Fortune{Message: "Additional fortune added at request time."})
+
+	sort.Sort(FortunesByMessage(fortunes))
+
+	ctx.SetContentType("text/html")
+	if err := tmpl.Execute(ctx, fortunes); err != nil {
+		log.Fatalf("Error executing fortune: %s", err)
+	}
+}
+
+// Test 5: Database updates
+func updateHandler(ctx *fasthttp.RequestCtx) {
+	n := getQueriesCount(ctx)
+
+	worlds := make([]World, n)
+	for i := 0; i < n; i++ {
+		w := &worlds[i]
+		fetchRandomWorld(w)
+		w.RandomNumber = int32(randomWorldNum())
+	}
+
+	// sorting is required for insert deadlock prevention.
+	sort.Sort(WorldsByID(worlds))
+	txn, err := db.Begin()
+	if err != nil {
+		log.Fatalf("Error starting transaction: %s", err)
+	}
+
+	for i := 0; i < n; i++ {
+		w := &worlds[i]
+		if _, err := txn.Exec("worldUpdateStmt", w.RandomNumber, w.Id); err != nil {
+			log.Fatalf("Error updating world row %d: %s", i, err)
+		}
+	}
+	if err = txn.Commit(); err != nil {
+		log.Fatalf("Error when commiting world rows: %s", err)
+	}
+
+	jsonMarshal(ctx, worlds)
+}
+
+// Test 6: Plaintext
+func plaintextHandler(ctx *fasthttp.RequestCtx) {
+	ctx.Success("text/plain", helloWorldBytes)
+}
+
+func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) {
+	ctx.SetContentType("application/json")
+	if err := json.NewEncoder(ctx).Encode(v); err != nil {
+		log.Fatalf("error in json.Encoder.Encode: %s", err)
+	}
+}
+
+func fetchRandomWorld(w *World) {
+	n := randomWorldNum()
+
+	if err := db.QueryRow("worldSelectStmt", n).Scan(&w.Id, &w.RandomNumber); err != nil {
+		log.Fatalf("Error scanning world row: %s", err)
+	}
+}
+
+func randomWorldNum() int {
+	return rand.Intn(worldRowCount) + 1
+}
+
+func getQueriesCount(ctx *fasthttp.RequestCtx) int {
+	n := ctx.QueryArgs().GetUintOrZero("queries")
+	if n < 1 {
+		n = 1
+	} else if n > 500 {
+		n = 500
+	}
+	return n
+}
+
+type FortunesByMessage []Fortune
+
+func (s FortunesByMessage) Len() int           { return len(s) }
+func (s FortunesByMessage) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s FortunesByMessage) Less(i, j int) bool { return s[i].Message < s[j].Message }
+
+type WorldsByID []World
+
+func (w WorldsByID) Len() int           { return len(w) }
+func (w WorldsByID) Swap(i, j int)      { w[i], w[j] = w[j], w[i] }
+func (w WorldsByID) Less(i, j int) bool { return w[i].Id < w[j].Id }
+
+func mustPrepare(db *pgx.Conn, name, query string) *pgx.PreparedStatement {
+	stmt, err := db.Prepare(name, query)
+	if err != nil {
+		log.Fatalf("Error when preparing statement %q: %s", query, err)
+	}
+	return stmt
+}
+
+func getListener() net.Listener {
+	if !*prefork {
+		runtime.GOMAXPROCS(runtime.NumCPU())
+		ln, err := net.Listen("tcp4", *listenAddr)
+		if err != nil {
+			log.Fatal(err)
+		}
+		return ln
+	}
+
+	if !*child {
+		children := make([]*exec.Cmd, runtime.NumCPU())
+		for i := range children {
+			children[i] = exec.Command(os.Args[0], "-prefork", "-child")
+			children[i].Stdout = os.Stdout
+			children[i].Stderr = os.Stderr
+			if err := children[i].Start(); err != nil {
+				log.Fatal(err)
+			}
+		}
+		for _, ch := range children {
+			if err := ch.Wait(); err != nil {
+				log.Print(err)
+			}
+		}
+		os.Exit(0)
+		panic("unreachable")
+	}
+
+	runtime.GOMAXPROCS(1)
+	ln, err := reuseport.Listen("tcp4", *listenAddr)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return ln
+}
+
+func initDatabase(dbHost string, dbUser string, dbPass string, dbName string, dbPort uint16, maxConnectionsInPool int) (*pgx.ConnPool, error) {
+
+	var successOrFailure string = "OK"
+
+	var config pgx.ConnPoolConfig
+
+	config.Host = dbHost
+	config.User = dbUser
+	config.Password = dbPass
+	config.Database = dbName
+	config.Port = dbPort
+
+	config.MaxConnections = maxConnectionsInPool
+
+	config.AfterConnect = func(eachConn *pgx.Conn) error {
+
+		worldSelectStmt = mustPrepare(eachConn, "worldSelectStmt", "SELECT id, randomNumber FROM World WHERE id = $1")
+		worldUpdateStmt = mustPrepare(eachConn, "worldUpdateStmt", "UPDATE World SET randomNumber = $1 WHERE id = $2")
+		fortuneSelectStmt = mustPrepare(eachConn, "fortuneSelectStmt", "SELECT id, message FROM Fortune")
+
+		return nil
+	}
+
+	fmt.Println("--------------------------------------------------------------------------------------------")
+
+	connPool, err := pgx.NewConnPool(config)
+	if err != nil {
+		successOrFailure = "FAILED"
+		log.Println("Connecting to database ", dbName, " as user ", dbUser, " ", successOrFailure, ": \n ", err)
+	} else {
+		log.Println("Connecting to database ", dbName, " as user ", dbUser, ": ", successOrFailure)
+
+		log.Println("Fetching one record to test if db connection is valid...")
+		var w World
+		n := randomWorldNum()
+		if errPing := connPool.QueryRow("worldSelectStmt", n).Scan(&w.Id, &w.RandomNumber); errPing != nil {
+			log.Fatalf("Error scanning world row: %s", errPing)
+		}
+		log.Println("OK")
+	}
+
+	fmt.Println("--------------------------------------------------------------------------------------------")
+
+	return connPool, err
+
+}

+ 14 - 0
frameworks/Go/fasthttp-postgresql/templates/fortune.html

@@ -0,0 +1,14 @@
+{{define "content"}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+{{end}}

+ 9 - 0
frameworks/Go/fasthttp-postgresql/templates/layout.html

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+{{template "content" .}}
+</body>
+</html>

+ 0 - 2
frameworks/Go/fasthttp/setup.bat

@@ -1,2 +0,0 @@
-set GOPATH=C:\FrameworkBenchmarks\Go\fasthttp
-go run src\hello\hello.go

+ 1 - 1
frameworks/Python/asyncio/aiohttp.web/setup.sh

@@ -4,7 +4,7 @@ fw_depends python3
 
 sed -i 's|host: 127.0.0.1|host: '${DBHOST}'|g' aiohttp.web/etc/hello/main/main.yaml
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 cd $TROOT/aiohttp.web
 api_hour -ac hello:Container &

+ 1 - 1
frameworks/Python/asyncio/yocto_http/setup.sh

@@ -2,7 +2,7 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 cd $TROOT/yocto_http
 api_hour -ac hello:Container &

+ 1 - 1
frameworks/Python/bottle/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 gunicorn app:app -c gunicorn_conf.py &

+ 1 - 1
frameworks/Python/cherrypy/setup_py3.sh

@@ -4,6 +4,6 @@ fw_depends python3
 
 sed -i 's|127.0.0.1|'${DBHOST}'|g' app.py
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 python3 app.py &

+ 1 - 1
frameworks/Python/falcon/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 gunicorn app:app -c gunicorn_conf.py &

+ 1 - 1
frameworks/Python/flask/app.py

@@ -24,7 +24,7 @@ DBHOST = os.environ.get('DBHOST', 'localhost')
 # setup
 
 app = Flask(__name__)
-app.config['SQLALCHEMY_DATABASE_URI'] = DBDRIVER + '//benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % DBHOST
+app.config['SQLALCHEMY_DATABASE_URI'] = DBDRIVER + '://benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % DBHOST
 app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
 db = SQLAlchemy(app)
 dbraw_engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'], connect_args={'autocommit': True}, pool_reset_on_return=None)

+ 2 - 2
frameworks/Python/flask/requirements.txt

@@ -1,11 +1,11 @@
 Jinja2==2.7.3
 Werkzeug==0.10.4
 flask==0.10.1
-SQLAlchemy==1.0.4
+SQLAlchemy==1.0.11
 Flask-SQLAlchemy==2.0
 mysqlclient==1.3.6
 gunicorn==19.3.0
 meinheld==0.5.7
-uwsgi
+uwsgi==2.0.12
 
 greenlet==0.4.7

+ 1 - 1
frameworks/Python/flask/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 gunicorn app:app -c gunicorn_conf.py &

+ 1 - 1
frameworks/Python/pyramid/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 gunicorn wsgi:app -c gunicorn_conf.py &

+ 1 - 1
frameworks/Python/tornado/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 python3 server.py --port=8080 --mongo=$DBHOST --logging=error &

+ 1 - 1
frameworks/Python/wheezyweb/setup_py3.sh

@@ -2,6 +2,6 @@
 
 fw_depends python3
 
-pip install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
+pip3 install --install-option="--prefix=${PY3_ROOT}" -r $TROOT/requirements.txt
 
 gunicorn app:app -c gunicorn_conf.py &

+ 36 - 13
toolset/benchmark/test_types/verifications.py

@@ -66,19 +66,42 @@ def verify_headers(headers, url, should_be='json'):
              'No content encoding found, expected \"%s\"' % (
                  expected_type),
              url))
-    elif content_type.lower() == includes_charset:
-        problems.append(
-            ('warn',
-             ("Content encoding found \"%s\" where \"%s\" is acceptable.\n"
-              "Additional response bytes may negatively affect benchmark performance."
-              % (includes_charset, expected_type)),
-             url))
-    elif content_type != expected_type:
-        problems.append(
-            ('warn',
-             'Unexpected content encoding, found \"%s\", expected \"%s\"' % (
-                 content_type, expected_type),
-             url))
+    else:
+    	content_type = content_type.lower()
+        # "text/html" requires charset to be set. The others do not
+        if expected_type == types['html']:
+            if content_type == expected_type:
+                problems.append(
+                    ('warn',
+                     ('The \"%s\" content type requires \"charset=utf-8\" to be specified.'
+                      % content_type),
+                     url))
+            elif content_type != includes_charset:
+                problems.append(
+                    ('warn',
+                     'Unexpected content encoding, found \"%s\", expected \"%s\".' % (
+                         content_type, expected_type),
+                     url))
+        elif expected_type == types['json']:
+            if content_type == includes_charset:
+                problems.append(
+                    ('warn',
+                     ("Content encoding found \"%s\" where \"%s\" is acceptable.\n"
+                      "Additional response bytes may negatively affect benchmark performance."
+                      % (content_type, expected_type)),
+                     url))
+            elif content_type != expected_type:
+                problems.append(
+                    ('warn',
+                     'Unexpected content encoding, found \"%s\", expected \"%s\"' % (
+                         content_type, expected_type),
+                     url))
+        elif content_type != expected_type and content_type != includes_charset:
+            problems.append(
+                ('warn',
+                 'Unexpected content encoding, found \"%s\", expected \"%s\"' % (
+                     content_type, expected_type),
+                 url))
     return problems
 
 

+ 13 - 7
toolset/setup/linux/languages/mono.sh

@@ -17,7 +17,13 @@ sudo apt-get update
 
 # Find the most recent snapshot
 #SNAPSHOT=$(apt-cache search 'mono-snapshot-.*-assemblies' | cut -d'-' -f3 | tail -1)
-SNAPSHOT="20150202010831"
+
+# REMARK:
+# Rollback this after I execute above command manually due to "msmith-techempower"'s request.
+# According to him "apt-get to install mono is not stable", so keep this way. 
+# If you see mono fail, please doubt this SNAPSHOT and execute above command manually, then
+# copy and paste the value to SNAPSHOT variable just like below.
+SNAPSHOT="2016.01.04+14.28.05"
 
 # save environment
 
@@ -40,13 +46,13 @@ rm -rf $MONO_HOME && mkdir -p $MONO_HOME
 fw_apt_to_iroot mono-snapshot-$SNAPSHOT
 fw_apt_to_iroot mono-snapshot-$SNAPSHOT-assemblies mono-snapshot-$SNAPSHOT
 
-# Simplify paths
-mv $MONO_HOME/opt/mono-*/* $MONO_HOME
-file $MONO_HOME/bin/* | grep "POSIX shell script" | awk -F: '{print $1}' | xargs sed -i "s|/opt/mono-$SNAPSHOT|$MONO_HOME|g"
-sed -i "s|/opt/mono-$SNAPSHOT|$MONO_HOME|g" $MONO_HOME/lib/pkgconfig/*.pc $MONO_HOME/etc/mono/config
 
+# Simplify paths
+sudo mv $MONO_HOME/opt/mono-*/* $MONO_HOME
+file $MONO_HOME/bin/* | grep "POSIX shell script" | awk -F: '{print $1}' | xargs sudo sed -i "s|/opt/mono-$SNAPSHOT|$MONO_HOME|g"
+sudo sed -i "s|/opt/mono-$SNAPSHOT|$MONO_HOME|g" $MONO_HOME/lib/pkgconfig/*.pc $MONO_HOME/etc/mono/config
 echo "mozroots --import --sync" >> $IROOT/mono.installing
 
-mv $IROOT/mono.installing $IROOT/mono.installed
+sudo mv $IROOT/mono.installing $IROOT/mono.installed
 
-source $IROOT/mono.installed
+source $IROOT/mono.installed

+ 3 - 3
toolset/setup/linux/languages/python3.sh

@@ -7,9 +7,9 @@ RETCODE=$(fw_exists ${IROOT}/py3.installed)
   
 PY3_ROOT=$IROOT/py3
 
-fw_get -O http://www.python.org/ftp/python/3.4.2/Python-3.4.2.tar.xz
-fw_untar Python-3.4.2.tar.xz
-cd Python-3.4.2
+fw_get -O http://www.python.org/ftp/python/3.5.1/Python-3.5.1.tar.xz
+fw_untar Python-3.5.1.tar.xz
+cd Python-3.5.1
 ./configure --prefix=$PY3_ROOT --disable-shared --with-computed-gotos --quiet
 make -j4 --quiet 2>&1 | tee $IROOT/python3-install.log | awk '{ if (NR%100 == 0) printf "."}'
 make install --quiet 2>&1 | tee -a $IROOT/python3-install.log | awk '{ if (NR%100 == 0) printf "."}'

+ 1 - 1
toolset/setup/linux/languages/xsp.sh

@@ -15,7 +15,7 @@ git checkout e272a2c006211b6b03be2ef5bbb9e3f8fefd0768
 # build
 ./autogen.sh --prefix=$MONO_HOME --disable-docs
 make
-make install
+sudo make install
 
 # cleanup
 cd ..