Merge branch 'master' of github.com:EiffelWebFramework/EWF into widget_integration

This commit is contained in:
2014-07-07 10:30:42 +02:00
318 changed files with 14865 additions and 10865 deletions

View File

@@ -9,509 +9,11 @@ note
class
WSF_PERCENT_ENCODER
feature -- Percent encoding
percent_encoded_string (v: READABLE_STRING_GENERAL): STRING_8
-- Return `a_string' percent-encoded
do
create Result.make (v.count)
append_percent_encoded_string_to (v, Result)
end
append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
-- Append `a_string' as percent-encoded value to `a_result'
local
c: NATURAL_32
i,n: INTEGER
do
from
i := 1
n := s.count
until
i > n
loop
c := s.code (i)
if
--| unreserved ALPHA / DIGIT
(48 <= c and c <= 57) -- DIGIT: 0 .. 9
or (65 <= c and c <= 90) -- ALPHA: A .. Z
or (97 <= c and c <= 122) -- ALPHA: a .. z
then
a_result.append_code (c)
else
inspect c
when
45, 46, 95, 126 -- unreserved characters: -._~
then
a_result.append_code (c)
when
58, 64, -- reserved =+ gen-delims: :@
33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()*
43, 44, 59, 61, -- reserved = sub-delims: +,;=
37 -- percent encoding: %
then
append_percent_encoded_character_code_to (c, a_result)
else
append_percent_encoded_character_code_to (c, a_result)
end
end
i := i + 1
end
end
feature -- Percent encoding: character
append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append character code `a_code' as percent-encoded content into `a_result'
do
if a_code > 0xFF then
-- Unicode
append_percent_encoded_unicode_character_code_to (a_code, a_result)
elseif a_code > 0x7F then
-- Extended ASCII
-- This requires percent-encoding on UTF-8 converted character.
append_percent_encoded_unicode_character_code_to (a_code, a_result)
else
-- ASCII
append_percent_encoded_ascii_character_code_to (a_code, a_result)
end
ensure
appended: a_result.count > old a_result.count
end
feature {NONE} -- Implementation: character encoding
append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append extended ascii character code `a_code' as percent-encoded content into `a_result'
-- Note: it does not UTF-8 convert this extended ASCII.
require
is_extended_ascii: a_code <= 0xFF
local
c: INTEGER
do
if a_code > 0xFF then
-- Unicode
append_percent_encoded_unicode_character_code_to (a_code, a_result)
else
-- Extended ASCII
c := a_code.to_integer_32
a_result.append_code (37) -- 37 '%%'
a_result.append_code (hex_digit [c |>> 4])
a_result.append_code (hex_digit [c & 0xF])
end
ensure
appended: a_result.count > old a_result.count
end
append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result'
-- Note: it does include UTF-8 conversion of extended ASCII and Unicode.
do
if a_code <= 0x7F then
-- 0xxxxxxx
append_percent_encoded_ascii_character_code_to (a_code, a_result)
elseif a_code <= 0x7FF then
-- 110xxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
elseif a_code <= 0xFFFF then
-- 1110xxxx 10xxxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
else
-- c <= 1FFFFF - there are no higher code points
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
end
ensure
appended: a_result.count > old a_result.count
end
feature -- Percent decoding
percent_decoded_string (v: READABLE_STRING_GENERAL): STRING_32
-- Return the percent decoded string equivalent to the percent-encoded string `v'
--| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8
do
create Result.make (v.count)
append_percent_decoded_string_to (v, Result)
end
append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
-- Append to `a_result' a string equivalent to the percent-encoded string `v'
--| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8
local
i,n: INTEGER
c: NATURAL_32
pr: CELL [INTEGER]
a_result_is_string_32: BOOLEAN
do
a_result_is_string_32 := attached {STRING_32} a_result
from
i := 1
create pr.put (i)
n := v.count
until
i > n
loop
c := v.code (i)
inspect c
when 43 then -- 43 '+'
-- Some implementation are replacing spaces with "+" instead of "%20"
a_result.append_code (32) -- 32 ' '
when 37 then -- 37 '%%'
-- An escaped character ?
if i = n then -- Error?
a_result.append_code (c)
else
if a_result_is_string_32 then
-- Convert UTF-8 to UTF-32
pr.replace (i)
c := next_percent_decoded_unicode_character_code (v, pr)
a_result.append_code (c)
i := pr.item
else
-- Keep UTF-8
pr.replace (i)
c := next_percent_decoded_character_code (v, pr)
a_result.append_code (c)
i := pr.item
end
end
else
if c <= 0x7F then
a_result.append_code (c)
else
if a_result_is_string_32 then
a_result.append_code (c)
else
append_percent_encoded_character_code_to (c, a_result)
end
end
end
i := i + 1
end
end
feature {NONE} -- Implementation: decoding
next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
-- Character decoded from string `v' starting from index `a_position.item'
-- note: it also updates `a_position.item' to indicate the new index position.
require
valid_start: a_position.item <= v.count
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
local
c: NATURAL_32
i, n: INTEGER
not_a_digit: BOOLEAN
ascii_pos: NATURAL_32
ival: NATURAL_32
pos: INTEGER
c_is_digit: BOOLEAN
do
--| pos is index in stream of escape character ('%')
pos := a_position.item
c := v.code (pos + 1)
if c = 85 or c = 117 then -- 117 'u' 85 'U'
-- NOTE: this is not a standard, but it can occur, so use this for decoding only
-- An escaped Unicode (ucs2) value, from ECMA scripts
-- has the form: %u<n> where <n> is the UCS value
-- of the character (two byte integer, one to 4 chars
-- after escape sequence).
-- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations
-- UTF-8 result can be 1 to 4 characters.
from
i := pos + 2
n := v.count
until
(i > n) or not_a_digit
loop
c := v.code (i)
c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9
if
c_is_digit
or (97 <= c and c <= 102) -- ALPHA: a..f
or (65 <= c and c <= 70) -- ALPHA: A..F
then
ival := ival * 16
if c_is_digit then
ival := ival + (c - 48) -- 48 '0'
else
if c > 70 then -- a..f
ival := ival + (c - 97) + 10 -- 97 'a'
else -- A..F
ival := ival + (c - 65) + 10 -- 65 'A'
end
end
i := i + 1
else
not_a_digit := True
i := i - 1
end
end
a_position.replace (i)
Result := ival
else
-- ASCII char?
ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2))
Result := ascii_pos
a_position.replace (pos + 2)
end
end
next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
-- Next decoded character from `v' at position `a_position.item'
-- note: it also updates `a_position' to indicate the new index position.
require
valid_start: a_position.item <= v.count
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
local
n, j: INTEGER
c: NATURAL_32
c1, c2, c3, c4: NATURAL_32
pr: CELL [INTEGER]
do
create pr.put (a_position.item)
c1 := next_percent_decoded_character_code (v, pr)
j := pr.item
n := v.count
Result := c1
a_position.replace (j)
if c1 <= 0x7F then
-- 0xxxxxxx
Result := c1
elseif c1 <= 0xDF then
-- 110xxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
Result := (
((c1 & 0x1F) |<< 6) |
( c2 & 0x3F )
)
a_position.replace (j)
else
-- Do not try to decode
end
end
elseif c1 <= 0xEF then
-- 1110xxxx 10xxxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c3 := next_percent_decoded_character_code (v, pr)
j := pr.item
Result := (
((c1 & 0xF) |<< 12) |
((c2 & 0x3F) |<< 6) |
( c3 & 0x3F )
)
a_position.replace (j)
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
elseif c1 <= 0xF7 then
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c3 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c4 := next_percent_decoded_character_code (v, pr)
j := pr.item
a_position.replace (j)
Result := (
((c1 & 0x7) |<< 18 ) |
((c2 & 0x3F) |<< 12) |
((c3 & 0x3F) |<< 6) |
( c4 & 0x3F )
)
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
else
Result := c1
end
end
feature -- RFC and characters
is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN
-- Is hexadecimal character ?
do
Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA
or ('0' <= c and c <= '9') -- DIGIT
end
is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN
-- Is ALPHA or DIGIT character ?
do
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA
or ('0' <= c and c <= '9') -- DIGIT
end
is_alpha_character (c: CHARACTER_32): BOOLEAN
-- Is ALPHA character ?
do
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z')
end
is_digit_character (c: CHARACTER_32): BOOLEAN
-- Is DIGIT character ?
do
Result := ('0' <= c and c <= '9')
end
is_unreserved_character (c: CHARACTER_32): BOOLEAN
-- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
do
if
('a' <= c and c <= 'z') -- ALPHA
or ('A' <= c and c <= 'Z') -- ALPHA
or ('0' <= c and c <= '9') -- DIGIT
then
Result := True
else
inspect c
when '-', '_', '.', '~' then -- unreserved
Result := True
else
end
end
end
is_reserved_character (c: CHARACTER_32): BOOLEAN
-- reserved = gen-delims / sub-delims
do
Result := is_gen_delims_character (c) or is_sub_delims_character (c)
end
is_gen_delims_character (c: CHARACTER_32): BOOLEAN
-- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
do
inspect c
when ':' , '/', '?' , '#' , '[' , ']' , '@' then
Result := True
else
end
end
is_sub_delims_character (c: CHARACTER_32): BOOLEAN
-- sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
-- / "*" / "+" / "," / ";" / "="
do
inspect c
when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims
Result := True
else
end
end
feature {NONE} -- Implementation
hex_digit: SPECIAL [NATURAL_32]
-- Hexadecimal digits.
once
create Result.make_filled (0, 16)
Result [0] := {NATURAL_32} 48 -- 48 '0'
Result [1] := {NATURAL_32} 49 -- 49 '1'
Result [2] := {NATURAL_32} 50 -- 50 '2'
Result [3] := {NATURAL_32} 51 -- 51 '3'
Result [4] := {NATURAL_32} 52 -- 52 '4'
Result [5] := {NATURAL_32} 53 -- 53 '5'
Result [6] := {NATURAL_32} 54 -- 54 '6'
Result [7] := {NATURAL_32} 55 -- 55 '7'
Result [8] := {NATURAL_32} 56 -- 56 '8'
Result [9] := {NATURAL_32} 57 -- 57 '9'
Result [10] := {NATURAL_32} 65 -- 65 'A'
Result [11] := {NATURAL_32} 66 -- 66 'B'
Result [12] := {NATURAL_32} 67 -- 67 'C'
Result [13] := {NATURAL_32} 68 -- 68 'D'
Result [14] := {NATURAL_32} 69 -- 69 'E'
Result [15] := {NATURAL_32} 70 -- 70 'F'
end
is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN
-- Is `a_string' a valid hexadecimal sequence?
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32)
Result := l_convertor.is_integral_integer
end
hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32
-- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value.
require
is_hexa: is_hexa_decimal (a_hex_string)
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_natural_32
end
ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
-- Converter used to convert string to integer or natural.
once
create Result.make
Result.set_leading_separators_acceptable (False)
Result.set_trailing_separators_acceptable (False)
ensure
ctoi_convertor_not_void: Result /= Void
end
inherit
PERCENT_ENCODER
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -102,6 +102,16 @@ feature -- Access: Uploaded File
filename: STRING
-- original filename
safe_filename: STRING
-- Safe name version of `filename'.
-- i.e: removing whitespace, accent, unicode characters ...
local
ut: WSF_FILE_UTILITIES [RAW_FILE]
do
create ut
Result := ut.safe_filename (filename)
end
content_type: STRING
-- Content type
@@ -118,7 +128,7 @@ feature -- Access: Uploaded File
end
end
tmp_basename: detachable STRING
tmp_basename: detachable READABLE_STRING_GENERAL
-- Basename of tmp file
feature -- Conversion
@@ -156,92 +166,6 @@ feature -- Conversion
retry
end
feature -- Implementation
safe_filename: STRING
local
fn: like filename
c: CHARACTER
i, n: INTEGER
do
fn := filename
--| Compute safe filename, to avoid creating impossible filename, or dangerous one
from
i := 1
n := fn.count
create Result.make (n)
until
i > n
loop
c := fn[i]
inspect c
when '.', '-', '_' then
Result.extend (c)
when 'A' .. 'Z', 'a' .. 'z', '0' .. '9' then
Result.extend (c)
else
inspect c
when '%/192/' then Result.extend ('A') -- À
when '%/193/' then Result.extend ('A') -- Á
when '%/194/' then Result.extend ('A') -- Â
when '%/195/' then Result.extend ('A') -- Ã
when '%/196/' then Result.extend ('A') -- Ä
when '%/197/' then Result.extend ('A') -- Å
when '%/199/' then Result.extend ('C') -- Ç
when '%/200/' then Result.extend ('E') -- È
when '%/201/' then Result.extend ('E') -- É
when '%/202/' then Result.extend ('E') -- Ê
when '%/203/' then Result.extend ('E') -- Ë
when '%/204/' then Result.extend ('I') -- Ì
when '%/205/' then Result.extend ('I') -- Í
when '%/206/' then Result.extend ('I') -- Î
when '%/207/' then Result.extend ('I') -- Ï
when '%/210/' then Result.extend ('O') -- Ò
when '%/211/' then Result.extend ('O') -- Ó
when '%/212/' then Result.extend ('O') -- Ô
when '%/213/' then Result.extend ('O') -- Õ
when '%/214/' then Result.extend ('O') -- Ö
when '%/217/' then Result.extend ('U') -- Ù
when '%/218/' then Result.extend ('U') -- Ú
when '%/219/' then Result.extend ('U') -- Û
when '%/220/' then Result.extend ('U') -- Ü
when '%/221/' then Result.extend ('Y') -- Ý
when '%/224/' then Result.extend ('a') -- à
when '%/225/' then Result.extend ('a') -- á
when '%/226/' then Result.extend ('a') -- â
when '%/227/' then Result.extend ('a') -- ã
when '%/228/' then Result.extend ('a') -- ä
when '%/229/' then Result.extend ('a') -- å
when '%/231/' then Result.extend ('c') -- ç
when '%/232/' then Result.extend ('e') -- è
when '%/233/' then Result.extend ('e') -- é
when '%/234/' then Result.extend ('e') -- ê
when '%/235/' then Result.extend ('e') -- ë
when '%/236/' then Result.extend ('i') -- ì
when '%/237/' then Result.extend ('i') -- í
when '%/238/' then Result.extend ('i') -- î
when '%/239/' then Result.extend ('i') -- ï
when '%/240/' then Result.extend ('o') -- ð
when '%/242/' then Result.extend ('o') -- ò
when '%/243/' then Result.extend ('o') -- ó
when '%/244/' then Result.extend ('o') -- ô
when '%/245/' then Result.extend ('o') -- õ
when '%/246/' then Result.extend ('o') -- ö
when '%/249/' then Result.extend ('u') -- ù
when '%/250/' then Result.extend ('u') -- ú
when '%/251/' then Result.extend ('u') -- û
when '%/252/' then Result.extend ('u') -- ü
when '%/253/' then Result.extend ('y') -- ý
when '%/255/' then Result.extend ('y') -- ÿ
else
Result.extend ('-')
end
end
i := i + 1
end
end
feature -- Basic operation
move_to (a_destination: READABLE_STRING_GENERAL): BOOLEAN
@@ -317,7 +241,7 @@ feature -- Element change
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -148,8 +148,10 @@ feature {WSF_RESPONSE} -- Output
</style>
</head>
<body>
<div id="header">]" + l_html_error_code_text + "[!!</div>
<div id="header">
]")
s.append (l_html_error_code_text)
s.append ("!!</div>")
s.append ("<div id=%"logo%">")
s.append ("<div class=%"outter%"> ")
s.append ("<div class=%"inner1%"></div>")

View File

@@ -55,6 +55,8 @@ feature {WSF_RESPONSE} -- Output
res.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
s := "Error 501 Not Implemented ! "
s.append (request.request_method)
s.append (" ")
s.append (request.request_uri)
if attached body as b then
s.append ("%N")
@@ -69,7 +71,7 @@ feature {WSF_RESPONSE} -- Output
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2013, 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

@@ -98,14 +98,14 @@ feature {WSF_RESPONSE} -- Output
h.put_content_type_text_plain
end
end
res.put_header_text (h.string)
res.put_header_lines (h)
if b /= Void then
res.put_string (b)
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -45,7 +45,7 @@ feature {WSF_RESPONSE} -- Output
req := request
if attached req.raw_header_data as l_header then
create s.make (l_header.count)
s.append (l_header.to_string_8)
s.append (l_header.to_string_8) -- Is valid as string 8, as ensured by req.raw_header_data
s.append_character ('%N')
else
create s.make_empty
@@ -99,7 +99,7 @@ feature {WSF_RESPONSE} -- Output
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -56,12 +56,12 @@ feature {NONE} -- Initialization
launch
end
frozen make_callback (a_callback: like {WSF_CALLBACK_SERVICE}.callback; a_options: like options)
frozen make_callback (a_callback: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; a_options: like options)
do
make (create {WSF_CALLBACK_SERVICE}.make (a_callback), a_options)
end
frozen make_callback_and_launch (a_callback: like {WSF_CALLBACK_SERVICE}.callback; a_options: like options)
frozen make_callback_and_launch (a_callback: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; a_options: like options)
do
make (create {WSF_CALLBACK_SERVICE}.make (a_callback), a_options)
end
@@ -120,7 +120,7 @@ invariant
connector_attached: connector /= Void
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -0,0 +1,171 @@
note
description: "Summary description for {WSF_FILE_UTILITIES}."
date: "$Date$"
revision: "$Revision$"
expanded class
WSF_FILE_UTILITIES [G -> FILE create make_with_path end]
feature -- Factory
new_temporary_file (d: DIRECTORY; a_prefix: detachable READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable G
-- New temporary file open for writing inside directory `d', with prefix `a_prefix' is set, and based on name `a_name' is set.
-- If it is unable to create such file opened for writing, then return Void.
require
d_valid: d.exists and then d.is_writable
local
f: G
fn: PATH
bn, tmp: STRING_32
dn: PATH
n: INTEGER
do
from
if a_prefix /= Void then
create tmp.make_from_string_general (a_prefix)
else
create tmp.make_from_string_general ("tmp")
end
dn := d.path
if a_name /= Void then
tmp.append_character ('-')
tmp.append_string_general (safe_filename (a_name))
end
fn := dn.extended (tmp)
create f.make_with_path (fn)
Result := new_file_opened_for_writing (f)
n := 0
until
Result /= Void
or else n > 1_000
loop
n := n + 1
create bn.make_from_string (tmp)
bn.append_character ('-')
bn.append_integer (n)
fn := dn.extended (bn)
f.make_with_path (fn)
Result := new_file_opened_for_writing (f)
end
ensure
result_opened_for_writing_if_set: Result /= Void implies Result.is_open_write
end
safe_filename (fn: READABLE_STRING_GENERAL): STRING
-- Safe filename that avoid impossible filename, or dangerous one.
local
c: CHARACTER_32
i, n: INTEGER
do
from
i := 1
n := fn.count
create Result.make (n)
until
i > n
loop
c := fn[i]
inspect c
when '.', '-', '_' then
Result.append_code (c.natural_32_code)
when 'A' .. 'Z', 'a' .. 'z', '0' .. '9' then
Result.append_code (c.natural_32_code)
else
inspect c
when '%/192/' then Result.extend ('A') -- À
when '%/193/' then Result.extend ('A') -- Á
when '%/194/' then Result.extend ('A') -- Â
when '%/195/' then Result.extend ('A') -- Ã
when '%/196/' then Result.extend ('A') -- Ä
when '%/197/' then Result.extend ('A') -- Å
when '%/199/' then Result.extend ('C') -- Ç
when '%/200/' then Result.extend ('E') -- È
when '%/201/' then Result.extend ('E') -- É
when '%/202/' then Result.extend ('E') -- Ê
when '%/203/' then Result.extend ('E') -- Ë
when '%/204/' then Result.extend ('I') -- Ì
when '%/205/' then Result.extend ('I') -- Í
when '%/206/' then Result.extend ('I') -- Î
when '%/207/' then Result.extend ('I') -- Ï
when '%/210/' then Result.extend ('O') -- Ò
when '%/211/' then Result.extend ('O') -- Ó
when '%/212/' then Result.extend ('O') -- Ô
when '%/213/' then Result.extend ('O') -- Õ
when '%/214/' then Result.extend ('O') -- Ö
when '%/217/' then Result.extend ('U') -- Ù
when '%/218/' then Result.extend ('U') -- Ú
when '%/219/' then Result.extend ('U') -- Û
when '%/220/' then Result.extend ('U') -- Ü
when '%/221/' then Result.extend ('Y') -- Ý
when '%/224/' then Result.extend ('a') -- à
when '%/225/' then Result.extend ('a') -- á
when '%/226/' then Result.extend ('a') -- â
when '%/227/' then Result.extend ('a') -- ã
when '%/228/' then Result.extend ('a') -- ä
when '%/229/' then Result.extend ('a') -- å
when '%/231/' then Result.extend ('c') -- ç
when '%/232/' then Result.extend ('e') -- è
when '%/233/' then Result.extend ('e') -- é
when '%/234/' then Result.extend ('e') -- ê
when '%/235/' then Result.extend ('e') -- ë
when '%/236/' then Result.extend ('i') -- ì
when '%/237/' then Result.extend ('i') -- í
when '%/238/' then Result.extend ('i') -- î
when '%/239/' then Result.extend ('i') -- ï
when '%/240/' then Result.extend ('o') -- ð
when '%/242/' then Result.extend ('o') -- ò
when '%/243/' then Result.extend ('o') -- ó
when '%/244/' then Result.extend ('o') -- ô
when '%/245/' then Result.extend ('o') -- õ
when '%/246/' then Result.extend ('o') -- ö
when '%/249/' then Result.extend ('u') -- ù
when '%/250/' then Result.extend ('u') -- ú
when '%/251/' then Result.extend ('u') -- û
when '%/252/' then Result.extend ('u') -- ü
when '%/253/' then Result.extend ('y') -- ý
when '%/255/' then Result.extend ('y') -- ÿ
else
Result.extend ('-')
end
end
i := i + 1
end
end
feature {NONE} -- Implementation
new_file_opened_for_writing (f: G): detachable G
-- Returns a new file object opened for writing if possible
-- otherwise returns Void.
local
retried: BOOLEAN
do
if not retried then
if not f.exists then
f.open_write
if f.is_open_write then
Result := f
elseif not f.is_closed then
f.close
end
end
end
ensure
Result /= Void implies Result.is_open_write
rescue
retried := True
retry
end
note
copyright: "2011-2014, 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

@@ -16,6 +16,9 @@ note
And also has
execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY
--| to keep value attached to the request
About https support: `is_https' indicates if the request is made through an https connection or not.
]"
date: "$Date$"
revision: "$Revision$"
@@ -120,6 +123,20 @@ feature {NONE} -- Initialization
if meta_variable ({WSF_META_NAMES}.request_time) = Void then
set_meta_string_variable ({WSF_META_NAMES}.request_time, date_time_utilities.unix_time_stamp (Void).out)
end
--| HTTPS support
is_https := False
if attached meta_string_variable ("HTTPS") as l_https and then not l_https.is_empty then
is_https := l_https.is_case_insensitive_equal_general ("on")
or else l_https.is_case_insensitive_equal_general ("yes")
or else l_https.is_case_insensitive_equal_general ("true")
or else l_https.is_case_insensitive_equal_general ("1")
--| Usually, if not empty, this means this is https
--| but it occurs that server (like IIS) sets "off" when this is NOT https
--| so, let's be flexible, and accepts other variants of "on"
else
check is_not_https: is_https = False end
end
end
wgi_request: WGI_REQUEST
@@ -156,10 +173,15 @@ feature -- Destroy
raw_input_data_recorded := False
request_method := empty_string_8
set_uploaded_file_path (Void)
is_https := False
end
feature -- Status report
is_https: BOOLEAN
-- Is https connection?
--| based on meta variable HTTPS=on .
debug_output: STRING_8
do
create Result.make_from_string (request_method + " " + request_uri)
@@ -270,7 +292,9 @@ feature -- Access: Input
s: STRING
l_input: WGI_INPUT_STREAM
l_raw_data: detachable STRING_8
n: INTEGER
len: NATURAL_64
nb, l_step: INTEGER
l_size: NATURAL_64
do
if raw_input_data_recorded and then attached raw_input_data as d then
a_file.put_string (d)
@@ -279,22 +303,47 @@ feature -- Access: Input
create l_raw_data.make_empty
end
l_input := input
len := content_length_value
debug ("wsf")
io.error.put_string (generator + ".read_input_data_into_file (a_file) content_length=" + len.out + "%N")
end
from
n := 8_192
create s.make (n)
l_size := 0
l_step := 8_192
create s.make (l_step)
until
n = 0 or l_input.end_of_input
l_step = 0 or l_input.end_of_input
loop
l_input.append_to_string (s, n)
a_file.put_string (s)
if l_raw_data /= Void then
l_raw_data.append (s)
if len < l_step.to_natural_64 then
l_step := len.to_integer_32
end
s.wipe_out
if l_input.last_appended_count < n then
n := 0
if l_step > 0 then
l_input.append_to_string (s, l_step)
nb := l_input.last_appended_count
l_size := l_size + nb.to_natural_64
len := len - nb.to_natural_64
debug ("wsf")
io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N")
end
a_file.put_string (s)
if l_raw_data /= Void then
l_raw_data.append (s)
end
s.wipe_out
if nb < l_step then
l_step := 0
end
end
end
a_file.flush
debug ("wsf")
io.error.put_string ("offset =" + len.out + "%N")
end
check got_all_data: len = 0 end
if l_raw_data /= Void then
set_raw_input_data (l_raw_data)
end
@@ -351,31 +400,9 @@ feature -- Helper
-- Does client accepts content_type for the response?
--| Based on header "Accept:" that can be for instance
--| text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
local
i, j: INTEGER
do
if attached http_accept as l_accept then
i := l_accept.substring_index (a_content_type, 1)
if i > 0 then
-- contains the text, now check if this is the exact text
if
i = 1 -- At the beginning of text
or else l_accept[i-1].is_space -- preceded by space
or else l_accept[i-1] = ',' -- preceded by other mime type
then
j := i + a_content_type.count
if l_accept.valid_index (j) then
Result := l_accept[j] = ',' -- followed by other mime type
or else l_accept[j] = ';' -- followed by quality ;q=...
or else l_accept[j].is_space -- followed by space
else -- end of text
Result := True
end
end
end
Result := l_accept.has_substring (a_content_type)
else
Result := True
if attached (create {SERVER_MEDIA_TYPE_NEGOTIATION}.make (a_content_type.as_string_8)).preference (<<a_content_type.as_string_8>>, http_accept) as l_variants then
Result := l_variants.is_acceptable
end
end
@@ -1260,41 +1287,43 @@ feature {NONE} -- Cookies
local
i,j,p,n: INTEGER
l_cookies: like internal_cookies_table
s32: READABLE_STRING_32
k,v,s: STRING
do
l_cookies := internal_cookies_table
if l_cookies = Void then
create l_cookies.make_equal (0)
if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then
s := val.value
create l_cookies.make_equal (5)
from
n := s.count
p := 1
i := 1
until
p < 1
loop
i := s.index_of ('=', p)
if i > 0 then
j := s.index_of (';', i)
if j = 0 then
j := n + 1
k := s.substring (p, i - 1)
v := s.substring (i + 1, n)
s32 := val.value
if s32.is_valid_as_string_8 then
s := s32.to_string_8
from
n := s.count
p := 1
i := 1
until
p < 1
loop
i := s.index_of ('=', p)
if i > 0 then
j := s.index_of (';', i)
if j = 0 then
j := n + 1
k := s.substring (p, i - 1)
v := s.substring (i + 1, n)
p := 0 -- force termination
else
k := s.substring (p, i - 1)
v := s.substring (i + 1, j - 1)
p := j + 1
p := 0 -- force termination
else
k := s.substring (p, i - 1)
v := s.substring (i + 1, j - 1)
p := j + 1
end
k.left_adjust
k.right_adjust
add_value_to_table (k, v, l_cookies)
end
k.left_adjust
k.right_adjust
add_value_to_table (k, v, l_cookies)
end
end
else
create l_cookies.make_equal (0)
end
internal_cookies_table := l_cookies
end
@@ -1443,8 +1472,12 @@ feature {NONE} -- Query parameters: implementation
if j > 0 then
l_name := s.substring (1, j - 1)
l_value := s.substring (j + 1, s.count)
add_value_to_table (l_name, l_value, Result)
else
-- I.e variable without value
l_name := s
l_value := empty_string_8
end
add_value_to_table (l_name, l_value, Result)
end
end
end
@@ -1729,10 +1762,7 @@ feature -- URL Utility
do
s := internal_server_url
if s = Void then
if
server_protocol.count >= 5 and then
server_protocol.substring (1, 5).is_case_insensitive_equal ("https")
then
if is_https then
create s.make_from_string ("https://")
else
create s.make_from_string ("http://")
@@ -1740,8 +1770,14 @@ feature -- URL Utility
s.append (server_name)
p := server_port
if p > 0 then
s.append_character (':')
s.append_integer (p)
if is_https and p = 443 then
-- :443 is default for https, so no need to put it
elseif not is_https and p = 80 then
-- :80 is default for http, so no need to put it
else
s.append_character (':')
s.append_integer (p)
end
end
end
Result := s
@@ -1853,16 +1889,14 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
end
save_uploaded_file (a_up_file: WSF_UPLOADED_FILE; a_content: STRING)
-- Save uploaded file content to `a_filename'
-- Save uploaded file content `a_content' into `a_filename'.
local
bn: STRING
l_safe_name: STRING
f: RAW_FILE
dn: PATH
fn: PATH
d: DIRECTORY
n: INTEGER
rescued: BOOLEAN
temp_fac: WSF_FILE_UTILITIES [RAW_FILE]
l_prefix: STRING
dt: DATE_TIME
do
if not rescued then
if attached uploaded_file_path as p then
@@ -1873,26 +1907,24 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
end
create d.make_with_path (dn)
if d.exists and then d.is_writable then
l_safe_name := a_up_file.safe_filename
from
bn := "tmp-" + l_safe_name
fn := dn.extended (bn)
create f.make_with_path (fn)
n := 0
until
not f.exists
or else n > 1_000
loop
n := n + 1
bn := "tmp-" + n.out + "-" + l_safe_name
fn := dn.extended (bn)
f.make_with_path (fn)
end
create temp_fac
if not f.exists or else f.is_writable then
create l_prefix.make_from_string ("tmp_uploaded_")
create dt.make_now_utc
l_prefix.append_integer (dt.date.ordered_compact_date)
l_prefix.append_character ('_')
l_prefix.append_integer (dt.time.compact_time)
l_prefix.append_character ('.')
l_prefix.append_integer ((dt.time.fractional_second * 1_000_000_000).truncated_to_integer)
if attached temp_fac.new_temporary_file (d, l_prefix, a_up_file.filename) as f then
a_up_file.set_tmp_path (f.path)
a_up_file.set_tmp_basename (bn)
f.open_write
if attached f.path.entry as e then
a_up_file.set_tmp_basename (e.name)
else
a_up_file.set_tmp_basename (f.path.name) -- Should not occurs.
end
check f.is_open_write end
f.put_string (a_content)
f.close
else
@@ -2059,7 +2091,7 @@ invariant
wgi_request.content_type /= Void implies content_type /= Void
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -35,15 +35,17 @@ feature {NONE} -- Initialization
wres: detachable WSF_WGI_DELAYED_HEADER_RESPONSE
do
transfered_content_length := 0
create header.make
create internal_header.make
wgi_response := r
if attached {WSF_WGI_DELAYED_HEADER_RESPONSE} r as r_delayed then
wres := r_delayed
wres.update_wsf_response (Current)
r_delayed.update_wsf_response (Current)
wgi_response := r_delayed
elseif attached {WGI_FILTER_RESPONSE} r as r_filter then
wgi_response := r_filter.wgi_response
else
create wres.make (r, Current)
wgi_response := wres
end
wgi_response := wres
set_status_code ({HTTP_STATUS_CODE}.ok) -- Default value
end
@@ -51,7 +53,7 @@ feature {NONE} -- Initialization
do
transfered_content_length := 0
wgi_response := res.wgi_response
header := res.header
internal_header := res.internal_header
set_status_code ({HTTP_STATUS_CODE}.ok) -- Default value
end
@@ -60,7 +62,7 @@ feature {WSF_RESPONSE, WSF_RESPONSE_EXPORTER} -- Properties
wgi_response: WGI_RESPONSE
-- Associated WGI_RESPONSE.
header: WSF_HEADER
internal_header: WSF_HEADER
-- Associated response header.
feature {WSF_RESPONSE_EXPORTER} -- Change
@@ -156,7 +158,7 @@ feature {WSF_RESPONSE_EXPORTER} -- Header output operation
-- commit status code and reason phrase
wgi_response.set_status_code (status_code, status_reason_phrase)
-- commit header text
wgi_response.put_header_text (header.string)
wgi_response.put_header_text (internal_header.string)
end
ensure
status_committed: status_committed
@@ -168,6 +170,26 @@ feature {WSF_RESPONSE_EXPORTER} -- Header output operation
put_error ("Content already sent, new header text ignored!")
end
feature -- Header access
header: HTTP_HEADER_MODIFIER
-- Associated header builder interface.
local
res: like internal_response_header
do
res := internal_response_header
if res = Void then
create {WSF_RESPONSE_HEADER} res.make_with_response (Current)
internal_response_header := res
end
Result := res
end
feature {NONE} -- Header access
internal_response_header: detachable like header
-- Cached version of `header'.
feature -- Header output operation
put_header_line (h: READABLE_STRING_8)
@@ -179,7 +201,7 @@ feature -- Header output operation
if header_committed then
report_content_already_sent_and_header_ignored
else
header.put_header (h)
internal_header.put_header (h)
end
end
@@ -192,7 +214,7 @@ feature -- Header output operation
if header_committed then
report_content_already_sent_and_header_ignored
else
header.add_header (h)
internal_header.add_header (h)
end
end
@@ -207,7 +229,7 @@ feature -- Header output operation
if header_committed then
report_content_already_sent_and_header_ignored
else
header.put_raw_header_data (a_text)
internal_header.put_raw_header_data (a_text)
end
ensure
message_writable: message_writable
@@ -225,7 +247,7 @@ feature -- Header output operation
if header_committed then
report_content_already_sent_and_header_ignored
else
header.append_raw_header_data (a_text)
internal_header.append_raw_header_data (a_text)
end
ensure
status_set: status_is_set
@@ -494,7 +516,7 @@ feature -- Error reporting
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2014, 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

@@ -0,0 +1,64 @@
note
description: "[
Interface to build the http header associated with WSF_RESPONSE.
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_RESPONSE_HEADER
inherit
HTTP_HEADER_MODIFIER
WSF_RESPONSE_EXPORTER -- to access WSF_RESPONSE.internal_header
create
make_with_response
feature {NONE} -- Initialization
make_with_response (res: WSF_RESPONSE)
do
response := res
end
feature -- Access
response: WSF_RESPONSE
feature -- Access
new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8]
-- Fresh cursor associated with current structure.
do
Result := response.internal_header.new_cursor
end
feature -- Header change: core
add_header (h: READABLE_STRING_8)
-- Add header `h'
-- if it already exists, there will be multiple header with same name
-- which can also be valid
do
response.add_header_line (h)
end
put_header (h: READABLE_STRING_8)
-- Add header `h' or replace existing header of same header name
do
response.put_header_line (h)
end
note
copyright: "2011-2014, 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