Introduce WSF_COMPRESSION and applied to WSF_*_WITH_COMPRESSION classes.
Modified the example to send the file with or without compression.
This commit is contained in:
@@ -28,17 +28,26 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
setup_router
|
setup_router
|
||||||
local
|
local
|
||||||
fhdl: WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
fhdl_with_compression: WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
||||||
|
fhdl: WSF_FILE_SYSTEM_HANDLER
|
||||||
do
|
do
|
||||||
|
create fhdl_with_compression.make_hidden ("www")
|
||||||
|
fhdl_with_compression.set_directory_index (<<"index.html">>)
|
||||||
|
fhdl_with_compression.compression.set_default_compression_format
|
||||||
|
fhdl_with_compression.compression.enable_compression_for_media_type ({HTTP_MIME_TYPES}.image_jpg)
|
||||||
|
fhdl_with_compression.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 ("/compressed/", fhdl_with_compression, router.methods_GET)
|
||||||
|
|
||||||
create fhdl.make_hidden ("www")
|
create fhdl.make_hidden ("www")
|
||||||
fhdl.set_directory_index (<<"index.html">>)
|
fhdl.set_directory_index (<<"index.html">>)
|
||||||
fhdl.set_default_compression_format
|
|
||||||
fhdl.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)
|
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||||
do
|
do
|
||||||
execute_default (ia_req, ia_res)
|
execute_default (ia_req, ia_res)
|
||||||
end)
|
end)
|
||||||
router.handle_with_request_methods ("/", fhdl, router.methods_GET)
|
router.handle ("/", fhdl, router.methods_GET)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,21 @@
|
|||||||
<h1>EWF simple_file example</h1>
|
<h1>EWF simple_file example</h1>
|
||||||
<p>This is a static html file served by EWF.</p>
|
<p>This is a static html file served by EWF.</p>
|
||||||
<p>Try to <a href="nowhere.html">get lost</a>.</p>
|
<p>Try to <a href="nowhere.html">get lost</a>.</p>
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>Without any compression</h2>
|
||||||
<a href="ewf.png"><img src="ewf.png"/></a>
|
<a href="ewf.png"><img src="ewf.png"/></a>
|
||||||
<p>This is the real Eiffel tower.</p>
|
<p>This is the real Eiffel tower.</p>
|
||||||
<a href="eiffel.jpg"><img src="eiffel.jpg"/></a>
|
<a href="eiffel.jpg"><img src="eiffel.jpg"/></a>
|
||||||
<p>Try to <a href="big_file2.html">load a big file</a>.</p>
|
<p>Try to <a href="big_file2.html">load a big file</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>With gzip compression</h2>
|
||||||
|
<a href="compressed/ewf.png"><img src="compressed/ewf.png"/></a>
|
||||||
|
<p>This is the real Eiffel tower.</p>
|
||||||
|
<a href="compressed/eiffel.jpg"><img src="compressed/eiffel.jpg"/></a>
|
||||||
|
<p>Try to <a href="compressed/big_file2.html">load a compressed big file</a>.</p>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
205
library/server/wsf/compression/wsf_compression.e
Normal file
205
library/server/wsf/compression/wsf_compression.e
Normal 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), {ZLIB_CONSTANTS}.Z_mem_level_9, {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-2017, 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
|
||||||
@@ -9,6 +9,9 @@ class
|
|||||||
|
|
||||||
inherit
|
inherit
|
||||||
WSF_FILE_RESPONSE
|
WSF_FILE_RESPONSE
|
||||||
|
redefine
|
||||||
|
send_to
|
||||||
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
make_with_path,
|
make_with_path,
|
||||||
@@ -18,9 +21,87 @@ create
|
|||||||
make_with_content_type,
|
make_with_content_type,
|
||||||
make_html
|
make_html
|
||||||
|
|
||||||
|
feature {NONE} -- Access
|
||||||
|
|
||||||
|
compression_variants: detachable HTTP_ACCEPT_ENCODING_VARIANTS
|
||||||
|
|
||||||
|
compression: detachable WSF_COMPRESSION
|
||||||
|
|
||||||
|
feature -- Compression setting
|
||||||
|
|
||||||
|
apply_compression (a_compression: WSF_COMPRESSION; req: WSF_REQUEST)
|
||||||
|
do
|
||||||
|
compression := a_compression
|
||||||
|
compression_variants := a_compression.encoding_variants (req, content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
reset_compression
|
||||||
|
do
|
||||||
|
compression := Void
|
||||||
|
compression_variants := Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {WSF_RESPONSE} -- Output
|
||||||
|
|
||||||
|
send_to (res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
|
elseif
|
||||||
|
attached compression as l_compression and then
|
||||||
|
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
|
||||||
|
send_compressed_to (res, l_compression, l_encoding, l_vary_header)
|
||||||
|
else
|
||||||
|
-- Send uncompressed...
|
||||||
|
Precursor (res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send_compressed_to (res: WSF_RESPONSE; a_compression: WSF_COMPRESSION; a_comp_encoding, a_comp_vary_header: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
s: detachable READABLE_STRING_8
|
||||||
|
l_content, l_compressed_content: STRING_8
|
||||||
|
f: RAW_FILE
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
res.set_status_code (status_code)
|
||||||
|
create f.make_with_path (file_path)
|
||||||
|
l_count := f.count
|
||||||
|
f.open_read
|
||||||
|
f.read_stream (l_count)
|
||||||
|
f.close
|
||||||
|
l_content := f.last_string
|
||||||
|
|
||||||
|
s := head
|
||||||
|
if s /= Void then
|
||||||
|
l_content.prepend (s)
|
||||||
|
end
|
||||||
|
s := bottom
|
||||||
|
if s /= Void then
|
||||||
|
l_content.append_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
l_compressed_content := a_compression.compressed_string (l_content, a_comp_encoding)
|
||||||
|
|
||||||
|
debug
|
||||||
|
res.put_error (l_content.count.out + " -(compression-> " + l_compressed_content.count.out + "%N")
|
||||||
|
end
|
||||||
|
header.put_content_encoding (a_comp_encoding)
|
||||||
|
header.add_header ("Vary:" + a_comp_vary_header)
|
||||||
|
header.put_content_length (l_compressed_content.count)
|
||||||
|
|
||||||
|
res.put_header_text (header.string)
|
||||||
|
if not answer_head_request_method then
|
||||||
|
res.put_string (l_compressed_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
copyright: "2011-2017, 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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -25,33 +25,12 @@ feature {NONE} -- Initialization
|
|||||||
initialize
|
initialize
|
||||||
do
|
do
|
||||||
Precursor
|
Precursor
|
||||||
initialize_compression
|
create compression.make
|
||||||
end
|
end
|
||||||
|
|
||||||
initialize_compression
|
feature -- Access: 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 ("", "", "", "")
|
|
||||||
|
|
||||||
-- compression algorithms
|
compression: WSF_COMPRESSION
|
||||||
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
|
|
||||||
-- content negotiation.
|
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
@@ -59,11 +38,8 @@ feature -- Execution
|
|||||||
local
|
local
|
||||||
ext: READABLE_STRING_32
|
ext: READABLE_STRING_32
|
||||||
ct: detachable READABLE_STRING_8
|
ct: detachable READABLE_STRING_8
|
||||||
-- fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
|
fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
|
||||||
dt: DATE_TIME
|
dt: DATE_TIME
|
||||||
h: HTTP_HEADER
|
|
||||||
l_file: RAW_FILE
|
|
||||||
l_content: detachable STRING
|
|
||||||
do
|
do
|
||||||
ext := extension (f.path.name)
|
ext := extension (f.path.name)
|
||||||
ct := extension_mime_mapping.mime_type (ext)
|
ct := extension_mime_mapping.mime_type (ext)
|
||||||
@@ -71,246 +47,28 @@ feature -- Execution
|
|||||||
ct := {HTTP_MIME_TYPES}.application_force_download
|
ct := {HTTP_MIME_TYPES}.application_force_download
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the CLIENT request
|
create fres.make_with_content_type_and_path (ct, f.path)
|
||||||
-- If the client support compression and one of the algorithms is `deflate_compression_format' we can do compression.
|
-- Apply compression based on request `req` header.
|
||||||
-- and we need to add the corresponding 'Content-Encoding' with supported compression formats.
|
fres.apply_compression (compression, req)
|
||||||
if
|
|
||||||
attached compression_encoding_variants (req, ct) as l_compression_variants and then
|
-- Prepare response
|
||||||
attached l_compression_variants.encoding as l_encoding and then
|
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
attached l_compression_variants.vary_header_value as l_vary_header
|
|
||||||
then
|
-- cache control
|
||||||
create h.make
|
create dt.make_now_utc
|
||||||
create l_file.make_with_path (create {PATH}.make_from_string (f.path.name))
|
fres.header.put_utc_date (dt)
|
||||||
check
|
if max_age >= 0 then
|
||||||
f_valid: l_file.exists and then l_file.is_access_readable
|
fres.set_max_age (max_age)
|
||||||
|
if max_age > 0 then
|
||||||
|
dt := dt.twin
|
||||||
|
dt.second_add (max_age)
|
||||||
end
|
end
|
||||||
h.put_last_modified (file_date (l_file))
|
fres.set_expires_date (dt)
|
||||||
l_file.open_read
|
|
||||||
l_file.read_stream (l_file.count)
|
|
||||||
l_file.close
|
|
||||||
l_content := 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)
|
|
||||||
h.put_content_length (l_content.count)
|
|
||||||
|
|
||||||
-- cache control
|
|
||||||
create dt.make_now_utc
|
|
||||||
h.put_utc_date (dt)
|
|
||||||
if max_age >= 0 then
|
|
||||||
h.put_cache_control ("max-age=" +max_age.out)
|
|
||||||
if max_age > 0 then
|
|
||||||
dt := dt.twin
|
|
||||||
dt.second_add (max_age)
|
|
||||||
end
|
|
||||||
h.put_expires_date (dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
|
||||||
res.put_header_text (h.string)
|
|
||||||
res.put_string (l_content)
|
|
||||||
else
|
|
||||||
Precursor (f, req, res)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Compression: constants
|
-- send
|
||||||
|
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
|
||||||
gzip_compression_format: STRING = "gzip"
|
res.send (fres)
|
||||||
-- 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), {ZLIB_CONSTANTS}.Z_mem_level_9, {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
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
|
|||||||
@@ -104,6 +104,15 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
feature -- Element change
|
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)
|
set_max_age (sec: INTEGER)
|
||||||
do
|
do
|
||||||
header.put_cache_control ("max-age=" + sec.out)
|
header.put_cache_control ("max-age=" + sec.out)
|
||||||
@@ -227,6 +236,7 @@ feature {WSF_RESPONSE} -- Output
|
|||||||
do
|
do
|
||||||
res.set_status_code (status_code)
|
res.set_status_code (status_code)
|
||||||
if status_code = {HTTP_STATUS_CODE}.not_found then
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
else
|
else
|
||||||
res.put_header_text (header.string)
|
res.put_header_text (header.string)
|
||||||
s := head
|
s := head
|
||||||
|
|||||||
Reference in New Issue
Block a user