Introduce WSF_COMPRESSION (experimental)

This commit is contained in:
2016-12-14 17:33:52 +01:00
parent e14bb568d2
commit f4ed19d679
6 changed files with 283 additions and 228 deletions

View File

@@ -32,13 +32,13 @@ feature {NONE} -- Initialization
do
create fhdl.make_hidden ("www")
fhdl.set_directory_index (<<"index.html">>)
fhdl.set_default_compression_format
fhdl.enable_compression_for_media_type ({HTTP_MIME_TYPES}.image_jpg)
fhdl.compression.set_default_compression_format
fhdl.compression.enable_compression_for_media_type ({HTTP_MIME_TYPES}.image_jpg)
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
do
execute_default (ia_req, ia_res)
end)
router.handle_with_request_methods ("/", fhdl, router.methods_GET)
router.handle ("/", fhdl, router.methods_GET)
end

View File

@@ -0,0 +1,205 @@
note
description: "Summary description for {WSF_COMPRESSION}."
date: "$Date$"
revision: "$Revision$"
class
WSF_COMPRESSION
create
make
feature {NONE} -- Initialization
make
-- Initialize compression support, by default no compression
-- Gzip with the following media types
-- applications/javascript
-- application/json
-- application/xml
-- text/css
-- text/html
--
do
-- compression algorithms
create {ARRAYED_LIST [STRING]} compression_supported_formats.make (0)
compression_supported_formats.compare_objects
-- media types supported by compression.
create {ARRAYED_LIST [STRING]} compression_enabled_media_types.make (0)
compression_enabled_media_types.compare_objects
set_default_compression_enabled_media_types
end
feature -- Query
encoding_variants (req: WSF_REQUEST; ct: STRING): detachable HTTP_ACCEPT_ENCODING_VARIANTS
-- If the client support compression and the server support one of the algorithms
-- compress it and update the response header.
local
conneg : SERVER_CONTENT_NEGOTIATION
do
if
attached req.http_accept_encoding as l_http_encoding and then
not compression_supported_formats.is_empty and then
compression_enabled_media_types.has (ct)
then
create conneg.make ("", "", "", "")
Result := conneg.encoding_preference (compression_supported_formats, l_http_encoding)
if not Result.is_acceptable then
Result := Void
end
end
end
feature -- Compression: constants
gzip_compression_format: STRING = "gzip"
-- RFC 1952 (gzip compressed format)
deflate_compression_format: STRING = "deflate"
-- RFC 1951 (deflate compressed format)
compress_compression_format: STRING = "compress"
-- RFC 1950 (zlib compressed format)
feature -- Compression
compression_supported_formats : LIST [STRING]
-- Server side compression supported formats.
-- Supported compression agorithms: `gzip_compression_format', `deflate_compression_format', `compress_compression_format'.
-- identity, means no compression at all.
compression_enabled_media_types: LIST [STRING]
-- List of media types supported by compression.
set_default_compression_format
-- gzip default format
do
enable_gzip_compression
end
disable_all_compression_formats
-- Remove all items.
do
compression_supported_formats.wipe_out
end
enable_gzip_compression
-- add 'gzip' format to the list of 'compression_supported' formats.
do
compression_supported_formats.force (gzip_compression_format)
ensure
has_gzip: compression_supported_formats.has (gzip_compression_format)
end
disable_gzip_compression
-- remove 'gzip' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (gzip_compression_format)
ensure
not_gzip: not compression_supported_formats.has (gzip_compression_format)
end
enable_deflate_compression
-- add 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.force (deflate_compression_format)
ensure
has_deflate: compression_supported_formats.has (deflate_compression_format)
end
disable_deflate_compression
-- remove 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (deflate_compression_format)
ensure
not_deflate: not compression_supported_formats.has (deflate_compression_format)
end
enable_compress_compression
-- add 'compress' format to the list of 'compression_supported' formats
do
compression_supported_formats.force (compress_compression_format)
ensure
has_compress: compression_supported_formats.has (compress_compression_format)
end
disable_compress_compression
-- remove 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (compress_compression_format)
ensure
no_compress: not compression_supported_formats.has (compress_compression_format)
end
feature -- Compression: media types
set_default_compression_enabled_media_types
-- Default media types
-- applications/javascript
-- application/json
-- application/xml
-- text/css
-- text/html
-- text/plain
do
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_javascript)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_json)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_xml)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_css)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_html)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_plain)
end
remove_all_compression_enabled_media_types
-- Remove all items.
do
compression_enabled_media_types.wipe_out
end
enable_compression_for_media_type (a_media_type: STRING)
do
compression_enabled_media_types.force (a_media_type)
ensure
has_media_type: compression_enabled_media_types.has (a_media_type)
end
feature -- Compress Data
compressed_string (a_string: STRING; a_encoding: STRING): STRING
-- Compress `a_string' using `deflate_compression_format'
local
dc: ZLIB_STRING_COMPRESS
do
create Result.make_empty
create dc.string_stream_with_size (Result, 32_768) -- chunk size 32k
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, zlb_strategy (a_encoding), 8, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
-- We use the default compression level
-- We use the default value for windows bits, the range is 8..15. Higher values use more memory, but produce smaller output.
-- Memory: Higher values use more memory, but are faster and produce smaller output. The default is 8, we use 9.
end
zlb_strategy (a_encoding: STRING): INTEGER
do
if a_encoding.is_case_insensitive_equal_general (gzip_compression_format) then
Result := {ZLIB_CONSTANTS}.z_default_window_bits + 16
elseif a_encoding.is_case_insensitive_equal_general (deflate_compression_format) then
Result := -{ZLIB_CONSTANTS}.z_default_window_bits
else
check compress: a_encoding.is_case_insensitive_equal_general (compress_compression_format) end
Result := {ZLIB_CONSTANTS}.z_default_window_bits
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -9,6 +9,10 @@ class
inherit
WSF_FILE_RESPONSE
redefine
send_to,
initialize
end
create
make_with_path,
@@ -18,7 +22,58 @@ create
make_with_content_type,
make_html
feature {NONE} -- Initialization
initialize
do
Precursor
create compression.make
end
feature -- Access
compression: WSF_COMPRESSION
compression_variants: detachable HTTP_ACCEPT_ENCODING_VARIANTS
feature -- Compression setting
apply_compression (req: WSF_REQUEST)
do
compression_variants := compression.encoding_variants (req, content_type)
end
feature {WSF_RESPONSE} -- Output
send_to (res: WSF_RESPONSE)
local
s: detachable READABLE_STRING_8
do
if attached compression_variants as l_compression_variants and then
attached l_compression_variants.encoding as l_encoding and then
attached l_compression_variants.vary_header_value as l_vary_header
then
Precursor (res)
else
Precursor (res)
-- res.set_status_code (status_code)
-- if status_code = {HTTP_STATUS_CODE}.not_found then
-- else
-- res.put_header_text (header.string)
-- s := head
-- if s /= Void then
-- res.put_string (s)
-- end
-- if not answer_head_request_method then
-- send_file_content_to (file_path, res)
-- end
-- s := bottom
-- if s /= Void then
-- res.put_string (s)
-- end
-- end
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -25,32 +25,12 @@ feature {NONE} -- Initialization
initialize
do
Precursor
initialize_compression
create compression.make
end
initialize_compression
-- Initialize compression support, by default no compression
-- Gzip with the following media types
-- applications/javascript
-- application/json
-- application/xml
-- text/css
-- text/html
--
do
create conneg.make ("", "", "", "")
feature -- Access: compression
-- compression algorithms
create {ARRAYED_LIST [STRING]} compression_supported_formats.make (0)
compression_supported_formats.compare_objects
-- media types supported by compression.
create {ARRAYED_LIST [STRING]} compression_enabled_media_types.make (0)
compression_enabled_media_types.compare_objects
set_default_compression_enabled_media_types
end
conneg : SERVER_CONTENT_NEGOTIATION
compression: WSF_COMPRESSION
feature -- Execution
@@ -58,23 +38,26 @@ feature -- Execution
local
ext: READABLE_STRING_32
ct: detachable READABLE_STRING_8
-- fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
dt: DATE_TIME
h: HTTP_HEADER
l_file: RAW_FILE
l_content: detachable STRING
do
create fres.make_with_path (f.path)
ext := extension (f.path.name)
ct := extension_mime_mapping.mime_type (ext)
if ct = Void then
ct := {HTTP_MIME_TYPES}.application_force_download
ct := fres.content_type
else
fres.set_content_type (ct)
end
-- Check the CLIENT request
-- If the client support compression and one of the algorithms is `deflate_compression_format' we can do compression.
-- and we need to add the corresponding 'Content-Encoding' with supported compression formats.
if
attached compression_encoding_variants (req, ct) as l_compression_variants and then
attached compression.encoding_variants (req, ct) as l_compression_variants and then
attached l_compression_variants.encoding as l_encoding and then
attached l_compression_variants.vary_header_value as l_vary_header
then
@@ -87,7 +70,7 @@ feature -- Execution
l_file.open_read
l_file.read_stream (l_file.count)
l_file.close
l_content := compressed_string (l_file.last_string, l_encoding)
l_content := compression.compressed_string (l_file.last_string, l_encoding)
h.add_header ("Content-Encoding:" + l_encoding)
h.add_header ("Vary:" + l_vary_header)
h.put_content_type (ct)
@@ -113,204 +96,9 @@ feature -- Execution
end
end
feature -- Compression: constants
gzip_compression_format: STRING = "gzip"
-- RFC 1952 (gzip compressed format)
deflate_compression_format: STRING = "deflate"
-- RFC 1951 (deflate compressed format)
compress_compression_format: STRING = "compress"
-- RFC 1950 (zlib compressed format)
feature -- Compression
compression_supported_formats : LIST [STRING]
-- Server side compression supported formats.
-- Supported compression agorithms: `gzip_compression_format', `deflate_compression_format', `compress_compression_format'.
-- identity, means no compression at all.
compression_enabled_media_types: LIST [STRING]
-- List of media types supported by compression.
set_default_compression_format
-- gzip default format
do
enable_gzip_compression
end
disable_all_compression_formats
-- Remove all items.
do
compression_supported_formats.wipe_out
end
enable_gzip_compression
-- add 'gzip' format to the list of 'compression_supported' formats.
do
compression_supported_formats.force (gzip_compression_format)
ensure
has_gzip: compression_supported_formats.has (gzip_compression_format)
end
disable_gzip_compression
-- remove 'gzip' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (gzip_compression_format)
ensure
not_gzip: not compression_supported_formats.has (gzip_compression_format)
end
enable_deflate_compression
-- add 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.force (deflate_compression_format)
ensure
has_deflate: compression_supported_formats.has (deflate_compression_format)
end
disable_deflate_compression
-- remove 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (deflate_compression_format)
ensure
not_deflate: not compression_supported_formats.has (deflate_compression_format)
end
enable_compress_compression
-- add 'compress' format to the list of 'compression_supported' formats
do
compression_supported_formats.force (compress_compression_format)
ensure
has_compress: compression_supported_formats.has (compress_compression_format)
end
disable_compress_compression
-- remove 'deflate' format to the list of 'compression_supported' formats.
do
compression_supported_formats.prune (compress_compression_format)
ensure
no_compress: not compression_supported_formats.has (compress_compression_format)
end
feature -- Compression: media types
set_default_compression_enabled_media_types
-- Default media types
-- applications/javascript
-- application/json
-- application/xml
-- text/css
-- text/html
-- text/plain
do
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_javascript)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_json)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_xml)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_css)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_html)
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_plain)
end
remove_all_compression_enabled_media_types
-- Remove all items.
do
compression_enabled_media_types.wipe_out
end
enable_compression_for_media_type (a_media_type: STRING)
do
compression_enabled_media_types.force (a_media_type)
ensure
has_media_type: compression_enabled_media_types.has (a_media_type)
end
feature -- Support Compress
compression_encoding_variants (req: WSF_REQUEST; ct: STRING): detachable HTTP_ACCEPT_ENCODING_VARIANTS
-- If the client support compression and the server support one of the algorithms
-- compress it and update the response header.
local
l_compression_supported: LIST [STRING]
l_compression: STRING
do
if
attached req.http_accept_encoding as l_http_encoding and then
not compression_supported_formats.is_empty and then
compression_enabled_media_types.has (ct)
then
Result := conneg.encoding_preference (compression_supported_formats, l_http_encoding)
if not Result.is_acceptable then
Result := Void
end
end
end
generated_compressed_output (req: WSF_REQUEST; f:FILE; h: HTTP_HEADER; ct: STRING): detachable STRING
-- If the client support compression and the server support one of the algorithms
-- compress it and update the response header.
local
l_file: RAW_FILE
l_compression_variants: HTTP_ACCEPT_ENCODING_VARIANTS
l_compression_supported: LIST [STRING]
l_compression: STRING
do
if
not compression_supported_formats.is_empty and then
compression_enabled_media_types.has (ct) and then
attached req.http_accept_encoding as l_http_encoding
then
l_compression_variants := conneg.encoding_preference (compression_supported_formats, l_http_encoding)
if
l_compression_variants.is_acceptable and then
attached l_compression_variants.encoding as l_encoding and then
attached l_compression_variants.vary_header_value as l_vary_header
then
create l_file.make_with_path (create {PATH}.make_from_string (f.path.name))
if l_file.exists then
l_file.open_read
h.put_last_modified (create {DATE_TIME}.make_from_epoch (l_file.date))
-- Check the CLIENT request
-- If the client support compression and one of the algorithms is `deflate_compression_format' we can do compression.
-- and we need to add the corresponding 'Content-Ecoding' witht the supported algorithm.
l_file.read_stream (l_file.count)
Result := compressed_string (l_file.last_string, l_encoding)
h.add_header ("Content-Encoding:" + l_encoding)
h.add_header ("Vary:" + l_vary_header)
l_file.close
end
end
end
end
feature -- Compress Data
compressed_string (a_string: STRING; a_encoding: STRING): STRING
-- Compress `a_string' using `deflate_compression_format'
local
dc: ZLIB_STRING_COMPRESS
do
create Result.make_empty
create dc.string_stream_with_size (Result, 32_768) -- chunk size 32k
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, zlb_strategy (a_encoding), 8, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
-- We use the default compression level
-- We use the default value for windows bits, the range is 8..15. Higher values use more memory, but produce smaller output.
-- Memory: Higher values use more memory, but are faster and produce smaller output. The default is 8, we use 9.
end
zlb_strategy (a_encoding: STRING): INTEGER
do
if a_encoding.is_case_insensitive_equal_general (gzip_compression_format) then
Result := {ZLIB_CONSTANTS}.z_default_window_bits + 16
elseif a_encoding.is_case_insensitive_equal_general (deflate_compression_format) then
Result := -{ZLIB_CONSTANTS}.z_default_window_bits
else
check compress: a_encoding.is_case_insensitive_equal_general (compress_compression_format) end
Result := {ZLIB_CONSTANTS}.z_default_window_bits
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"

View File

@@ -43,8 +43,6 @@ feature {NONE} -- Initialize
initialize_filtered_router
-- Initialize `router` and `filter`.
local
f: like filter
do
initialize_router
initialize_filter
@@ -63,7 +61,7 @@ feature -- Execute Filter
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -104,6 +104,15 @@ feature {NONE} -- Initialization
feature -- Element change
set_content_type (a_content_type: detachable like content_type)
do
if a_content_type = Void then
get_content_type
else
content_type := a_content_type
end
end
set_max_age (sec: INTEGER)
do
header.put_cache_control ("max-age=" + sec.out)