Explorar o código

Make HTTP header verification more strict (#3643)

* Make HTTP header verification more strict

It has long been a *requirement* for responses to include these headers:
 - Server
 - Date
 - Content-Type
 - either Content-Length or Transfer-Encoding

Now we are enforcing that requirement instead of emitting a warning.

Frameworks missing these headers will no longer be benchmarked (until we
fix them).  I bet there is at least one framework that will fail because
of this.  I'm sure I've seen warnings about a missing Server header
before.

Also, do not emit a warning for frameworks that include ";charset=utf-8"
on plaintext and json responses.  Many frameworks do this by default and
that behavior isn't a problem that we want anyone to fix.

* [ci skip] Fix toolset exception when Content-Type is missing

* Try fixing several header verification failures

* Add validation for Date header format

* Fix more header verification failures

* Remove test code from ninja-stanalone, update dependencies

Having the test dependencies causes ninja-standalone to take much longer
to build.  And we don't encourage putting test code in here in general.
The implementations ARE the test code.

* Make invalid Date header format a warning, not a failure

I can't fix octane's or ninja-standalone's date formats.

* Use octane source files in implementation, remove unused files

Previously it was ignoring all of our source files and only running code
found in the octane GitHub repository.

* Try harder to make aspnet include charset=utf-8 in fortunes

* Fix wrong content type for yii2 plaintext, was html

* Fix Date headers for crystal frameworks

* Fix up aspnet content type problems finally, hopefully
Michael Hixson %!s(int64=7) %!d(string=hai) anos
pai
achega
eb67470bd1
Modificáronse 76 ficheiros con 262 adicións e 733 borrados
  1. 3 0
      frameworks/C++/cppcms/config-direct-mysql.json
  2. 3 0
      frameworks/C++/cppcms/config-nginx-mysql.json
  3. 3 0
      frameworks/C++/cppcms/config-nginx-postgresql.json
  4. 1 0
      frameworks/C++/treefrog/controllers/worldcontroller.cpp
  5. 0 52
      frameworks/C/octane/CMakeLists.txt
  6. 0 100
      frameworks/C/octane/Makefile
  7. 0 56
      frameworks/C/octane/common.cmake
  8. 8 11
      frameworks/C/octane/octane.dockerfile
  9. 2 1
      frameworks/C/onion/hello.c
  10. 2 1
      frameworks/CSharp/aspnet/src/Controllers/AdoController.cs
  11. 1 0
      frameworks/CSharp/aspnet/src/Controllers/EntityFrameworkController.cs
  12. 11 6
      frameworks/CSharp/evhttp-sharp/src/Program.cs
  13. 1 1
      frameworks/CSharp/nancy/NancyModules/PlainModule.cs
  14. 1 1
      frameworks/Crystal/amber/src/controllers/benchmark_controller.cr
  15. 1 1
      frameworks/Crystal/crystal/server.cr
  16. 1 1
      frameworks/Crystal/crystal/server_radix.cr
  17. 1 1
      frameworks/Crystal/kemal/server-postgres.cr
  18. 1 1
      frameworks/D/hunt/source/app/controller/index.d
  19. 2 2
      frameworks/Dart/redstone/server.dart
  20. 2 1
      frameworks/Erlang/elli/elli.dockerfile
  21. 3 3
      frameworks/Erlang/elli/src/elli_bench_cb.erl
  22. 1 1
      frameworks/Go/falcore/src/framework_benchmarks/falcore.go
  23. 1 1
      frameworks/Go/goji/src/goji/server.go
  24. 1 1
      frameworks/Go/kami/src/kami/server.go
  25. 6 0
      frameworks/Go/revel/src/benchmark/app/controllers/app.go
  26. 3 2
      frameworks/Go/webgo/src/hello/hello.go
  27. 4 0
      frameworks/Java/act/src/main/java/com/techempower/act/controller/HelloWorldController.java
  28. 0 1
      frameworks/Java/act/src/main/resources/conf/json_plaintext/routes.conf
  29. 2 2
      frameworks/Java/jawn/src/main/java/app/BenchmarkMain.java
  30. 5 5
      frameworks/Java/jawn/src/main/java/app/controllers/DbController.java
  31. 1 0
      frameworks/Java/jawn/src/main/java/app/controllers/FortunesController.java
  32. 1 1
      frameworks/Java/light-java/src/main/java/com/networknt/techempower/handler/FortunesPostgresqlGetHandler.java
  33. 10 17
      frameworks/Java/ninja-standalone/pom.xml
  34. 0 7
      frameworks/Java/ninja-standalone/src/main/java/conf/Routes.java
  35. 0 13
      frameworks/Java/ninja-standalone/src/main/java/conf/application.conf
  36. 0 27
      frameworks/Java/ninja-standalone/src/main/java/controllers/SetupController.java
  37. 0 69
      frameworks/Java/ninja-standalone/src/main/java/dao/SetupDao.java
  38. 0 19
      frameworks/Java/ninja-standalone/src/main/resources/META-INF/persistence.xml
  39. 0 92
      frameworks/Java/ninja-standalone/src/test/java/controllers/HelloDbControllerTest.java
  40. 0 37
      frameworks/Java/ninja-standalone/src/test/java/controllers/HelloFortuneControllerTest.java
  41. 0 48
      frameworks/Java/ninja-standalone/src/test/java/controllers/HelloJsonControllerTest.java
  42. 0 29
      frameworks/Java/ninja-standalone/src/test/java/controllers/HelloPlaintextControllerTest.java
  43. 2 2
      frameworks/Java/restexpress/pom.xml
  44. 10 1
      frameworks/Java/restexpress/src/main/java/hello/Main.java
  45. 1 3
      frameworks/Java/restexpress/src/main/java/hello/controller/JsonController.java
  46. 1 0
      frameworks/Java/restexpress/src/main/java/hello/controller/PlaintextController.java
  47. 1 0
      frameworks/PHP/yii2/app/controllers/SiteController.php
  48. 11 0
      frameworks/Python/bottle/app.py
  49. 8 5
      frameworks/Python/bottle/bottle-nginx-uwsgi.dockerfile
  50. 5 3
      frameworks/Python/bottle/bottle-pypy2.dockerfile
  51. 5 3
      frameworks/Python/bottle/bottle-raw.dockerfile
  52. 5 3
      frameworks/Python/bottle/bottle.dockerfile
  53. 3 0
      frameworks/Python/falcon/app.py
  54. 5 4
      frameworks/Python/falcon/falcon-py3.dockerfile
  55. 5 4
      frameworks/Python/falcon/falcon-pypy2.dockerfile
  56. 5 4
      frameworks/Python/falcon/falcon.dockerfile
  57. 0 7
      frameworks/Python/falcon/requirements-pypy.lock
  58. 7 3
      frameworks/Python/falcon/requirements-pypy.txt
  59. 0 7
      frameworks/Python/falcon/requirements.lock
  60. 7 5
      frameworks/Python/falcon/requirements.txt
  61. 11 5
      frameworks/Python/flask/app.py
  62. 4 4
      frameworks/Python/pyramid/frameworkbenchmarks/views.py
  63. 4 1
      frameworks/Python/uwsgi/hello.py
  64. 7 0
      frameworks/Python/weppy/app.py
  65. 1 0
      frameworks/Ruby/hanami/apps/web/controllers/hello_world/db.rb
  66. 1 0
      frameworks/Ruby/hanami/apps/web/controllers/hello_world/json.rb
  67. 1 0
      frameworks/Ruby/hanami/apps/web/controllers/hello_world/plaintext.rb
  68. 1 0
      frameworks/Ruby/hanami/apps/web/controllers/hello_world/query.rb
  69. 1 0
      frameworks/Ruby/hanami/apps/web/controllers/hello_world/update.rb
  70. 16 4
      frameworks/Ruby/padrino/app/controllers.rb
  71. 1 2
      frameworks/Ruby/padrino/config/database.rb
  72. 10 5
      frameworks/Ruby/padrino/padrino-unicorn.dockerfile
  73. 8 3
      frameworks/Ruby/padrino/padrino.dockerfile
  74. 3 3
      frameworks/Rust/rouille/rouille.dockerfile
  75. 2 2
      frameworks/Rust/rouille/src/main.rs
  76. 28 39
      toolset/benchmark/test_types/verifications.py

+ 3 - 0
frameworks/C++/cppcms/config-direct-mysql.json

@@ -1,4 +1,7 @@
 {
+    "localization": {
+        "locales": [ "en_US.UTF-8" ]
+    },
     "service": {
         "api": "http",
         "ip": "0.0.0.0",

+ 3 - 0
frameworks/C++/cppcms/config-nginx-mysql.json

@@ -1,4 +1,7 @@
 {
+    "localization": {
+        "locales": [ "en_US.UTF-8" ]
+    },
     "service": {
         "api": "fastcgi",
         "socket": "/var/tmp/cppcms.sock"

+ 3 - 0
frameworks/C++/cppcms/config-nginx-postgresql.json

@@ -1,4 +1,7 @@
 {
+    "localization": {
+        "locales": [ "en_US.UTF-8" ]
+    },
     "service": {
         "api": "fastcgi",
         "socket": "/var/tmp/cppcms.sock"

+ 1 - 0
frameworks/C++/treefrog/controllers/worldcontroller.cpp

@@ -12,6 +12,7 @@ void WorldController::index()
 
 void WorldController::plain()
 {
+    setContentType("text/plain");
     renderText(QLatin1String("Hello, World!"));
 }
 

+ 0 - 52
frameworks/C/octane/CMakeLists.txt

@@ -1,52 +0,0 @@
-cmake_minimum_required(VERSION 2.6)
-include(CMakeToolsHelpers OPTIONAL)
-include("common.cmake")
-
-project(octane C CXX)
-
-set(CMAKE_BUILD_TYPE RelWithDebInfo)
-set (CMAKE_CXX_STANDARD 14)
-
-add_definitions(-std=gnu99)
-#add_definitions(-msse4.1)
-add_definitions(-mavx)
-add_definitions(-pedantic)
-add_definitions(-O3)
-add_definitions(-Wall)
-add_definitions(-Wextra)
-add_definitions(-Wcast-align)
-add_definitions(-w)
-
-# ----------------------------------------
-# Benchmark service
-# ----------------------------------------
-file(GLOB_RECURSE TECHEMPOWER_BENCHMARKS_SOURCES
-    ${CMAKE_CURRENT_SOURCE_DIR}/lib/octane/lib/sds/*.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/lib/octane/lib/sds/*.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
-
-list(SORT TECHEMPOWER_BENCHMARKS_SOURCES)
-create_source_group("Source Files" "${CMAKE_CURRENT_SOURCE_DIR}/src" ${TECHEMPOWER_BENCHMARKS_SOURCES})
-include_directories(${CMAKE_SOURCE_DIR}/lib/octane/lib/libuv/include)
-include_directories(${CMAKE_SOURCE_DIR}/lib/octane/lib/sds)
-include_directories(${CMAKE_SOURCE_DIR}/lib/octane/lib/rapidjson/include)
-include_directories(${CMAKE_SOURCE_DIR}/lib/octane/include)
-include_directories(${CMAKE_SOURCE_DIR}/include)
-
-find_package(Threads REQUIRED)
-
-add_executable (techempower_benchmarks
-    ${TECHEMPOWER_BENCHMARKS_SOURCES})
-
-# Libraries to link in reverse order because that's what ld requires.
-target_link_libraries (techempower_benchmarks
-    ${CMAKE_SOURCE_DIR}/lib/octane/build/liboctane.a
-    ${CMAKE_SOURCE_DIR}/lib/octane/lib/libuv/.libs/libuv.a
-    ${CMAKE_THREAD_LIBS_INIT})
-
-if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-    target_link_libraries (techempower_benchmarks rt)
-endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")

+ 0 - 100
frameworks/C/octane/Makefile

@@ -1,100 +0,0 @@
-projectpath = ${CURDIR}
-libuv_path = ${projectpath}/lib/libuv
-wrk_path = ${projectpath}/lib/wrk
-wrk2_path = ${projectpath}/lib/wrk2
-sds_path = ${projectpath}/lib/sds
-rapidjson_path = ${projectpath}/lib/rapidjson
-lockless_path = ${projectpath}/lib/lockless_allocator
-tcmalloc_path = ${projectpath}/lib/tcmalloc
-
-ifeq ($(OS),Windows_NT)
-		OPERATING_SYSTEM = WINDOWS
-    CCFLAGS += -D WIN32
-    ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
-        CCFLAGS += -D AMD64
-    else
-        ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
-            CCFLAGS += -D AMD64
-        endif
-        ifeq ($(PROCESSOR_ARCHITECTURE),x86)
-            CCFLAGS += -D IA32
-        endif
-    endif
-else
-    UNAME_S := $(shell uname -s)
-    ifeq ($(UNAME_S),Linux)
-				OPERATING_SYSTEM = LINUX
-				PLATFORM_TARGET = linux
-        CCFLAGS += -D LINUX
-    endif
-    ifeq ($(UNAME_S),Darwin)
-				OPERATING_SYSTEM = OSX
-				PLATFORM_TARGET = osx
-        CCFLAGS += -D OSX
-    endif
-    UNAME_P := $(shell uname -p)
-    ifeq ($(UNAME_P),x86_64)
-        CCFLAGS += -D AMD64
-    endif
-    ifneq ($(filter %86,$(UNAME_P)),)
-        CCFLAGS += -D IA32
-    endif
-    ifneq ($(filter arm%,$(UNAME_P)),)
-        CCFLAGS += -D ARM
-    endif
-endif
-
-all: deps $(PLATFORM_TARGET)
-
-target: all
-
-osx: deps
-	if [ ! -d "build" ]; then mkdir -p build; fi
-	if [ ! -d "build/Makefile" ]; then cd build;cmake -DCMAKE_OSX_ARCHITECTURES=x86_64 ..; fi
-	cmake --build ./build --target all --config Debug -- -j 10
-
-xcode: deps
-	if [ ! -d "build" ]; then mkdir -p build; fi
-	if [ ! -d "build/octane.xcodeproj" ]; then cd build;cmake -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Xcode ..; fi
-	cd build;xcodebuild -project octane.xcodeproj/
-
-linux: deps $(lockless_path)
-	rm -rf build
-	mkdir -p build
-	cd build;cmake ..
-	cd build;make VERBOSE=1
-
-$(libuv_path)/.libs/libuv.a:
-	if [ ! -d "$(libuv_path)" ]; then git clone https://github.com/libuv/libuv.git $(libuv_path); fi
-	cd $(libuv_path);sh autogen.sh
-	cd $(libuv_path);./configure
-	cd $(libuv_path);make
-
-$(wrk_path)/wrk:
-	if [ ! -d "$(wrk_path)" ]; then git clone https://github.com/wg/wrk.git $(wrk_path); fi
-	cd $(wrk_path);make
-
-$(wrk2_path)/wrk:
-	if [ ! -d "$(wrk2_path)" ]; then git clone https://github.com/giltene/wrk2.git $(wrk2_path); fi
-	cd $(wrk2_path);make
-
-$(sds_path):
-	if [ ! -d "$(sds_path)" ]; then git clone https://github.com/antirez/sds $(sds_path); fi
-
-$(rapidjson_path):
-	if [ ! -d "$(rapidjson_path)" ]; then git clone https://github.com/miloyip/rapidjson.git $(rapidjson_path); fi
-
-$(tcmalloc_path):
-	if [ ! -d "$(tcmalloc_path)" ]; then git clone https://github.com/gperftools/gperftools.git $(tcmalloc_path); fi
-	cd $(tcmalloc_path);./autogen.sh
-	cd $(tcmalloc_path);./configure
-	cd $(tcmalloc_path);make
-
-$(lockless_path):
-	if [ ! -d "$(lockless_path)" ]; then wget -q https://locklessinc.com/downloads/lockless_allocator_src.tgz -P lib; fi
-	cd lib;tar xvzf lockless_allocator_src.tgz
-	cd lib/lockless_allocator;make
-
-tools: $(wrk_path)/wrk $(wrk2_path)/wrk
-
-deps: $(libuv_path)/.libs/libuv.a $(sds_path) $(rapidjson_path) $(tcmalloc_path)

+ 0 - 56
frameworks/C/octane/common.cmake

@@ -1,56 +0,0 @@
-cmake_minimum_required(VERSION 2.6)
-
-# create_source_group(relativeSourcePath sourceGroupName files)
-#
-# Creates a source group with the specified name relative to the relative path
-# specified.
-#
-# Parameters:
-#    - sourceGroupName: Name of the source group to create.
-#    - relativeSourcePath: Relative path to the files.
-#    - sourceFiles: Files to add to the source group.
-#
-# For example if you have the following directory structure:
-#
-#    - ExampleApplication
-#        - include
-#            - Main.h
-#                - Window
-#                    Window.h
-#        - source
-#            - Main.cpp
-#                - Window
-#                    Window.cpp
-#
-# You can get your list of files and call create_source_group the following way
-#
-#    file(GLOB_RECURSE my_source_files ${CMAKE_CURRENT_SOURCE_DIR}/source/*)
-#    create_source_group("Source Files" "${CMAKE_CURRENT_SOURCE_DIR}/source" ${my_source_files})
-#    file(GLOB_RECURSE my_header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/*)
-#    create_source_group("Header Files" "${CMAKE_CURRENT_SOURCE_DIR}/include" ${my_header_files})
-#    add_executable(ExampleApplication ${my_source_files} ${my_header_files})
-#
-# Then the generated solution would look like this
-#
-#    - ExampleApplication (project)
-#        - Header Files
-#            - Main.h
-#                - Window
-#                    Window.h
-#        - Source Files
-#            - Main.cpp
-#                - Window
-#                    Window.cpp
-#
-function(create_source_group sourceGroupName relativeSourcePath)
-    FOREACH(currentSourceFile ${ARGN})
-        FILE(RELATIVE_PATH folder ${relativeSourcePath} ${currentSourceFile})
-        get_filename_component(filename ${folder} NAME)
-        string(REPLACE ${filename} "" folder ${folder})
-        if(NOT folder STREQUAL "")
-            string(REGEX REPLACE "/+$" "" folderlast ${folder})
-            string(REPLACE "/" "\\" folderlast ${folderlast})
-            SOURCE_GROUP("${sourceGroupName}\\${folderlast}" FILES ${currentSourceFile})
-        endif(NOT folder STREQUAL "")
-    ENDFOREACH(currentSourceFile ${ARGN})
-endfunction(create_source_group)

+ 8 - 11
frameworks/C/octane/octane.dockerfile

@@ -1,18 +1,15 @@
 FROM ubuntu:16.04
 
-ADD ./ /octane
-WORKDIR /octane
-
 RUN apt update -yqq && \
     apt install -yqq software-properties-common build-essential git cmake automake libtool wget
 
-# May 22nd, 2017
-RUN git clone https://github.com/simongui/octane.git && \
-    cd octane && \
-    git checkout 8c28b1b83f1aa2817a401a3e8437a0af4ec53c28 && \
-    make
+WORKDIR /octane
+RUN git clone https://github.com/simongui/octane.git .
+RUN git checkout 8c28b1b83f1aa2817a401a3e8437a0af4ec53c28
+RUN rm -rf src/techempower_benchmarks
+COPY src src/techempower_benchmarks
 
-ENV PATH=/octane/octane/build/:${PATH}
-ENV LD_PRELOAD=/octane/octane/lib/lockless_allocator/libllalloc.so.1.3
+RUN make
 
-CMD ["techempower_benchmarks"]
+ENV LD_PRELOAD /octane/lib/lockless_allocator/libllalloc.so.1.3
+CMD ["./build/techempower_benchmarks"]

+ 2 - 1
frameworks/C/onion/hello.c

@@ -221,7 +221,8 @@ onion_connection_status return_fortune(MYSQL *db, onion_request *req, onion_resp
 	}
 	
 	onion_dict_add(context,"fortunes",fortunes, OD_DICT|OD_FREE_VALUE);
-	
+
+	onion_response_set_header(res, "Content-Type","text/html;charset=utf-8");
 	onion_connection_status ret=fortunes_html_template(context, req, res);
 	free(fortune_list.list);
 	return ret;

+ 2 - 1
frameworks/CSharp/aspnet/src/Controllers/AdoController.cs

@@ -94,7 +94,8 @@ namespace Benchmarks.AspNet.Controllers
             
             fortunes.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
             fortunes.Sort();
-            
+
+            Response.Charset = "utf-8";
             return View("Fortunes", fortunes);
         }
 

+ 1 - 0
frameworks/CSharp/aspnet/src/Controllers/EntityFrameworkController.cs

@@ -39,6 +39,7 @@ namespace Benchmarks.AspNet.Controllers
             fortunes.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
             fortunes.Sort();
 
+            Response.Charset = "utf-8";
             return View("Fortunes", fortunes);
         }
 

+ 11 - 6
frameworks/CSharp/evhttp-sharp/src/Program.cs

@@ -23,16 +23,21 @@ namespace EvHttpSharpBenchmark
 		private static void Handler(EventHttpRequest req)
 		{
 			var headers = new Dictionary<string, string>();
-			var resp = "Hello, World!";
-
-			if (!req.Uri.Contains("plaintext"))
+			headers["Server"] = "evhttp-sharp";
+			string responseText;
+			if (req.Uri.Contains("plaintext"))
+			{
+				headers["Content-Type"] = "text/plain";
+				responseText = "Hello, World!";
+			}
+			else
 			{
+				headers["Content-Type"] = "application/json";
 				var sw = new StringWriter();
 				Serializer.Serialize(sw, new {message = "Hello, World!"});
-				resp = sw.ToString();
-				headers["Content-Type"] = "application/json";
+				responseText = sw.ToString();
 			}
-			req.Respond (HttpStatusCode.OK, headers, Encoding.UTF8.GetBytes (resp));
+			req.Respond (HttpStatusCode.OK, headers, Encoding.UTF8.GetBytes (responseText));
 		}
 	}
 }

+ 1 - 1
frameworks/CSharp/nancy/NancyModules/PlainModule.cs

@@ -8,7 +8,7 @@ namespace NancyModules
     {
       Get["/"] = x =>
       {
-        return "Hello, World!";
+        return Response.AsText("Hello, World!");
       };
     }
   }

+ 1 - 1
frameworks/Crystal/amber/src/controllers/benchmark_controller.cr

@@ -8,7 +8,7 @@ class BenchmarkController < Amber::Controller::Base
   before_action do
     all do
       response.headers["Server"] = "Amber"
-      response.headers["Date"] = "#{Time.now}"
+      response.headers["Date"] = Time.utc_now.to_s("%a, %d %b %Y %H:%M:%S GMT")
     end
   end
 

+ 1 - 1
frameworks/Crystal/crystal/server.cr

@@ -11,7 +11,7 @@ server = HTTP::Server.new("0.0.0.0", 8080) do |context|
   request = context.request
 
   response.headers["Server"] = "Crystal"
-  response.headers["Date"] = Time.utc_now.to_s
+  response.headers["Date"] = Time.utc_now.to_s("%a, %d %b %Y %H:%M:%S GMT")
   
   case request.path
   when "/json"

+ 1 - 1
frameworks/Crystal/crystal/server_radix.cr

@@ -96,7 +96,7 @@ server = HTTP::Server.new("0.0.0.0", 8080) do |context|
   request = context.request
   response = context.response
   response.headers["Server"] = "Crystal"
-  response.headers["Date"] = Time.utc_now.to_s
+  response.headers["Date"] = Time.utc_now.to_s("%a, %d %b %Y %H:%M:%S GMT")
 
   result = tree.find(request.path)
 

+ 1 - 1
frameworks/Crystal/kemal/server-postgres.cr

@@ -44,7 +44,7 @@ end
 
 before_all do |env|
   env.response.headers["Server"] = "Kemal"
-  env.response.headers["Date"] = Time.now.to_s
+  env.response.headers["Date"] = Time.utc_now.to_s("%a, %d %b %Y %H:%M:%S GMT")
 end
 
 #

+ 1 - 1
frameworks/D/hunt/source/app/controller/index.d

@@ -37,7 +37,7 @@ class IndexController : Controller
 	@Action void plaintext()
 	{
 		res.setHeader("Date",printDate);
-		res.html("Hello, World!");
+		res.plain("Hello, World!");
 	}
 
 	private string printDate() {

+ 2 - 2
frameworks/Dart/redstone/server.dart

@@ -145,7 +145,7 @@ class PgTests {
     })));
   }
   
-  @app.Route("/fortunes", responseType: "text/html")
+  @app.Route("/fortunes", responseType: "text/html;charset=utf-8")
   Future<String> fortunesTest(@app.Inject() mustache.Template template) {
     return pgSql.query(fortuneQuery, Fortune).then((values) {
       values
@@ -193,7 +193,7 @@ class MongoTests {
     })));
   }
   
-  @app.Route("/fortunes", responseType: "text/html")
+  @app.Route("/fortunes", responseType: "text/html;charset=utf-8")
   Future<String> fortunesTest(@app.Inject() mustache.Template template) {
     return mongoDb.find(fortuneCollection, MongoFortune).then((values) {
       values

+ 2 - 1
frameworks/Erlang/elli/elli.dockerfile

@@ -1,7 +1,8 @@
 FROM erlang:18.3.4.8
 
-ADD ./ /elli
 WORKDIR /elli
+COPY src src
+COPY rebar.config rebar.config
 
 RUN rebar get-deps
 RUN rebar compile

+ 3 - 3
frameworks/Erlang/elli/src/elli_bench_cb.erl

@@ -12,13 +12,13 @@ handle(Req, _Args) ->
 handle('GET', [<<"plaintext">>], _Req) ->
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% to signal success.
-    {ok,[{<<"Content-Type">>, <<"text/plain">>}], <<"Hello, World!">>};
+    {ok, [{<<"Server">>, <<"elli">>}, {<<"Content-Type">>, <<"text/plain">>}], <<"Hello, World!">>};
 
 %% Json test route
 handle('GET',[<<"json">>], _Req) ->
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% to signal success.
-    {ok, [{<<"Content-Type">>, <<"application/json">>}], jiffy:encode({[{<<"message">>, <<"Hello, World!">>}]})};
+    {ok, [{<<"Server">>, <<"elli">>}, {<<"Content-Type">>, <<"application/json">>}], jiffy:encode({[{<<"message">>, <<"Hello, World!">>}]})};
 
 %% db test route (Single Database Query)
 handle('GET',[<<"db">>], Req) ->
@@ -33,7 +33,7 @@ handle('GET',[<<"db">>], Req) ->
 			        {result_packet, _, _, [[ID, Rand]], _} <- [emysql:execute(test_pool, db_stmt, [random:uniform(10000)]) || _ <- lists:seq(1, I) ]],
 			Res
 		end,
-    {ok, [{<<"Content-Type">>, <<"application/json">>}], jiffy:encode(lists:nth(1,JSON))};
+    {ok, [{<<"Server">>, <<"elli">>}, {<<"Content-Type">>, <<"application/json">>}], jiffy:encode(lists:nth(1,JSON))};
 
 %% TODO : Finish this function with correct logic.
 %%        Please check TFB document

+ 1 - 1
frameworks/Go/falcore/src/framework_benchmarks/falcore.go

@@ -232,7 +232,7 @@ var fortuneFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Re
 			pipeWriter.Close()
 		}()
 
-		textHtml := http.Header{"Content-Type": []string{"text/html"}}
+		textHtml := http.Header{"Content-Type": []string{"text/html;charset=utf-8"}}
 		return falcore.SimpleResponse(req.HttpRequest, 200, textHtml, -1, pipeReader)
 	}
 	return nil

+ 1 - 1
frameworks/Go/goji/src/goji/server.go

@@ -158,7 +158,7 @@ func fortunes(c web.C, w http.ResponseWriter, r *http.Request) {
 	fortunes = append(fortunes, &Fortune{Message: extraFortuneMessage})
 
 	sort.Sort(ByMessage{fortunes})
-	setContentType(w, "text/html")
+	setContentType(w, "text/html;charset=utf-8")
 	if err := tmpl.Execute(w, fortunes); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 	}

+ 1 - 1
frameworks/Go/kami/src/kami/server.go

@@ -141,7 +141,7 @@ func fortuneHandler(ctx context.Context, w http.ResponseWriter, r *http.Request)
 	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
 
 	sort.Sort(ByMessage{fortunes})
-	w.Header().Set("Content-Type", "text/html")
+	w.Header().Set("Content-Type", "text/html;charset=utf-8")
 	if err := tmpl.Execute(w, fortunes); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 	}

+ 6 - 0
frameworks/Go/revel/src/benchmark/app/controllers/app.go

@@ -41,6 +41,7 @@ var (
 
 func init() {
 	revel.Filters = []revel.Filter{
+		ServerHeaderFilter,
 		revel.RouterFilter,
 		revel.ParamsFilter,
 		revel.ActionInvoker,
@@ -65,6 +66,11 @@ func init() {
 	})
 }
 
+var ServerHeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
+	c.Response.Out.Header().Set("Server", "revel")
+	fc[0](c, fc[1:]) // Execute the next filter stage.
+}
+
 type App struct {
 	*revel.Controller
 }

+ 3 - 2
frameworks/Go/webgo/src/hello/hello.go

@@ -12,10 +12,11 @@ type MessageStruct struct {
 	Message string `json:"message"`
 }
 
-func hello(val string) string {
+func hello(ctx *web.Context, val string) {
 	m := MessageStruct{"Hello, World!"}
 	j, _ := json.Marshal(m)
-	return string(j)
+	ctx.ContentType("application/json")
+	ctx.Write(j)
 }
 
 func main() {

+ 4 - 0
frameworks/Java/act/src/main/java/com/techempower/act/controller/HelloWorldController.java

@@ -47,6 +47,10 @@ public class HelloWorldController {
         Act.getNonblock("/json", context -> context.resp()
                 .contentType(H.Format.JSON.contentType())
                 .writeContent(JSON.toJSONString(new Message(HELLO_WORLD))));
+
+        Act.getNonblock("/plaintext", context -> context.resp()
+                .contentType(H.Format.TXT.contentType())
+                .writeContent(HELLO_WORLD));
     }
 
 }

+ 0 - 1
frameworks/Java/act/src/main/resources/conf/json_plaintext/routes.conf

@@ -1 +0,0 @@
-GET /plaintext echo: Hello, World!

+ 2 - 2
frameworks/Java/jawn/src/main/java/app/BenchmarkMain.java

@@ -30,8 +30,8 @@ public class BenchmarkMain extends Jawn {
         get("/queries",DbController.class, DbController::getQueries);
         get("/updates",DbController.class, DbController::getUpdates);
         
-        get("/json", (context) -> Results.json(new Message(message)));
-        get("/plaintext", (context) -> Results.text(bytemessage));
+        get("/json", (context) -> Results.json(new Message(message)).addHeader("Server", "jawn"));
+        get("/plaintext", (context) -> Results.text(bytemessage).addHeader("Server", "jawn"));
         
         use(new AbstractModule() {
             @Override

+ 5 - 5
frameworks/Java/jawn/src/main/java/app/controllers/DbController.java

@@ -14,21 +14,21 @@ public class DbController extends Controller {
     
     // /db
     public void index() {
-        respond().json(db.getWorld(Helper.getRandomNumber()));
+        respond().json(db.getWorld(Helper.getRandomNumber())).addHeader("Server", "jawn");
     }
     
     // /queries?queries=
     public void getQueries() {
         int param = parseQueryParam();
-        
-        respond().json(db.getWorlds(param));
+
+        respond().json(db.getWorlds(param)).addHeader("Server", "jawn");
     }
     
     // /updates?queries=
     public void getUpdates() {
         int param = parseQueryParam();
-        
-        respond().json(db.getAndUpdateWorlds(param));
+
+        respond().json(db.getAndUpdateWorlds(param)).addHeader("Server", "jawn");
     }
     
     private int parseQueryParam() {

+ 1 - 0
frameworks/Java/jawn/src/main/java/app/controllers/FortunesController.java

@@ -18,6 +18,7 @@ public class FortunesController extends Controller {
         List<Fortune> fortunes = db.fetchAllFortunes();
         fortunes.add(new Fortune(0, "Additional fortune added at request time."));
         Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+        header("Server", "jawn");
         view("fortunes", fortunes);
     }
 }

+ 1 - 1
frameworks/Java/light-java/src/main/java/com/networknt/techempower/handler/FortunesPostgresqlGetHandler.java

@@ -42,7 +42,7 @@ public class FortunesPostgresqlGetHandler implements HttpHandler {
         StringWriter writer = new StringWriter();
         mustache.execute(writer, fortunes);
         exchange.getResponseHeaders().put(
-                Headers.CONTENT_TYPE, "text/html");
+                Headers.CONTENT_TYPE, "text/html;charset=utf-8");
         exchange.getResponseSender().send(writer.toString());
     }
 }

+ 10 - 17
frameworks/Java/ninja-standalone/pom.xml

@@ -9,8 +9,8 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <maven.compiler.source>1.8</maven.compiler.source>
-        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>10</maven.compiler.source>
+        <maven.compiler.target>10</maven.compiler.target>
 
         <maven-assembly-plugin.version>3.1.0</maven-assembly-plugin.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
@@ -18,14 +18,14 @@
         <maven-enforcer-plugin.version>1.4.1</maven-enforcer-plugin.version>
         <maven-war-plugin.version>3.2.0</maven-war-plugin.version>
 
-        <h2.version>1.4.196</h2.version>
-        <hibernate.version>5.2.12.Final</hibernate.version>
-        <hibernate-validator.version>6.0.5.Final</hibernate-validator.version>
-        <jaxb-api.version>2.2.12</jaxb-api.version>
-        <jetty.version>9.3.6.v20151106</jetty.version>
-        <mysql.version>5.1.45</mysql.version>
-        <ninja.version>6.2.0</ninja.version>
-        <xml-apis.version>1.4.01</xml-apis.version>
+        <h2.version>1.4.197</h2.version>
+        <hibernate.version>5.2.16.Final</hibernate.version>
+        <hibernate-validator.version>6.0.9.Final</hibernate-validator.version>
+        <jaxb-api.version>2.3.0</jaxb-api.version>
+        <jetty.version>9.3.15.v20161220</jetty.version>
+        <mysql.version>5.1.46</mysql.version>
+        <ninja.version>6.2.2</ninja.version>
+        <xml-apis.version>2.0.2</xml-apis.version>
     </properties>
 
     <dependencies>
@@ -39,13 +39,6 @@
             <groupId>org.ninjaframework</groupId>
             <artifactId>ninja-standalone</artifactId>
             <version>${ninja.version}</version>
-        </dependency>   
-
-        <dependency>
-            <groupId>org.ninjaframework</groupId>
-            <artifactId>ninja-test-utilities</artifactId>
-            <version>${ninja.version}</version>
-            <scope>test</scope>
         </dependency>
 
         <dependency>

+ 0 - 7
frameworks/Java/ninja-standalone/src/main/java/conf/Routes.java

@@ -5,7 +5,6 @@ import controllers.HelloDbController;
 import controllers.HelloFortuneController;
 import controllers.HelloJsonController;
 import controllers.HelloPlaintextController;
-import controllers.SetupController;
 import ninja.Router;
 import ninja.application.ApplicationRoutes;
 import ninja.utils.NinjaProperties;
@@ -25,11 +24,5 @@ public class Routes implements ApplicationRoutes {
         router.GET().route("/fortunes").with(HelloFortuneController.class, "index");
         router.GET().route("/update").with(HelloDbController.class, "update");
 
-        // This route is only active when developing the app in dev mode
-        // e.g. when calling "mvn ninja:run".
-        if (ninjaProperties.isDev()) {
-            router.GET().route("/setupData").with(SetupController.class, "setupData");
-        }
-
     }
 }

+ 0 - 13
frameworks/Java/ninja-standalone/src/main/java/conf/application.conf

@@ -35,17 +35,4 @@ ninja.migration.run=false
 %prod.db.connection.username=benchmarkdbuser
 %prod.db.connection.password=benchmarkdbpass
 
-## for testing and developing locally:
-%dev.ninja.jpa.persistence_unit_name = h2
-%dev.db.connection.url=jdbc:h2:target/h2database
-%dev.db.connection.username=ra
-%dev.db.connection.password=
-
-## for testing
-%test.ninja.jpa.persistence_unit_name = h2
-# in memory database for testing...
-%test.db.connection.url=jdbc:h2:mem:test_db
-%test.db.connection.username=ra
-%test.db.connection.password=
-
 application.secret = b9z4AQO0huDRrJXFVjNiNXmSVqPSbcqjEiNjdPVBApb8n9GnxVjWBr9jp8tRfe73

+ 0 - 27
frameworks/Java/ninja-standalone/src/main/java/controllers/SetupController.java

@@ -1,27 +0,0 @@
-package controllers;
-
-import ninja.Result;
-import ninja.Results;
-
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import dao.SetupDao;
-
-@Singleton
-public class SetupController {
-
-    @Inject
-    SetupDao setupDao;
-
-    public Result setupData() {
-
-        setupDao.deleteAllData();
-
-        setupDao.generateWorldsForTest();
-        setupDao.generateFortunesForTest();
-
-        return Results.text().render("setup done");
-
-    }
-
-}

+ 0 - 69
frameworks/Java/ninja-standalone/src/main/java/dao/SetupDao.java

@@ -1,69 +0,0 @@
-package dao;
-
-import model.Fortune;
-
-import javax.persistence.EntityManager;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
-import model.World;
-
-/**
- * This class is just for testing. Has nothing to do with the TechEmpower test
- * itself...
- *
- * @author ra
- */
-@Singleton
-public class SetupDao {
-
-    @Inject
-    Provider<EntityManager> entitiyManagerProvider;
-
-    @Transactional
-    public void deleteAllData() {
-
-        entitiyManagerProvider.get().createQuery("DELETE FROM World");
-        entitiyManagerProvider.get().createQuery("DELETE FROM Fortune");
-
-    }
-
-    @Transactional
-    public void generateWorldsForTest() {
-
-        for (int i = 0; i < 10000; i++) {
-
-            World world = new World();
-            world.randomNumber = i; // not really a random number. But we can test with that...
-            entitiyManagerProvider.get().persist(world);
-
-        }
-
-    }
-
-    @Transactional
-    public void generateFortunesForTest() {
-
-        {
-
-            Fortune fortune = new Fortune();
-            // dummy message => just to make sure utf-8 works.
-            fortune.message = "レームワークのベンチマーク";
-            entitiyManagerProvider.get().persist(fortune);
-
-        }
-
-        {
-
-            Fortune fortune = new Fortune();
-            // dummy message => just to make sure utf-8 works.
-            fortune.message = "<script>I want to be escaped</script>";
-            entitiyManagerProvider.get().persist(fortune);
-
-        }
-
-    }
-
-}

+ 0 - 19
frameworks/Java/ninja-standalone/src/main/resources/META-INF/persistence.xml

@@ -4,25 +4,6 @@
              xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
              version="2.0">
 
-  <persistence-unit name="h2" transaction-type="RESOURCE_LOCAL">
-    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
-    <properties>
-      <property name="javax.persistence.provider" value="org.hibernate.jpa.HibernatePersistenceProvider" />
-      <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
-      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
-      <property name="hibernate.hbm2ddl.auto" value="create" />
-      <property name="hibernate.show_sql" value="false" />
-      <property name="hibernate.format_sql" value="false" />
-      <property name="hibernate.jdbc.batch_size" value="100" />
-      <property name="hibernate.connection.provider_class" value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider" />
-      <property name="hibernate.cache.use_query_cache" value="false" />
-      <property name="hibernate.minimumIdle" value="256" />
-      <property name="hibernate.maximumPoolSize" value="256" />
-      <property name="hibernate.idleTimeout" value="30000" />
-      <property name="hibernate.dataSourceClassName" value="org.h2.jdbcx.JdbcDataSource" />
-    </properties>
-  </persistence-unit>
-
   <persistence-unit name="mysql" transaction-type="RESOURCE_LOCAL">
     <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
     <properties>

+ 0 - 92
frameworks/Java/ninja-standalone/src/test/java/controllers/HelloDbControllerTest.java

@@ -1,92 +0,0 @@
-package controllers;
-
-import dao.SetupDao;
-import model.World;
-import ninja.NinjaDocTester;
-import org.doctester.testbrowser.Request;
-import org.doctester.testbrowser.Response;
-import org.hamcrest.CoreMatchers;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import org.junit.Test;
-import static org.junit.Assert.*;
-import org.junit.Before;
-
-public class HelloDbControllerTest extends NinjaDocTester {
-            
-    String URL_DB = "/db";
-    String URL_QUERIES = "/queries";
-    String URL_UPDATE = "/update";
-    
-    @Before
-    public void setupClass() {
-        getInjector().getInstance(SetupDao.class).generateWorldsForTest();
-    }
-    
-    @Test
-    public void testSingleGet() {
-        
-        Response response = makeRequest(
-                Request
-                        .GET()
-                        .url(testServerUrl().path(URL_DB))
-                        .contentTypeApplicationJson());
-        
-        // Just make sure that we get back a World Json.
-        assertThat(response.payloadAs(World.class), notNullValue());      
-                
-    }
-    
-    @Test
-    public void multipleQueries() {
-        
-        assertThatMutipleGetWorksFor(1);
-        assertThatMutipleGetWorksFor(5);
-        assertThatMutipleGetWorksFor(10);
-        assertThatMutipleGetWorksFor(15);
-        assertThatMutipleGetWorksFor(20);
-                
-    }
-    
-    private void assertThatMutipleGetWorksFor(int numberOfQueries) {
-        Response response = makeRequest(
-            Request
-                .GET()
-                .url(
-                    testServerUrl()
-                    .path(URL_QUERIES)
-                    .addQueryParameter("queries", numberOfQueries + ""))
-                .contentTypeApplicationJson());
-        
-        // Just make sure that we get back an array
-        assertThat(response.payloadAs(World[].class).length, is(numberOfQueries)); 
-    }
-    
-    @Test
-    public void testUpdates() {
-        
-        assertThatUpdateWorks(1);
-        assertThatUpdateWorks(5);
-        assertThatUpdateWorks(10);
-        assertThatUpdateWorks(15);
-        assertThatUpdateWorks(20);
-                
-    }
-    
-    private void assertThatUpdateWorks(int numberOfQueries) {
-        
-        Response response = makeRequest(
-            Request.GET()
-                .url(
-                    testServerUrl()
-                    .path(URL_UPDATE)
-                    .addQueryParameter("queries", numberOfQueries + ""))
-                .contentTypeApplicationJson());
-        
-        assertThat(response.payloadAs(World[].class).length, is(numberOfQueries)); 
-        
-    }
-
-    
-    
-}

+ 0 - 37
frameworks/Java/ninja-standalone/src/test/java/controllers/HelloFortuneControllerTest.java

@@ -1,37 +0,0 @@
-package controllers;
-
-import dao.SetupDao;
-import model.Fortune;
-import ninja.NinjaDocTester;
-import org.doctester.testbrowser.Request;
-import org.doctester.testbrowser.Response;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class HelloFortuneControllerTest extends NinjaDocTester {
-
-    String URL_FORTUNES = "/fortunes";
-    
-    @Test
-    public void testSomeMethod() {
-        
-        getInjector().getInstance(SetupDao.class).generateFortunesForTest();
-
-        Response response 
-                = makeRequest(Request.GET().url(testServerUrl().path(URL_FORTUNES)));
-        
-        System.out.println(" " + response.payload);
-        
-        // make sure escaping works
-        assertTrue(response.payload.contains("&lt;script&gt;I want to be escaped&lt;/script&gt;"));
-
-        // make sure utf-8 works
-        assertTrue(response.payload.contains("レームワークのベンチマーク<"));
-        
-        // make sure new Fortune has been added to response
-        assertTrue(response.payload.contains("Additional fortune added at request time."));
-
-        
-    }
-    
-}

+ 0 - 48
frameworks/Java/ninja-standalone/src/test/java/controllers/HelloJsonControllerTest.java

@@ -1,48 +0,0 @@
-package controllers;
-
-import ninja.NinjaDocTester;
-import org.doctester.testbrowser.Request;
-import org.doctester.testbrowser.Response;
-import org.hamcrest.CoreMatchers;
-import static org.hamcrest.CoreMatchers.is;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-
-public class HelloJsonControllerTest extends NinjaDocTester {
-    
-    String URL_JSON = "/json";
-
-    @Test
-    public void testHelloJsonController() {
-        
-        Response response = makeRequest(
-            Request
-                .GET()
-                .url(testServerUrl().path(URL_JSON))
-                .contentTypeApplicationJson());
-        
-        assertThat(
-            response.payloadAs(Message.class).message, 
-            is("Hello, World!"));
-        
-    }
-    
-    /**
-     * Duplicated from HelloJsonController.
-     * 
-     * Stuff in HelloJsonController is final, but to deserialize the message we
-     * need an empty constructor...
-     */
-    public final static class Message {
-
-	public String message;
-        
-	public Message() {}
-
-	public Message(String message) {
-	    this.message = message;
-	}
-    }
-    
-}

+ 0 - 29
frameworks/Java/ninja-standalone/src/test/java/controllers/HelloPlaintextControllerTest.java

@@ -1,29 +0,0 @@
-package controllers;
-
-import ninja.NinjaDocTester;
-import org.doctester.testbrowser.Request;
-import org.doctester.testbrowser.Response;
-import org.hamcrest.CoreMatchers;
-import static org.hamcrest.CoreMatchers.is;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class HelloPlaintextControllerTest extends NinjaDocTester {
-    
-    String URL_PLAINTEXT = "/plaintext";
-    
-    @Test
-    public void helloPlaintextControllerTest() {
-        
-        Response response = makeRequest(
-            Request.GET().url(testServerUrl().path(URL_PLAINTEXT)));
-        
-        assertThat(response.payload, CoreMatchers.is("Hello, World!"));
-        assertThat(
-            response.headers.get("Content-Type"), 
-            is("text/plain;charset=utf-8"));
-        
-   
-    }
-    
-}

+ 2 - 2
frameworks/Java/restexpress/pom.xml

@@ -43,8 +43,8 @@
 				<artifactId>maven-compiler-plugin</artifactId>
 				<version>3.0</version>
 				<configuration>
-					<source>1.7</source>
-					<target>1.7</target>
+					<source>1.8</source>
+					<target>1.8</target>
 					<encoding>UTF-8</encoding>
 				</configuration>
 			</plugin>

+ 10 - 1
frameworks/Java/restexpress/src/main/java/hello/Main.java

@@ -6,6 +6,9 @@ import hello.controller.JsonController.HelloWorld;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import org.jboss.netty.handler.codec.http.HttpMethod;
 
 import com.strategicgains.restexpress.RestExpress;
@@ -25,7 +28,8 @@ public class Main
 			.action("helloWorld", HttpMethod.GET);
 
                 server.uri("/restexpress/plaintext", config.getPlaintextController())
-                        .action("helloWorld", HttpMethod.GET);
+                        .action("helloWorld", HttpMethod.GET)
+                        .noSerialization();
 
 		server.uri("/restexpress/mysql", config.getMysqlController())
 			.method(HttpMethod.GET);
@@ -33,6 +37,11 @@ public class Main
 		server.uri("/restexpress/mongodb", config.getMongodbController())
 		    .method(HttpMethod.GET);
 
+		server.addPostprocessor((request, response) -> {
+			response.addHeader("Server", "RestExpress");
+			response.addHeader("Date", DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));
+		});
+
 		server.bind(config.getPort());
 		server.awaitShutdown();
 	}

+ 1 - 3
frameworks/Java/restexpress/src/main/java/hello/controller/JsonController.java

@@ -5,11 +5,9 @@ import com.strategicgains.restexpress.Response;
 
 public class JsonController
 {
-	private static final HelloWorld MESSAGE_OBJECT = new HelloWorld();
-
 	public HelloWorld helloWorld(Request request, Response response)
 	{
-		return MESSAGE_OBJECT;
+		return new HelloWorld();
 	}
 
 	/**

+ 1 - 0
frameworks/Java/restexpress/src/main/java/hello/controller/PlaintextController.java

@@ -9,6 +9,7 @@ public class PlaintextController
 
 	public String helloWorld(Request request, Response response)
 	{
+		response.setContentType("text/plain");
 		return MESSAGE;
 	}
 }

+ 1 - 0
frameworks/PHP/yii2/app/controllers/SiteController.php

@@ -125,6 +125,7 @@ EOM;
     }
 
     public function actionPlaintext() {
+        Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
         header("Content-Type: text/plain");
         echo 'Hello, World!';
     }

+ 11 - 0
frameworks/Python/bottle/app.py

@@ -1,6 +1,7 @@
 from functools import partial
 from operator import attrgetter, itemgetter
 from random import randint
+from email.utils import formatdate
 import os
 import sys
 
@@ -56,6 +57,7 @@ class Fortune(Base):
 
 @app.route("/json")
 def hello():
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     response.content_type = 'application/json'
     resp = {"message": "Hello, World!"}
     return json.dumps(resp)
@@ -64,6 +66,7 @@ def hello():
 @app.route("/db")
 def get_random_world_single(db):
     """Test Type 2: Single Database Query"""
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     wid = randint(1, 10000)
     world = db.query(World).get(wid).serialize()
     response.content_type = 'application/json'
@@ -72,6 +75,7 @@ def get_random_world_single(db):
 
 @app.route("/raw-db")
 def get_random_world_single_raw():
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     connection = raw_engine.connect()
     wid = randint(1, 10000)
     try:
@@ -86,6 +90,7 @@ def get_random_world_single_raw():
 @app.route("/queries")
 def get_random_world(db):
     """Test Type 3: Multiple database queries"""
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = request.query.get('queries', 1, type=int)
     if num_queries < 1:
         num_queries = 1
@@ -100,6 +105,7 @@ def get_random_world(db):
 
 @app.route("/raw-queries")
 def get_random_world_raw():
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = request.query.get('queries', 1, type=int)
     if num_queries < 1:
         num_queries = 1
@@ -120,6 +126,7 @@ def get_random_world_raw():
 
 @app.route("/fortune")
 def fortune_orm(db):
+  response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
   fortunes=db.query(Fortune).all()
   fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
   fortunes.sort(key=attrgetter('message'))
@@ -128,6 +135,7 @@ def fortune_orm(db):
 
 @app.route("/raw-fortune")
 def fortune_raw():
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     connection = raw_engine.connect()
     try:
         fortunes=[(f.id, f.message) for f in connection.execute("SELECT * FROM Fortune")]
@@ -141,6 +149,7 @@ def fortune_raw():
 @app.route("/updates")
 def updates(db):
     """Test 5: Database Updates"""
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = request.query.get('queries', 1, type=int)
     if num_queries < 1:
         num_queries = 1
@@ -163,6 +172,7 @@ def updates(db):
 @app.route("/raw-updates")
 def raw_updates():
     """Test 5: Database Updates"""
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = request.query.get('queries', 1, type=int)
     if num_queries < 1:
         num_queries = 1
@@ -187,6 +197,7 @@ def raw_updates():
 @app.route('/plaintext')
 def plaintext():
     """Test 6: Plaintext"""
+    response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     response.content_type = 'text/plain'
     return b'Hello, World!'
 

+ 8 - 5
frameworks/Python/bottle/bottle-nginx-uwsgi.dockerfile

@@ -1,16 +1,19 @@
 FROM python:3.6.5
 
+WORKDIR /bottle
+COPY views views
+COPY app.py app.py
+COPY nginx.conf nginx.conf
+COPY requirements.txt requirements.txt
+COPY uwsgi.ini uwsgi.ini
+
 RUN curl -s http://nginx.org/keys/nginx_signing.key | apt-key add -
 RUN echo "deb http://nginx.org/packages/debian/ jessie nginx" >> /etc/apt/sources.list
 RUN echo "deb-src http://nginx.org/packages/debian/ jessie nginx" >> /etc/apt/sources.list
 
 RUN apt update -yqq && apt install -yqq nginx
 
-ADD ./ /bottle
-
-WORKDIR /bottle
-
-RUN pip3 install -r /bottle/requirements.txt
+RUN pip3 install -r requirements.txt
 
 RUN sed -i 's|include .*/conf/uwsgi_params;|include /etc/nginx/uwsgi_params;|g' /bottle/nginx.conf
 

+ 5 - 3
frameworks/Python/bottle/bottle-pypy2.dockerfile

@@ -1,9 +1,11 @@
 FROM pypy:2-5.10
 
-ADD ./ /bottle
-
 WORKDIR /bottle
+COPY views views
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements-pypy.txt requirements-pypy.txt
 
-RUN pip install -r /bottle/requirements-pypy.txt
+RUN pip install -r requirements-pypy.txt
 
 CMD gunicorn app:app -c gunicorn_conf.py

+ 5 - 3
frameworks/Python/bottle/bottle-raw.dockerfile

@@ -1,9 +1,11 @@
 FROM python:3.6.5
 
-ADD ./ /bottle
-
 WORKDIR /bottle
+COPY views views
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements.txt requirements.txt
 
-RUN pip3 install -r /bottle/requirements.txt
+RUN pip3 install -r requirements.txt
 
 CMD gunicorn app:app -c gunicorn_conf.py

+ 5 - 3
frameworks/Python/bottle/bottle.dockerfile

@@ -1,9 +1,11 @@
 FROM python:3.6.5
 
-ADD ./ /bottle
-
 WORKDIR /bottle
+COPY views views
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements.txt requirements.txt
 
-RUN pip3 install -r /bottle/requirements.txt
+RUN pip3 install -r requirements.txt
 
 CMD gunicorn app:app -c gunicorn_conf.py

+ 3 - 0
frameworks/Python/falcon/app.py

@@ -3,17 +3,20 @@ import json
 
 import falcon
 
+from email.utils import formatdate
 
 # resource endpoints
 
 class JSONResource(object):
     def on_get(self, request, response):
+        response.set_header('Date', formatdate(timeval=None, localtime=False, usegmt=True))
         json_data = {'message': "Hello, world!"}
         response.body = json.dumps(json_data)
 
 
 class PlaintextResource(object):
     def on_get(self, request, response):
+        response.set_header('Date', formatdate(timeval=None, localtime=False, usegmt=True))
         response.set_header('Content-Type', 'text/plain')
         response.body = b'Hello, world!'
 

+ 5 - 4
frameworks/Python/falcon/falcon-py3.dockerfile

@@ -1,9 +1,10 @@
 FROM python:3.6.5
 
-ADD ./ /falcon
-
 WORKDIR /falcon
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements.txt requirements.txt
 
-RUN pip3 install -r /falcon/requirements.lock
+RUN pip3 install -r requirements.txt
 
-CMD gunicorn app:app -c gunicorn_conf.py
+CMD ["gunicorn", "app:app", "-c", "gunicorn_conf.py"]

+ 5 - 4
frameworks/Python/falcon/falcon-pypy2.dockerfile

@@ -1,9 +1,10 @@
 FROM pypy:2-5.10
 
-ADD ./ /falcon
-
 WORKDIR /falcon
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements-pypy.txt requirements-pypy.txt
 
-RUN pip install -r /falcon/requirements-pypy.lock
+RUN pip install -r requirements-pypy.txt
 
-CMD gunicorn app:app -c gunicorn_conf.py
+CMD ["gunicorn", "app:app", "-c", "gunicorn_conf.py"]

+ 5 - 4
frameworks/Python/falcon/falcon.dockerfile

@@ -1,9 +1,10 @@
 FROM python:2.7.14
 
-ADD ./ /falcon
-
 WORKDIR /falcon
+COPY app.py app.py
+COPY gunicorn_conf.py gunicorn_conf.py
+COPY requirements.txt requirements.txt
 
-RUN pip install -r /falcon/requirements.lock
+RUN pip install -r requirements.txt
 
-CMD gunicorn app:app -c gunicorn_conf.py
+CMD ["gunicorn", "app:app", "-c", "gunicorn_conf.py"]

+ 0 - 7
frameworks/Python/falcon/requirements-pypy.lock

@@ -1,7 +0,0 @@
-falcon==1.4.1
-greenlet==0.4.12
-gunicorn==19.7.1
-python-mimeparse==1.6.0
-readline==6.2.4.1
-six==1.11.0
-tornado==4.5.3

+ 7 - 3
frameworks/Python/falcon/requirements-pypy.txt

@@ -1,3 +1,7 @@
-gunicorn
-tornado
-falcon
+falcon==1.4.1
+greenlet==0.4.12
+gunicorn==19.7.1
+python-mimeparse==1.6.0
+readline==6.2.4.1
+six==1.11.0
+tornado==4.5.3

+ 0 - 7
frameworks/Python/falcon/requirements.lock

@@ -1,7 +0,0 @@
-Cython==0.27.3
-falcon==1.4.1
-greenlet==0.4.13
-gunicorn==19.7.1
-meinheld==0.6.1
-python-mimeparse==1.6.0
-six==1.11.0

+ 7 - 5
frameworks/Python/falcon/requirements.txt

@@ -1,5 +1,7 @@
-Cython
-falcon
-greenlet
-gunicorn
-meinheld
+Cython==0.27.3
+falcon==1.4.1
+greenlet==0.4.13
+gunicorn==19.7.1
+meinheld==0.6.1
+python-mimeparse==1.6.0
+six==1.11.0

+ 11 - 5
frameworks/Python/flask/app.py

@@ -5,6 +5,7 @@ from operator import attrgetter
 import os
 from random import randint
 import sys
+from email.utils import formatdate
 
 import flask
 from flask import Flask, request, render_template, make_response, jsonify
@@ -68,12 +69,17 @@ class Fortune(db.Model):
 def json_response(obj):
     res = make_response(json.dumps(obj))
     res.mimetype = "application/json"
+    return add_date_header(res)
+
+
+def add_date_header(res):
+    res.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
     return res
 
 
 @app.route("/json")
 def hello():
-    return jsonify(message='Hello, World!')
+    return add_date_header(jsonify(message='Hello, World!'))
 
 
 @app.route("/db")
@@ -126,16 +132,16 @@ def get_fortunes():
     fortunes = list(Fortune.query.all())
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=attrgetter('message'))
-    return render_template('fortunes.html', fortunes=fortunes)
+    return add_date_header(make_response(render_template('fortunes.html', fortunes=fortunes)))
 
 @app.route("/fortunesraw")
-def get_forutens_raw():
+def get_fortunes_raw():
     res = dbraw_engine.execute("SELECT * FROM fortune")
     fortunes = res.fetchall()
     res.close()
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=attrgetter('message'))
-    return render_template('fortunes.html', fortunes=fortunes)
+    return add_date_header(make_response(render_template('fortunes.html', fortunes=fortunes)))
 
 
 @app.route("/updates")
@@ -188,7 +194,7 @@ def plaintext():
     """Test 6: Plaintext"""
     response = make_response(b'Hello, World!')
     response.content_type = 'text/plain'
-    return response
+    return add_date_header(response)
 
 
 try:

+ 4 - 4
frameworks/Python/pyramid/frameworkbenchmarks/views.py

@@ -17,17 +17,17 @@ def test_1(request):
     return {"message":"Hello, World!"}
 
 
-@view_config(route_name='test_2', renderer='sqla_json')
+@view_config(route_name='test_2', renderer='json')
 def test_2(request):
     """
     Test type 2: Single database query
     """
     num = randint(1, 10000)
     result = DBSession.query(World).filter(World.id == num).one()
-    return result
+    return result.__json__()
 
 
-@view_config(route_name='test_3', renderer='sqla_json')
+@view_config(route_name='test_3', renderer='json')
 def test_3(request):
     """
     Test type 3: Multiple database queries
@@ -46,7 +46,7 @@ def test_3(request):
         DBSession.query(World).filter(World.id == num).one()
         for num in [randint(1, 10000) for _ in range(1, queries + 1)]
     ]
-    return result
+    return [obj.__json__() for obj in result]
 
 
 @view_config(route_name='test_4', renderer='templates/test_4.pt')

+ 4 - 1
frameworks/Python/uwsgi/hello.py

@@ -1,4 +1,5 @@
 import ujson
+from email.utils import formatdate
 
 
 def application(environ, start_response):
@@ -7,7 +8,9 @@ def application(environ, start_response):
     }
     data = ujson.dumps(response)
     response_headers = [
-        ('Content-type', 'application/json'),
+        ('Server', 'uwsgi'),
+        ('Date', formatdate(timeval=None, localtime=False, usegmt=True)),
+        ('Content-Type', 'application/json'),
         ('Content-Length', str(len(data)))
     ]
     start_response('200 OK', response_headers)

+ 7 - 0
frameworks/Python/weppy/app.py

@@ -5,6 +5,7 @@ from random import randint
 from weppy import App, request, response
 from weppy.orm import Database, Model, Field, rowmethod
 from weppy.tools import service
+from email.utils import formatdate
 
 _is_pypy = hasattr(sys, 'pypy_version_info')
 if sys.version_info[0] == 3:
@@ -49,12 +50,14 @@ db.define_models(World, Fortune)
 @app.route()
 @service.json
 def json():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     return {'message': 'Hello, World!'}
 
 
 @app.route("/db", pipeline=[db.pipe])
 @service.json
 def get_random_world():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     return World.get(randint(1, 10000)).serialize()
 
 
@@ -73,6 +76,7 @@ def get_qparam():
 @app.route("/queries", pipeline=[db.pipe])
 @service.json
 def get_random_worlds():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = get_qparam()
     worlds = [
         World.get(randint(1, 10000)).serialize() for _ in xrange(num_queries)]
@@ -81,6 +85,7 @@ def get_random_worlds():
 
 @app.route(pipeline=[db.pipe])
 def fortunes():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     fortunes = Fortune.all().select()
     fortunes.append(
         Fortune.new(id=0, message="Additional fortune added at request time."))
@@ -91,6 +96,7 @@ def fortunes():
 @app.route(pipeline=[db.pipe])
 @service.json
 def updates():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = get_qparam()
     worlds = []
     rp = partial(randint, 1, 10000)
@@ -105,6 +111,7 @@ def updates():
 
 @app.route()
 def plaintext():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     response.headers["Content-Type"] = "text/plain"
     return 'Hello, World!'
 

+ 1 - 0
frameworks/Ruby/hanami/apps/web/controllers/hello_world/db.rb

@@ -3,6 +3,7 @@ module Web::Controllers::HelloWorld
     include Web::Action
 
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       status 200, WorldRepository.new.find_random_entity.to_h.to_json
     end
   end

+ 1 - 0
frameworks/Ruby/hanami/apps/web/controllers/hello_world/json.rb

@@ -3,6 +3,7 @@ module Web::Controllers::HelloWorld
     include Web::Action
 
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       status 200, {:message => "Hello, World!"}.to_json
     end
   end

+ 1 - 0
frameworks/Ruby/hanami/apps/web/controllers/hello_world/plaintext.rb

@@ -4,6 +4,7 @@ module Web::Controllers::HelloWorld
 
     def call(params)
       self.format = :txt
+      self.headers.merge!({ 'Content-Type' => 'text/plain' })
       status 200, 'Hello, World!'
     end
   end

+ 1 - 0
frameworks/Ruby/hanami/apps/web/controllers/hello_world/query.rb

@@ -3,6 +3,7 @@ module Web::Controllers::HelloWorld
     include Web::Action
 
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       queries = params[:queries].to_i
       queries = 1 if queries < 1
       queries = 500 if queries > 500

+ 1 - 0
frameworks/Ruby/hanami/apps/web/controllers/hello_world/update.rb

@@ -3,6 +3,7 @@ module Web::Controllers::HelloWorld
     include Web::Action
 
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       queries = (params[:queries] || 1).to_i
       queries = 1 if queries < 1
       queries = 500 if queries > 500

+ 16 - 4
frameworks/Ruby/padrino/app/controllers.rb

@@ -1,14 +1,20 @@
 HelloWorld::App.controllers  do
-  get '/json', :provides => [:js] do
+  get '/json', :provides => [:json] do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     {message: "Hello, World!"}.to_json
   end
 
-  get '/db', :provides => [:js] do
+  get '/db', :provides => [:json] do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     id = Random.rand(10000) + 1
     World.get(id).attributes.to_json
   end
 
-  get '/queries', :provides => [:js] do
+  get '/queries', :provides => [:json] do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     queries = params['queries'].to_i
     queries = 1 if queries < 1
     queries = 500 if queries > 500
@@ -19,6 +25,8 @@ HelloWorld::App.controllers  do
   end
 
   get '/fortunes' do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     @fortunes = Fortune.all
     @fortunes << Fortune.new(:id => 0, :message => "Additional fortune added at request time.")
     @fortunes = @fortunes.sort_by { |x| x.message }
@@ -26,7 +34,9 @@ HelloWorld::App.controllers  do
     render 'fortunes', layout: "layout"
   end
 
-  get '/updates', :provides => [:js] do
+  get '/updates', :provides => [:json] do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     queries = params['queries'].to_i
     queries = 1 if queries < 1
     queries = 500 if queries > 500
@@ -43,6 +53,8 @@ HelloWorld::App.controllers  do
   end
 
   get '/plaintext' do
+    response.headers['Server'] = 'padrino'
+    response.headers['Date'] = Time.now.httpdate
     content_type 'text/plain'
     "Hello, World!"
   end

+ 1 - 2
frameworks/Ruby/padrino/config/database.rb

@@ -12,7 +12,6 @@
 DataMapper.logger = logger
 DataMapper::Property::String.length(255)
 
-host = ENV['DB_HOST'] || 'localhost'
 case Padrino.env
-  when :production  then DataMapper.setup(:default, "mysql://benchmarkdbuser:benchmarkdbpass@#{host}/hello_world")
+  when :production  then DataMapper.setup(:default, "mysql://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world")
 end

+ 10 - 5
frameworks/Ruby/padrino/padrino-unicorn.dockerfile

@@ -1,12 +1,17 @@
 FROM ruby:2.4
 
-RUN apt update -yqq && apt install -yqq nginx
-
-ADD ./ /padrino
-
 WORKDIR /padrino
+COPY app app
+COPY config config
+COPY models models
+COPY .components .components
+COPY config.ru config.ru
+COPY Gemfile Gemfile
+COPY Rakefile Rakefile
 
 RUN bundle install --jobs=4 --gemfile=/padrino/Gemfile --path=/padrino/padrino/bundle
 
+RUN apt update -yqq && apt install -yqq nginx
+
 CMD nginx -c /padrino/config/nginx.conf && \
-    DB_HOST=tfb-database bundle exec unicorn -E production -c config/unicorn.rb
+    bundle exec unicorn -E production -c config/unicorn.rb

+ 8 - 3
frameworks/Ruby/padrino/padrino.dockerfile

@@ -1,9 +1,14 @@
 FROM ruby:2.4
 
-ADD ./ /padrino
-
 WORKDIR /padrino
+COPY app app
+COPY config config
+COPY models models
+COPY .components .components
+COPY config.ru config.ru
+COPY Gemfile Gemfile
+COPY Rakefile Rakefile
 
 RUN bundle install --jobs=4 --gemfile=/padrino/Gemfile --path=/padrino/padrino/bundle
 
-CMD DB_HOST=tfb-database bundle exec puma -C config/puma.rb -w 8 --preload
+CMD ["bundle", "exec", "puma", "-C", "config/puma.rb", "-w", "8", "--preload"]

+ 3 - 3
frameworks/Rust/rouille/rouille.dockerfile

@@ -1,9 +1,9 @@
 FROM rust:1.25
 
-ADD ./ /rouille
 WORKDIR /rouille
+COPY src src
+COPY Cargo.toml Cargo.toml
 
-RUN cargo clean
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 
-CMD ./target/release/rouille
+CMD ["./target/release/rouille"]

+ 2 - 2
frameworks/Rust/rouille/src/main.rs

@@ -5,11 +5,11 @@ fn main() {
     rouille::start_server("0.0.0.0:8080", move |req| {
         router!(req,
                 (GET) (/plaintext) => {
-                    rouille::Response::text("Hello, World!")
+                    rouille::Response::from_data("text/plain", "Hello, World!")
                 },
                 (GET) (/json) => {
                     let json = json!({"message": "Hello, World!"});
-                    rouille::Response::text(json.to_string())
+                    rouille::Response::from_data("application/json", json.to_string())
                 },
                 _ => rouille::Response::empty_404()
             )

+ 28 - 39
toolset/benchmark/test_types/verifications.py

@@ -2,6 +2,7 @@ import json
 import re
 import traceback
 
+from datetime import datetime
 from toolset.utils.output_helper import log
 
 
@@ -41,58 +42,46 @@ def verify_headers(headers, url, should_be='json'):
     param `should_be` is a switch for the three acceptable content types
     '''
 
-    types = {
-        'json': 'application/json',
-        'html': 'text/html',
-        'plaintext': 'text/plain'
-    }
-    expected_type = types[should_be]
-
     problems = []
 
     for v in (v for v in ('Server', 'Date', 'Content-Type')
               if v.lower() not in headers):
-        problems.append(('warn', 'Required response header missing: %s' % v,
+        problems.append(('fail', 'Required response header missing: %s' % v,
                          url))
 
     if all(v.lower() not in headers
            for v in ('Content-Length', 'Transfer-Encoding')):
         problems.append((
-            'warn',
+            'fail',
             'Required response size header missing, please include either "Content-Length" or "Transfer-Encoding"',
             url))
 
-    content_type = headers.get('Content-Type', None)
+    date = headers.get('Date')
+    if date is not None:
+        expected_date_format = '%a, %d %b %Y %H:%M:%S %Z'
+        try:
+            datetime.strptime(date, expected_date_format)
+        except ValueError:
+            problems.append((
+                'warn',
+                'Invalid Date header, found \"%s\", did not match \"%s\".'
+                % (date, expected_date_format), url))
+
+    content_type = headers.get('Content-Type')
+    if content_type is not None:
+        types = {
+            'json':      '^application/json(; ?charset=(UTF|utf)-8)?$',
+            'html':      '^text/html; ?charset=(UTF|utf)-8$',
+            'plaintext': '^text/plain(; ?charset=(UTF|utf)-8)?$'
+        }
+        expected_type = types[should_be]
+
+        if not re.match(expected_type, content_type):
+            problems.append((
+                'fail',
+                'Invalid Content-Type header, found \"%s\", did not match \"%s\".'
+                % (content_type, expected_type), url))
 
-    if content_type is None:
-        problems.append(('warn', 'No content encoding found, expected \"%s\"' %
-                         (expected_type), url))
-    else:
-        # Split out "charset=utf-8" if it's included
-        content_type_list = re.split('; *', content_type.lower())
-        charset = 'charset=utf-8'
-        # "text/html" requires charset to be set. The others do not
-        if expected_type == types['html']:
-            if expected_type not in content_type_list:
-                problems.append((
-                    'warn',
-                    'Unexpected content encoding, found \"%s\", expected \"%s\".'
-                    % (content_type, expected_type + '; ' + charset), url))
-            elif charset not in content_type_list:
-                problems.append(('warn', (
-                    'The \"%s\" content type requires \"charset=utf-8\" to be specified.'
-                    % expected_type), url))
-        else:
-            if expected_type not in content_type_list:
-                problems.append((
-                    'warn',
-                    'Unexpected content encoding, found \"%s\", expected \"%s\"'
-                    % (content_type, expected_type), url))
-            elif charset in content_type_list:
-                problems.append(('warn', (
-                    "Content encoding found in \"%s\" where \"%s\" is acceptable.\n"
-                    "Additional response bytes may negatively affect benchmark performance."
-                    % (content_type, expected_type)), url))
     return problems