diff --git a/library/network/protocol/http/http-safe.ecf b/library/network/protocol/http/http-safe.ecf index 9a61b2bd..93ad8e1a 100644 --- a/library/network/protocol/http/http-safe.ecf +++ b/library/network/protocol/http/http-safe.ecf @@ -12,6 +12,10 @@ - + + + /tests$ + + diff --git a/library/network/protocol/http/http.ecf b/library/network/protocol/http/http.ecf index ab2320f6..003c0708 100644 --- a/library/network/protocol/http/http.ecf +++ b/library/network/protocol/http/http.ecf @@ -12,7 +12,11 @@ - + + + /tests$ + + diff --git a/library/network/protocol/http/src/http_date.e b/library/network/protocol/http/src/http_date.e new file mode 100644 index 00000000..ac7b9c83 --- /dev/null +++ b/library/network/protocol/http/src/http_date.e @@ -0,0 +1,596 @@ +note + description: "[ + Summary description for {HTTP_DATE}. + + + HTTP-date = rfc1123-date | rfc850-date | asctime-date + rfc1123-date = wkday "," SP date1 SP time SP "GMT" + rfc850-date = weekday "," SP date2 SP time SP "GMT" + asctime-date = wkday SP date3 SP time SP 4DIGIT + date1 = 2DIGIT SP month SP 4DIGIT + ; day month year (e.g., 02 Jun 1982) + date2 = 2DIGIT "-" month "-" 2DIGIT + ; day-month-year (e.g., 02-Jun-82) + date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) + ; month day (e.g., Jun 2) + time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + ; 00:00:00 - 23:59:59 + wkday = "Mon" | "Tue" | "Wed" + | "Thu" | "Fri" | "Sat" | "Sun" + weekday = "Monday" | "Tuesday" | "Wednesday" + | "Thursday" | "Friday" | "Saturday" | "Sunday" + month = "Jan" | "Feb" | "Mar" | "Apr" + | "May" | "Jun" | "Jul" | "Aug" + | "Sep" | "Oct" | "Nov" | "Dec" + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + + Not supported: + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + ]" + date: "$Date$" + revision: "$Revision$" + EIS: "name=RFC2616", "protocol=URI", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html" + +class + HTTP_DATE + +inherit + DEBUG_OUTPUT + +create + make_from_timestamp, + make_from_string, + make_from_date_time + +feature {NONE} -- Initialization + + make_from_timestamp (n: INTEGER_64) + -- Build from unix timestamp `n' + do + internal_timestamp := n + --| FIXME: find workaround when `n' is not INTEGER_32 + create date_time.make_from_epoch (n.as_integer_32) + end + + make_from_string (s: READABLE_STRING_8) + -- Create from string representation `s' + -- Supports: RFC 1123 and RFC 850 + -- Tolerant with: GMT+offset and GMT-offset + --| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + --| Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + --| Does not support ANSI C's asctime() format + do + if attached string_to_date_time (s) as dt then + date_time := dt + else + has_error := True + date_time := epoch + end + end + + make_from_date_time (dt: DATE_TIME) + -- Build from date `dt' + do + date_time := dt + end + +feature -- Access + + has_error: BOOLEAN + -- Error occurred during creation with `make_from_string'? + + date_time: DATE_TIME + -- Associated Date time. + + string: STRING + -- String representation recommended for HTTP date. + --| Sun, 06 Nov 1994 08:49:37 GMT + do + Result := rfc1123_string + end + + rfc1123_string: STRING + -- String representation following RFC 1123. + --| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + local + s: like internal_rfc1123_string + do + s := internal_rfc1123_string + if s = Void then + s := date_time_to_rfc1123_string (date_time) + internal_rfc1123_string := s + end + Result := s + end + + rfc850_string: STRING + -- String representation following RFC 850 + do + Result := date_time_to_rfc850_string (date_time) + end + + timestamp: INTEGER_64 + -- Unix timestamp. + do + Result := internal_timestamp + if Result = 0 then + Result := date_time.definite_duration (epoch).seconds_count + internal_timestamp := Result + end + end + +feature -- Status report + + debug_output: STRING + do + if attached internal_rfc1123_string as st then + Result := st.string + else + Result := date_time_to_rfc1123_string (date_time) + end + end + +feature {NONE} -- Implementation + + epoch: DATE_TIME + once ("THREAD") + create Result.make_from_epoch (0) + end + + internal_timestamp: like timestamp + + internal_rfc1123_string: detachable STRING + + date_time_to_rfc1123_string (dt: DATE_TIME): STRING + local + i: INTEGER + do + create Result.make (32) + i := dt.date.day_of_the_week + inspect i + when 1 then Result.append ("Sun") + when 2 then Result.append ("Mon") + when 3 then Result.append ("Tue") + when 4 then Result.append ("Wed") + when 5 then Result.append ("Thu") + when 6 then Result.append ("Fri") + when 7 then Result.append ("Sat") + else + -- Error + end + Result.append_character (',') + + -- SPace + Result.append_character (' ') + + -- dd + i := dt.day + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- SPace + Result.append_character (' ') + + -- mmm + i := dt.month + inspect i + when 1 then Result.append ("Jan") + when 2 then Result.append ("Feb") + when 3 then Result.append ("Mar") + when 4 then Result.append ("Apr") + when 5 then Result.append ("May") + when 6 then Result.append ("Jun") + when 7 then Result.append ("Jul") + when 8 then Result.append ("Aou") + when 9 then Result.append ("Sep") + when 10 then Result.append ("Oct") + when 11 then Result.append ("Nov") + when 12 then Result.append ("Dec") + else + -- Error + end + + -- SPace + Result.append_character (' ') + + -- yyyy + Result.append_integer (dt.year) + + -- SPace + Result.append_character (' ') + + -- hh + i := dt.hour + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- ':' + Result.append_character (':') + + -- mi + i := dt.minute + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- ':' + Result.append_character (':') + + -- ss + i := dt.second + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- SPace + GMT + Result.append (" GMT") + end + + date_time_to_rfc850_string (dt: DATE_TIME): STRING + local + i: INTEGER + do + create Result.make (32) + i := dt.date.day_of_the_week + inspect i + when 1 then Result.append ("Sunday") + when 2 then Result.append ("Monday") + when 3 then Result.append ("Tuesday") + when 4 then Result.append ("Wednesday") + when 5 then Result.append ("Thursday") + when 6 then Result.append ("Friday") + when 7 then Result.append ("Satursday") + else + -- Error + end + Result.append_character (',') + + -- SPace + Result.append_character (' ') + + -- dd + i := dt.day + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- '-' + Result.append_character ('-') + + -- mmm + i := dt.month + inspect i + when 1 then Result.append ("Jan") + when 2 then Result.append ("Feb") + when 3 then Result.append ("Mar") + when 4 then Result.append ("Apr") + when 5 then Result.append ("May") + when 6 then Result.append ("Jun") + when 7 then Result.append ("Jul") + when 8 then Result.append ("Aou") + when 9 then Result.append ("Sep") + when 10 then Result.append ("Oct") + when 11 then Result.append ("Nov") + when 12 then Result.append ("Dec") + else + -- Error + end + + -- '-' + Result.append_character ('-') + + -- yy + Result.append_integer (dt.year \\ 100) + + -- SPace + Result.append_character (' ') + + -- hh + i := dt.hour + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- ':' + Result.append_character (':') + + -- mi + i := dt.minute + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- ':' + Result.append_character (':') + + -- ss + i := dt.second + if i <= 9 then + Result.append_character ('0') + end + Result.append_integer (i) + + -- SPace + GMT + Result.append (" GMT") + end + + string_to_date_time (s: READABLE_STRING_8): detachable DATE_TIME + -- String representation of `dt' using the RFC 1123 + -- HTTP-date = rfc1123-date | rfc850-date | asctime-date + -- rfc1123-date = wkday "," SP date1 SP time SP "GMT" + -- rfc850-date = weekday "," SP date2 SP time SP "GMT" + -- asctime-date = wkday SP date3 SP time SP 4DIGIT + -- date1 = 2DIGIT SP month SP 4DIGIT + -- ; day month year (e.g., 02 Jun 1982) + -- date2 = 2DIGIT "-" month "-" 2DIGIT + -- ; day-month-year (e.g., 02-Jun-82) + -- date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) + -- ; month day (e.g., Jun 2) + -- time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + -- ; 00:00:00 - 23:59:59 + -- wkday = "Mon" | "Tue" | "Wed" + -- | "Thu" | "Fri" | "Sat" | "Sun" + -- weekday = "Monday" | "Tuesday" | "Wednesday" + -- | "Thursday" | "Friday" | "Saturday" | "Sunday" + -- month = "Jan" | "Feb" | "Mar" | "Apr" + -- | "May" | "Jun" | "Jul" | "Aug" + -- | "Sep" | "Oct" | "Nov" | "Dec" + --| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + --| Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + --| + --| ANSI C's format not support for now + --| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + note + EIS: "name=RFC2616", "protocol=URI", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html" + local + t: STRING_8 + l_ddd, l_mmm: detachable STRING_8 + l_dd, l_yyyy, l_hh, l_mi, l_ss, l_ff2: INTEGER + l_mo: INTEGER + l_gmt_offset: INTEGER + i, n: INTEGER + err: BOOLEAN + do + i := 1 + n := s.count + create t.make (4) + + -- Skip blanks + from until i > n or else not s[i].is_space loop i := i + 1 end + + --| ddd + t.wipe_out + from until i > n or else not s[i].is_alpha loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count >= 3 then -- Accept full day string + l_ddd := t.as_lower + else + err := True + end + + --| blanks + from until i > n or else not s[i].is_space loop i := i + 1 end + + --| ,[0]dd + if not err and i <= n and s[i] = ',' then + i := i + 1 + from until i > n or else not s[i].is_space loop i := i + 1 end + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 then + check t.is_integer end + l_dd := t.to_integer + else + err := True + end + else + err := True + end + + --| blanks or '-' + if s[i] = '-' then + i := i + 1 + else + from until i > n or else not s[i].is_space loop i := i + 1 end + end + + --| mmm + if not err then + t.wipe_out + from until i > n or else not s[i].is_alpha loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count = 3 then + l_mmm := t.as_upper + if l_mmm.same_string ("JAN") then l_mo := 01 + elseif l_mmm.same_string ("FEB") then l_mo := 02 + elseif l_mmm.same_string ("MAR") then l_mo := 03 + elseif l_mmm.same_string ("APR") then l_mo := 04 + elseif l_mmm.same_string ("MAY") then l_mo := 05 + elseif l_mmm.same_string ("JUN") then l_mo := 06 + elseif l_mmm.same_string ("JUL") then l_mo := 07 + elseif l_mmm.same_string ("AOU") then l_mo := 08 + elseif l_mmm.same_string ("SEP") then l_mo := 09 + elseif l_mmm.same_string ("OCT") then l_mo := 10 + elseif l_mmm.same_string ("NOV") then l_mo := 11 + elseif l_mmm.same_string ("DEC") then l_mo := 12 + else err := True + end + else + err := True + end + end + + --| blanks or '-' + if s[i] = '-' then + i := i + 1 + else + from until i > n or else not s[i].is_space loop i := i + 1 end + end + + --| yyyy + if not err then + + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 then + check t.count = 4 or t.count = 2 and t.is_integer end + l_yyyy := t.to_integer + if t.count = 2 then + -- RFC 850 + l_yyyy := 1900 + l_yyyy + end + else + err := True + end + end + + --| blank + from until i > n or else not s[i].is_space loop i := i + 1 end + + --| [0]hh: + if not err then + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 and s[i] = ':' then + check t.count = 2 and t.is_integer end + l_hh := t.to_integer + i := i + 1 + else + err := True + end + end + + --| [0]mi: + if not err then + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 and s[i] = ':' then + check t.count = 2 and t.is_integer end + l_mi := t.to_integer + i := i + 1 + else + err := True + end + end + --| [0]ss + if not err then + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 then + check t.count = 2 and t.is_integer end + l_ss := t.to_integer + else + err := True + end + end + + --| .ff2 + if not err and s[i] = '.' then + --| .ff2 + i := i + 1 + t.wipe_out + from until i > n or else not s[i].is_digit loop + t.extend (s[i]) + i := i + 1 + end + if i <= n and t.count > 0 then + check t.is_integer end + l_ff2 := t.to_integer + else + err := True + end + end + + if not err then + from until i > n or else not s[i].is_space loop i := i + 1 end + t.wipe_out + from until i > n or else not s[i].is_alpha loop + t.extend (s[i].as_upper) + i := i + 1 + end + if t.same_string ("GMT") then + from until i > n or else not s[i].is_space loop i := i + 1 end + if i <= n then + t.wipe_out + if s[i] = '+' then + t.extend (s[i]) + elseif s[i] = '-' then + t.extend (s[i]) + else + err := True + end + if not err then + from until i > n or else not s[i].is_digit loop + t.extend (s[i].as_upper) + i := i + 1 + end + l_gmt_offset := t.to_integer + end + end + else + err := True + end + end + + if not err then + check + valid_yyyy: 0 < l_yyyy + valid_dd: 0 < l_dd and l_dd <= 31 + valid_mo: 0 < l_mo and l_mo <= 12 + valid_hh: 0 <= l_hh and l_hh <= 23 + valid_mi: 0 <= l_mi and l_mi <= 59 + valid_ss: 0 <= l_ss and l_ss <= 59 + end + create Result.make (l_yyyy, l_mo, l_dd, l_hh, l_mi, l_ss) + if l_gmt_offset /= 0 then + Result.hour_add (- l_gmt_offset) + end + else +-- create Result.make_utc_now + end + end + +invariant + +note + copyright: "2011-2013, Jocelyn Fiat, 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 diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index 9a135b18..8e78fba6 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -160,7 +160,7 @@ feature -- Conversion loop append_line_to (c.item, a_result) end - end + end end feature -- Access @@ -763,8 +763,11 @@ feature {NONE} -- Implementation date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8 -- String representation of `dt' using the RFC 1123 + local + d: HTTP_DATE do - Result := dt.formatted_out ("ddd, [0]dd mmm yyyy [0]hh:[0]mi:[0]ss") + " GMT" + create d.make_from_date_time (dt) + Result := d.string end feature {NONE} -- Constants diff --git a/library/network/protocol/http/tests/autotest.e b/library/network/protocol/http/tests/autotest.e new file mode 100644 index 00000000..a8e34bac --- /dev/null +++ b/library/network/protocol/http/tests/autotest.e @@ -0,0 +1,15 @@ +class + AUTOTEST + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + + end + +end diff --git a/library/network/protocol/http/tests/http_date_test_set.e b/library/network/protocol/http/tests/http_date_test_set.e new file mode 100644 index 00000000..79df3288 --- /dev/null +++ b/library/network/protocol/http/tests/http_date_test_set.e @@ -0,0 +1,45 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + HTTP_DATE_TEST_SET + +inherit + EQA_TEST_SET + +feature -- Test routines + + test_http_date + -- New test routine + local + s: STRING + d: HTTP_DATE + do + s := "Sun, 06 Nov 1994 08:49:37 GMT" + + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string (s)) + create d.make_from_timestamp (d.timestamp) + assert ("RFC 1123", not d.has_error and then d.string.same_string (s)) + + s := "Sunday, 06-Nov-94 08:49:37 GMT" + create d.make_from_string (s) + assert ("RFC 850", not d.has_error and then d.rfc850_string.same_string (s)) + create d.make_from_timestamp (d.timestamp) + assert ("RFC 850", not d.has_error and then d.rfc850_string.same_string (s)) + + + s := "Sun Nov 6 08:49:37 1994" + create d.make_from_string (s) + assert ("ANSI format", d.has_error) + + + end + +end diff --git a/library/network/protocol/http/tests/tests-safe.ecf b/library/network/protocol/http/tests/tests-safe.ecf new file mode 100644 index 00000000..958be121 --- /dev/null +++ b/library/network/protocol/http/tests/tests-safe.ecf @@ -0,0 +1,18 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + diff --git a/library/network/protocol/http/tests/tests.ecf b/library/network/protocol/http/tests/tests.ecf new file mode 100644 index 00000000..d3f421e2 --- /dev/null +++ b/library/network/protocol/http/tests/tests.ecf @@ -0,0 +1,18 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + diff --git a/library/server/wsf/router/wsf_file_system_handler.e b/library/server/wsf/router/wsf_file_system_handler.e index 2cbc7473..5642842b 100644 --- a/library/server/wsf/router/wsf_file_system_handler.e +++ b/library/server/wsf/router/wsf_file_system_handler.e @@ -203,7 +203,8 @@ feature -- Execution end if attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then - attached file_date (f) as f_date and then (f_date >= rfc1123_http_date_format_to_date (s_if_modified_since)) + attached rfc1123_http_date_format_to_date (s_if_modified_since) as l_if_modified_since_date and then + attached file_date (f) as f_date and then (f_date >= l_if_modified_since_date) then process_not_modified (f_date, req, res) else @@ -425,35 +426,59 @@ feature {NONE} -- Implementation feature {NONE} -- implementation: date time - date_time_utility: HTTP_DATE_TIME_UTILITIES - once - create Result - end - file_date (f: FILE): DATE_TIME do Result := timestamp_to_date (f.date) end - rfc1123_http_date_format_to_date (s: STRING): DATE_TIME + rfc1123_http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME -- String representation of `dt' using the RFC 1123 + -- HTTP-date = rfc1123-date | rfc850-date | asctime-date + -- rfc1123-date = wkday "," SP date1 SP time SP "GMT" + -- rfc850-date = weekday "," SP date2 SP time SP "GMT" + -- asctime-date = wkday SP date3 SP time SP 4DIGIT + -- date1 = 2DIGIT SP month SP 4DIGIT + -- ; day month year (e.g., 02 Jun 1982) + -- date2 = 2DIGIT "-" month "-" 2DIGIT + -- ; day-month-year (e.g., 02-Jun-82) + -- date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) + -- ; month day (e.g., Jun 2) + -- time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + -- ; 00:00:00 - 23:59:59 + -- wkday = "Mon" | "Tue" | "Wed" + -- | "Thu" | "Fri" | "Sat" | "Sun" + -- weekday = "Monday" | "Tuesday" | "Wednesday" + -- | "Thursday" | "Friday" | "Saturday" | "Sunday" + -- month = "Jan" | "Feb" | "Mar" | "Apr" + -- | "May" | "Jun" | "Jul" | "Aug" + -- | "Sep" | "Oct" | "Nov" | "Dec" + --| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + --| Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + --| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + --| + --| "ddd, [0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2" + --| ex: "WED, 30 JAN 2013 21:34:33 " + note + EIS: "name=RFC2616", "protocol=URI", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html" local - t: STRING + d: HTTP_DATE do - t := s - if t.ends_with ("GMT") then - t := t.substring (1, t.count - 4) + create d.make_from_string (s) + if not d.has_error then + Result := d.date_time end - create Result.make_from_string (t, "ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") end timestamp_to_date (n: INTEGER): DATE_TIME + local + d: HTTP_DATE do - Result := date_time_utility.unix_time_stamp_to_date_time (n) + create d.make_from_timestamp (n) + Result := d.date_time 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