Browse Source

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 7 years ago
parent
commit
eb67470bd1
76 changed files with 262 additions and 733 deletions
  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": {
     "service": {
         "api": "http",
         "api": "http",
         "ip": "0.0.0.0",
         "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": {
     "service": {
         "api": "fastcgi",
         "api": "fastcgi",
         "socket": "/var/tmp/cppcms.sock"
         "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": {
     "service": {
         "api": "fastcgi",
         "api": "fastcgi",
         "socket": "/var/tmp/cppcms.sock"
         "socket": "/var/tmp/cppcms.sock"

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

@@ -12,6 +12,7 @@ void WorldController::index()
 
 
 void WorldController::plain()
 void WorldController::plain()
 {
 {
+    setContentType("text/plain");
     renderText(QLatin1String("Hello, World!"));
     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
 FROM ubuntu:16.04
 
 
-ADD ./ /octane
-WORKDIR /octane
-
 RUN apt update -yqq && \
 RUN apt update -yqq && \
     apt install -yqq software-properties-common build-essential git cmake automake libtool wget
     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_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);
 	onion_connection_status ret=fortunes_html_template(context, req, res);
 	free(fortune_list.list);
 	free(fortune_list.list);
 	return ret;
 	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.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
             fortunes.Sort();
             fortunes.Sort();
-            
+
+            Response.Charset = "utf-8";
             return View("Fortunes", fortunes);
             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.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
             fortunes.Sort();
             fortunes.Sort();
 
 
+            Response.Charset = "utf-8";
             return View("Fortunes", fortunes);
             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)
 		private static void Handler(EventHttpRequest req)
 		{
 		{
 			var headers = new Dictionary<string, string>();
 			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();
 				var sw = new StringWriter();
 				Serializer.Serialize(sw, new {message = "Hello, World!"});
 				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 =>
       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
   before_action do
     all do
     all do
       response.headers["Server"] = "Amber"
       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
   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
   request = context.request
 
 
   response.headers["Server"] = "Crystal"
   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
   case request.path
   when "/json"
   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
   request = context.request
   response = context.response
   response = context.response
   response.headers["Server"] = "Crystal"
   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)
   result = tree.find(request.path)
 
 

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

@@ -44,7 +44,7 @@ end
 
 
 before_all do |env|
 before_all do |env|
   env.response.headers["Server"] = "Kemal"
   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
 end
 
 
 #
 #

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

@@ -37,7 +37,7 @@ class IndexController : Controller
 	@Action void plaintext()
 	@Action void plaintext()
 	{
 	{
 		res.setHeader("Date",printDate);
 		res.setHeader("Date",printDate);
-		res.html("Hello, World!");
+		res.plain("Hello, World!");
 	}
 	}
 
 
 	private string printDate() {
 	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) {
   Future<String> fortunesTest(@app.Inject() mustache.Template template) {
     return pgSql.query(fortuneQuery, Fortune).then((values) {
     return pgSql.query(fortuneQuery, Fortune).then((values) {
       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) {
   Future<String> fortunesTest(@app.Inject() mustache.Template template) {
     return mongoDb.find(fortuneCollection, MongoFortune).then((values) {
     return mongoDb.find(fortuneCollection, MongoFortune).then((values) {
       values
       values

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

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

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

@@ -12,13 +12,13 @@ handle(Req, _Args) ->
 handle('GET', [<<"plaintext">>], _Req) ->
 handle('GET', [<<"plaintext">>], _Req) ->
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% to signal success.
     %% to signal success.
-    {ok,[{<<"Content-Type">>, <<"text/plain">>}], <<"Hello, World!">>};
+    {ok, [{<<"Server">>, <<"elli">>}, {<<"Content-Type">>, <<"text/plain">>}], <<"Hello, World!">>};
 
 
 %% Json test route
 %% Json test route
 handle('GET',[<<"json">>], _Req) ->
 handle('GET',[<<"json">>], _Req) ->
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% Reply with a normal response. 'ok' can be used instead of '200'
     %% to signal success.
     %% 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)
 %% db test route (Single Database Query)
 handle('GET',[<<"db">>], Req) ->
 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) ]],
 			        {result_packet, _, _, [[ID, Rand]], _} <- [emysql:execute(test_pool, db_stmt, [random:uniform(10000)]) || _ <- lists:seq(1, I) ]],
 			Res
 			Res
 		end,
 		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.
 %% TODO : Finish this function with correct logic.
 %%        Please check TFB document
 %%        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()
 			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 falcore.SimpleResponse(req.HttpRequest, 200, textHtml, -1, pipeReader)
 	}
 	}
 	return nil
 	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})
 	fortunes = append(fortunes, &Fortune{Message: extraFortuneMessage})
 
 
 	sort.Sort(ByMessage{fortunes})
 	sort.Sort(ByMessage{fortunes})
-	setContentType(w, "text/html")
+	setContentType(w, "text/html;charset=utf-8")
 	if err := tmpl.Execute(w, fortunes); err != nil {
 	if err := tmpl.Execute(w, fortunes); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		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."})
 	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
 
 
 	sort.Sort(ByMessage{fortunes})
 	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 {
 	if err := tmpl.Execute(w, fortunes); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		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() {
 func init() {
 	revel.Filters = []revel.Filter{
 	revel.Filters = []revel.Filter{
+		ServerHeaderFilter,
 		revel.RouterFilter,
 		revel.RouterFilter,
 		revel.ParamsFilter,
 		revel.ParamsFilter,
 		revel.ActionInvoker,
 		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 {
 type App struct {
 	*revel.Controller
 	*revel.Controller
 }
 }

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

@@ -12,10 +12,11 @@ type MessageStruct struct {
 	Message string `json:"message"`
 	Message string `json:"message"`
 }
 }
 
 
-func hello(val string) string {
+func hello(ctx *web.Context, val string) {
 	m := MessageStruct{"Hello, World!"}
 	m := MessageStruct{"Hello, World!"}
 	j, _ := json.Marshal(m)
 	j, _ := json.Marshal(m)
-	return string(j)
+	ctx.ContentType("application/json")
+	ctx.Write(j)
 }
 }
 
 
 func main() {
 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()
         Act.getNonblock("/json", context -> context.resp()
                 .contentType(H.Format.JSON.contentType())
                 .contentType(H.Format.JSON.contentType())
                 .writeContent(JSON.toJSONString(new Message(HELLO_WORLD))));
                 .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("/queries",DbController.class, DbController::getQueries);
         get("/updates",DbController.class, DbController::getUpdates);
         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() {
         use(new AbstractModule() {
             @Override
             @Override

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

@@ -14,21 +14,21 @@ public class DbController extends Controller {
     
     
     // /db
     // /db
     public void index() {
     public void index() {
-        respond().json(db.getWorld(Helper.getRandomNumber()));
+        respond().json(db.getWorld(Helper.getRandomNumber())).addHeader("Server", "jawn");
     }
     }
     
     
     // /queries?queries=
     // /queries?queries=
     public void getQueries() {
     public void getQueries() {
         int param = parseQueryParam();
         int param = parseQueryParam();
-        
-        respond().json(db.getWorlds(param));
+
+        respond().json(db.getWorlds(param)).addHeader("Server", "jawn");
     }
     }
     
     
     // /updates?queries=
     // /updates?queries=
     public void getUpdates() {
     public void getUpdates() {
         int param = parseQueryParam();
         int param = parseQueryParam();
-        
-        respond().json(db.getAndUpdateWorlds(param));
+
+        respond().json(db.getAndUpdateWorlds(param)).addHeader("Server", "jawn");
     }
     }
     
     
     private int parseQueryParam() {
     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();
         List<Fortune> fortunes = db.fetchAllFortunes();
         fortunes.add(new Fortune(0, "Additional fortune added at request time."));
         fortunes.add(new Fortune(0, "Additional fortune added at request time."));
         Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
         Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+        header("Server", "jawn");
         view("fortunes", fortunes);
         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();
         StringWriter writer = new StringWriter();
         mustache.execute(writer, fortunes);
         mustache.execute(writer, fortunes);
         exchange.getResponseHeaders().put(
         exchange.getResponseHeaders().put(
-                Headers.CONTENT_TYPE, "text/html");
+                Headers.CONTENT_TYPE, "text/html;charset=utf-8");
         exchange.getResponseSender().send(writer.toString());
         exchange.getResponseSender().send(writer.toString());
     }
     }
 }
 }

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

@@ -9,8 +9,8 @@
 
 
     <properties>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <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-assembly-plugin.version>3.1.0</maven-assembly-plugin.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-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-enforcer-plugin.version>1.4.1</maven-enforcer-plugin.version>
         <maven-war-plugin.version>3.2.0</maven-war-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>
     </properties>
 
 
     <dependencies>
     <dependencies>
@@ -39,13 +39,6 @@
             <groupId>org.ninjaframework</groupId>
             <groupId>org.ninjaframework</groupId>
             <artifactId>ninja-standalone</artifactId>
             <artifactId>ninja-standalone</artifactId>
             <version>${ninja.version}</version>
             <version>${ninja.version}</version>
-        </dependency>   
-
-        <dependency>
-            <groupId>org.ninjaframework</groupId>
-            <artifactId>ninja-test-utilities</artifactId>
-            <version>${ninja.version}</version>
-            <scope>test</scope>
         </dependency>
         </dependency>
 
 
         <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.HelloFortuneController;
 import controllers.HelloJsonController;
 import controllers.HelloJsonController;
 import controllers.HelloPlaintextController;
 import controllers.HelloPlaintextController;
-import controllers.SetupController;
 import ninja.Router;
 import ninja.Router;
 import ninja.application.ApplicationRoutes;
 import ninja.application.ApplicationRoutes;
 import ninja.utils.NinjaProperties;
 import ninja.utils.NinjaProperties;
@@ -25,11 +24,5 @@ public class Routes implements ApplicationRoutes {
         router.GET().route("/fortunes").with(HelloFortuneController.class, "index");
         router.GET().route("/fortunes").with(HelloFortuneController.class, "index");
         router.GET().route("/update").with(HelloDbController.class, "update");
         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.username=benchmarkdbuser
 %prod.db.connection.password=benchmarkdbpass
 %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
 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"
              xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
              version="2.0">
              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">
   <persistence-unit name="mysql" transaction-type="RESOURCE_LOCAL">
     <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
     <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
     <properties>
     <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>
 				<artifactId>maven-compiler-plugin</artifactId>
 				<version>3.0</version>
 				<version>3.0</version>
 				<configuration>
 				<configuration>
-					<source>1.7</source>
-					<target>1.7</target>
+					<source>1.8</source>
+					<target>1.8</target>
 					<encoding>UTF-8</encoding>
 					<encoding>UTF-8</encoding>
 				</configuration>
 				</configuration>
 			</plugin>
 			</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.FileNotFoundException;
 import java.io.IOException;
 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 org.jboss.netty.handler.codec.http.HttpMethod;
 
 
 import com.strategicgains.restexpress.RestExpress;
 import com.strategicgains.restexpress.RestExpress;
@@ -25,7 +28,8 @@ public class Main
 			.action("helloWorld", HttpMethod.GET);
 			.action("helloWorld", HttpMethod.GET);
 
 
                 server.uri("/restexpress/plaintext", config.getPlaintextController())
                 server.uri("/restexpress/plaintext", config.getPlaintextController())
-                        .action("helloWorld", HttpMethod.GET);
+                        .action("helloWorld", HttpMethod.GET)
+                        .noSerialization();
 
 
 		server.uri("/restexpress/mysql", config.getMysqlController())
 		server.uri("/restexpress/mysql", config.getMysqlController())
 			.method(HttpMethod.GET);
 			.method(HttpMethod.GET);
@@ -33,6 +37,11 @@ public class Main
 		server.uri("/restexpress/mongodb", config.getMongodbController())
 		server.uri("/restexpress/mongodb", config.getMongodbController())
 		    .method(HttpMethod.GET);
 		    .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.bind(config.getPort());
 		server.awaitShutdown();
 		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
 public class JsonController
 {
 {
-	private static final HelloWorld MESSAGE_OBJECT = new HelloWorld();
-
 	public HelloWorld helloWorld(Request request, Response response)
 	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)
 	public String helloWorld(Request request, Response response)
 	{
 	{
+		response.setContentType("text/plain");
 		return MESSAGE;
 		return MESSAGE;
 	}
 	}
 }
 }

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

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

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

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

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

@@ -1,16 +1,19 @@
 FROM python:3.6.5
 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 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 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 echo "deb-src http://nginx.org/packages/debian/ jessie nginx" >> /etc/apt/sources.list
 
 
 RUN apt update -yqq && apt install -yqq nginx
 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
 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
 FROM pypy:2-5.10
 
 
-ADD ./ /bottle
-
 WORKDIR /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
 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
 FROM python:3.6.5
 
 
-ADD ./ /bottle
-
 WORKDIR /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
 CMD gunicorn app:app -c gunicorn_conf.py

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

@@ -1,9 +1,11 @@
 FROM python:3.6.5
 FROM python:3.6.5
 
 
-ADD ./ /bottle
-
 WORKDIR /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
 CMD gunicorn app:app -c gunicorn_conf.py

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

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

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

@@ -1,9 +1,10 @@
 FROM python:3.6.5
 FROM python:3.6.5
 
 
-ADD ./ /falcon
-
 WORKDIR /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
 FROM pypy:2-5.10
 
 
-ADD ./ /falcon
-
 WORKDIR /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
 FROM python:2.7.14
 
 
-ADD ./ /falcon
-
 WORKDIR /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
 import os
 from random import randint
 from random import randint
 import sys
 import sys
+from email.utils import formatdate
 
 
 import flask
 import flask
 from flask import Flask, request, render_template, make_response, jsonify
 from flask import Flask, request, render_template, make_response, jsonify
@@ -68,12 +69,17 @@ class Fortune(db.Model):
 def json_response(obj):
 def json_response(obj):
     res = make_response(json.dumps(obj))
     res = make_response(json.dumps(obj))
     res.mimetype = "application/json"
     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
     return res
 
 
 
 
 @app.route("/json")
 @app.route("/json")
 def hello():
 def hello():
-    return jsonify(message='Hello, World!')
+    return add_date_header(jsonify(message='Hello, World!'))
 
 
 
 
 @app.route("/db")
 @app.route("/db")
@@ -126,16 +132,16 @@ def get_fortunes():
     fortunes = list(Fortune.query.all())
     fortunes = list(Fortune.query.all())
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=attrgetter('message'))
     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")
 @app.route("/fortunesraw")
-def get_forutens_raw():
+def get_fortunes_raw():
     res = dbraw_engine.execute("SELECT * FROM fortune")
     res = dbraw_engine.execute("SELECT * FROM fortune")
     fortunes = res.fetchall()
     fortunes = res.fetchall()
     res.close()
     res.close()
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=attrgetter('message'))
     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")
 @app.route("/updates")
@@ -188,7 +194,7 @@ def plaintext():
     """Test 6: Plaintext"""
     """Test 6: Plaintext"""
     response = make_response(b'Hello, World!')
     response = make_response(b'Hello, World!')
     response.content_type = 'text/plain'
     response.content_type = 'text/plain'
-    return response
+    return add_date_header(response)
 
 
 
 
 try:
 try:

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

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

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

@@ -1,4 +1,5 @@
 import ujson
 import ujson
+from email.utils import formatdate
 
 
 
 
 def application(environ, start_response):
 def application(environ, start_response):
@@ -7,7 +8,9 @@ def application(environ, start_response):
     }
     }
     data = ujson.dumps(response)
     data = ujson.dumps(response)
     response_headers = [
     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)))
         ('Content-Length', str(len(data)))
     ]
     ]
     start_response('200 OK', response_headers)
     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 import App, request, response
 from weppy.orm import Database, Model, Field, rowmethod
 from weppy.orm import Database, Model, Field, rowmethod
 from weppy.tools import service
 from weppy.tools import service
+from email.utils import formatdate
 
 
 _is_pypy = hasattr(sys, 'pypy_version_info')
 _is_pypy = hasattr(sys, 'pypy_version_info')
 if sys.version_info[0] == 3:
 if sys.version_info[0] == 3:
@@ -49,12 +50,14 @@ db.define_models(World, Fortune)
 @app.route()
 @app.route()
 @service.json
 @service.json
 def json():
 def json():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     return {'message': 'Hello, World!'}
     return {'message': 'Hello, World!'}
 
 
 
 
 @app.route("/db", pipeline=[db.pipe])
 @app.route("/db", pipeline=[db.pipe])
 @service.json
 @service.json
 def get_random_world():
 def get_random_world():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     return World.get(randint(1, 10000)).serialize()
     return World.get(randint(1, 10000)).serialize()
 
 
 
 
@@ -73,6 +76,7 @@ def get_qparam():
 @app.route("/queries", pipeline=[db.pipe])
 @app.route("/queries", pipeline=[db.pipe])
 @service.json
 @service.json
 def get_random_worlds():
 def get_random_worlds():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = get_qparam()
     num_queries = get_qparam()
     worlds = [
     worlds = [
         World.get(randint(1, 10000)).serialize() for _ in xrange(num_queries)]
         World.get(randint(1, 10000)).serialize() for _ in xrange(num_queries)]
@@ -81,6 +85,7 @@ def get_random_worlds():
 
 
 @app.route(pipeline=[db.pipe])
 @app.route(pipeline=[db.pipe])
 def fortunes():
 def fortunes():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     fortunes = Fortune.all().select()
     fortunes = Fortune.all().select()
     fortunes.append(
     fortunes.append(
         Fortune.new(id=0, message="Additional fortune added at request time."))
         Fortune.new(id=0, message="Additional fortune added at request time."))
@@ -91,6 +96,7 @@ def fortunes():
 @app.route(pipeline=[db.pipe])
 @app.route(pipeline=[db.pipe])
 @service.json
 @service.json
 def updates():
 def updates():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     num_queries = get_qparam()
     num_queries = get_qparam()
     worlds = []
     worlds = []
     rp = partial(randint, 1, 10000)
     rp = partial(randint, 1, 10000)
@@ -105,6 +111,7 @@ def updates():
 
 
 @app.route()
 @app.route()
 def plaintext():
 def plaintext():
+    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
     response.headers["Content-Type"] = "text/plain"
     response.headers["Content-Type"] = "text/plain"
     return 'Hello, World!'
     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
     include Web::Action
 
 
     def call(params)
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       status 200, WorldRepository.new.find_random_entity.to_h.to_json
       status 200, WorldRepository.new.find_random_entity.to_h.to_json
     end
     end
   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
     include Web::Action
 
 
     def call(params)
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       status 200, {:message => "Hello, World!"}.to_json
       status 200, {:message => "Hello, World!"}.to_json
     end
     end
   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)
     def call(params)
       self.format = :txt
       self.format = :txt
+      self.headers.merge!({ 'Content-Type' => 'text/plain' })
       status 200, 'Hello, World!'
       status 200, 'Hello, World!'
     end
     end
   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
     include Web::Action
 
 
     def call(params)
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       queries = params[:queries].to_i
       queries = params[:queries].to_i
       queries = 1 if queries < 1
       queries = 1 if queries < 1
       queries = 500 if queries > 500
       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
     include Web::Action
 
 
     def call(params)
     def call(params)
+      self.headers.merge!({ 'Content-Type' => 'application/json' })
       queries = (params[:queries] || 1).to_i
       queries = (params[:queries] || 1).to_i
       queries = 1 if queries < 1
       queries = 1 if queries < 1
       queries = 500 if queries > 500
       queries = 500 if queries > 500

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

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

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

@@ -12,7 +12,6 @@
 DataMapper.logger = logger
 DataMapper.logger = logger
 DataMapper::Property::String.length(255)
 DataMapper::Property::String.length(255)
 
 
-host = ENV['DB_HOST'] || 'localhost'
 case Padrino.env
 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
 end

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

@@ -1,12 +1,17 @@
 FROM ruby:2.4
 FROM ruby:2.4
 
 
-RUN apt update -yqq && apt install -yqq nginx
-
-ADD ./ /padrino
-
 WORKDIR /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 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 && \
 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
 FROM ruby:2.4
 
 
-ADD ./ /padrino
-
 WORKDIR /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 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
 FROM rust:1.25
 
 
-ADD ./ /rouille
 WORKDIR /rouille
 WORKDIR /rouille
+COPY src src
+COPY Cargo.toml Cargo.toml
 
 
-RUN cargo clean
 RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
 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| {
     rouille::start_server("0.0.0.0:8080", move |req| {
         router!(req,
         router!(req,
                 (GET) (/plaintext) => {
                 (GET) (/plaintext) => {
-                    rouille::Response::text("Hello, World!")
+                    rouille::Response::from_data("text/plain", "Hello, World!")
                 },
                 },
                 (GET) (/json) => {
                 (GET) (/json) => {
                     let json = json!({"message": "Hello, World!"});
                     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()
                 _ => rouille::Response::empty_404()
             )
             )

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

@@ -2,6 +2,7 @@ import json
 import re
 import re
 import traceback
 import traceback
 
 
+from datetime import datetime
 from toolset.utils.output_helper import log
 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
     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 = []
     problems = []
 
 
     for v in (v for v in ('Server', 'Date', 'Content-Type')
     for v in (v for v in ('Server', 'Date', 'Content-Type')
               if v.lower() not in headers):
               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))
                          url))
 
 
     if all(v.lower() not in headers
     if all(v.lower() not in headers
            for v in ('Content-Length', 'Transfer-Encoding')):
            for v in ('Content-Length', 'Transfer-Encoding')):
         problems.append((
         problems.append((
-            'warn',
+            'fail',
             'Required response size header missing, please include either "Content-Length" or "Transfer-Encoding"',
             'Required response size header missing, please include either "Content-Length" or "Transfer-Encoding"',
             url))
             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
     return problems