diff --git a/library/network/protocol/http/src/http_date.e b/library/network/protocol/http/src/http_date.e index 088713b6..8bfde127 100644 --- a/library/network/protocol/http/src/http_date.e +++ b/library/network/protocol/http/src/http_date.e @@ -25,8 +25,6 @@ note 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 ]" @@ -61,10 +59,11 @@ feature {NONE} -- Initialization -- 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 + elseif attached ansi_c_string_to_date_time (s) as dt then + date_time := dt else has_error := True date_time := epoch @@ -92,26 +91,6 @@ feature -- Access 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 @@ -122,18 +101,7 @@ feature -- Access 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 +feature {NONE} -- Internal epoch: DATE_TIME once ("THREAD") @@ -144,188 +112,175 @@ feature {NONE} -- Implementation internal_rfc1123_string: detachable STRING - date_time_to_rfc1123_string (dt: DATE_TIME): STRING +feature -- Conversion to string + + rfc1123_string: STRING + -- String representation following RFC 1123. + --| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 local - i: INTEGER + s: like internal_rfc1123_string 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 + s := internal_rfc1123_string + if s = Void then + create s.make (32) + append_date_time_to_rfc1123_string (date_time, s) + internal_rfc1123_string := s 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") + Result := s end - date_time_to_rfc850_string (dt: DATE_TIME): STRING - local - i: INTEGER + rfc850_string: STRING + -- String representation following RFC 850 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") + append_date_time_to_rfc850_string (date_time, Result) end + ansi_c_string: STRING + -- ANSI C's asctime() format + do + create Result.make (32) + append_date_time_to_ansi_c_string (date_time, Result) + end + +feature -- Conversion into string + + append_date_time_to_rfc1123_string (dt: DATE_TIME; s: STRING) + do + append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd + s.append_character (',') -- ',' + s.append_character (' ') -- SPace + append_2_digits_integer_to (dt.day, s) -- dd + s.append_character (' ') -- SPace + append_month_mmm_to (dt.month, s) -- mmm + s.append_character (' ') -- SPace + s.append_integer (dt.year) -- yyyy + s.append_character (' ') -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append (" GMT") -- SPace + GMT + end + + append_date_time_to_rfc850_string (dt: DATE_TIME; s: STRING) + do + append_day_name_to (dt.date.day_of_the_week, s) -- mmm + s.append_character (',') -- , + s.append_character (' ') -- SPace + append_2_digits_integer_to (dt.day, s) -- dd + s.append_character ('-') -- '-' + append_month_mmm_to (dt.month, s) -- mmm + s.append_character ('-') -- '-' + s.append_integer (dt.year \\ 100) -- yy + s.append_character (' ') -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append (" GMT") -- SPace + GMT + end + + append_date_time_to_ansi_c_string (dt: DATE_TIME; s: STRING) + --| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + do + append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd + s.append_character (' ') -- SP + append_month_mmm_to (dt.month, s) -- mmm + s.append_character (' ') -- SPace + s.append_character (' ') -- SPace + s.append_integer (dt.day) -- d + s.append_character (' ') -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append_character (' ') -- SPace + s.append_integer (dt.year) -- yyyy + end + +feature -- Status report + + debug_output: STRING + do + if attached internal_rfc1123_string as st then + Result := st.string + else + create Result.make (32) + append_date_time_to_rfc1123_string (date_time, Result) + end + end + +feature {NONE} -- Implementation + + append_2_digits_integer_to (i: INTEGER; s: STRING) + require + is_not_negative: i >= 0 + do + if i <= 9 then + s.append_character ('0') + end + s.append_integer (i) + end + + append_2_digits_time_to (t: TIME; s: STRING) + do + append_2_digits_integer_to (t.hour, s) -- hh + s.append_character (':') -- : + append_2_digits_integer_to (t.minute, s) -- mi + s.append_character (':') -- : + append_2_digits_integer_to (t.second, s) -- ss + end + + append_day_ddd_to (d: INTEGER; s: STRING) + require + 1 <= d and d <= 7 + do + inspect d + when 1 then s.append ("Sun") + when 2 then s.append ("Mon") + when 3 then s.append ("Tue") + when 4 then s.append ("Wed") + when 5 then s.append ("Thu") + when 6 then s.append ("Fri") + when 7 then s.append ("Sat") + else + -- Error + end + end + + append_day_name_to (d: INTEGER; s: STRING) + require + 1 <= d and d <= 7 + do + inspect d + when 1 then s.append ("Sunday") + when 2 then s.append ("Monday") + when 3 then s.append ("Tuesday") + when 4 then s.append ("Wednesday") + when 5 then s.append ("Thursday") + when 6 then s.append ("Friday") + when 7 then s.append ("Saturday") + else + -- Error + end + end + + append_month_mmm_to (m: INTEGER; s: STRING) + require + 1 <= m and m <= 12 + do + inspect m + when 1 then s.append ("Jan") + when 2 then s.append ("Feb") + when 3 then s.append ("Mar") + when 4 then s.append ("Apr") + when 5 then s.append ("May") + when 6 then s.append ("Jun") + when 7 then s.append ("Jul") + when 8 then s.append ("Aou") + when 9 then s.append ("Sep") + when 10 then s.append ("Oct") + when 11 then s.append ("Nov") + when 12 then s.append ("Dec") + else + -- Error + end + end + +feature {NONE} -- Implementation + 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 @@ -349,8 +304,6 @@ feature {NONE} -- Implementation -- | "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" @@ -359,7 +312,7 @@ feature {NONE} -- Implementation 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 + l_gmt_offset: INTEGER -- minutes i, n: INTEGER err: BOOLEAN do @@ -418,8 +371,12 @@ feature {NONE} -- Implementation t.extend (s[i]) i := i + 1 end - if i <= n and t.count = 3 then + if i <= n and t.count >= 3 then l_mmm := t.as_upper + if l_mmm.count > 3 then + -- Tolerant to full month name .. + l_mmm.keep_head (3) + end 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 @@ -539,7 +496,7 @@ feature {NONE} -- Implementation t.extend (s[i].as_upper) i := i + 1 end - if t.same_string ("GMT") then + if t.same_string ("GMT") or t.same_string ("UTC") then from until i > n or else not s[i].is_space loop i := i + 1 end if i <= n then t.wipe_out @@ -552,11 +509,21 @@ feature {NONE} -- Implementation end if not err then i := i + 1 + from until i > n or else not s[i].is_space loop i := i + 1 end 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 + l_gmt_offset := t.to_integer * 60 + if i <= n and s[i] = ':' then + i := i + 1 + t.wipe_out + 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 := l_gmt_offset + l_gmt_offset.sign * t.to_integer + end end end else @@ -575,10 +542,260 @@ feature {NONE} -- Implementation 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) + Result.minute_add (- l_gmt_offset) end else --- create Result.make_utc_now + -- Void is better than wrong date. + end + end + + ansi_c_string_to_date_time (s: READABLE_STRING_8): detachable DATE_TIME + -- String representation of `dt' using the RFC 1123 + -- asctime-date = wkday SP date3 SP time SP 4DIGIT + -- 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" + -- month = "Jan" | "Feb" | "Mar" | "Apr" + -- | "May" | "Jun" | "Jul" | "Aug" + -- | "Sep" | "Oct" | "Nov" | "Dec" + --| 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 -- minutes + 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 + + --| 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.count > 3 then + -- Tolerant to full month name .. + l_mmm.keep_head (3) + end + 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 + from until i > n or else not s[i].is_space loop i := i + 1 end + + --| dd + if not err and i <= n 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.is_integer end + l_dd := t.to_integer + else + err := True + end + else + err := True + end + + --| blanks + 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 + + --| blanks + from until i > n or else not s[i].is_space loop i := i + 1 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 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 + + if i <= n and 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") or t.same_string ("UTC") 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 + i := i + 1 + from until i > n or else not s[i].is_space loop i := i + 1 end + 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 * 60 + if i <= n and s[i] = ':' then + i := i + 1 + t.wipe_out + 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 := l_gmt_offset + l_gmt_offset.sign * t.to_integer + end + 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.minute_add (- l_gmt_offset) + end + else + -- Void is better than wrong date. 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 index ffdd39fc..f81df5c7 100644 --- a/library/network/protocol/http/tests/http_date_test_set.e +++ b/library/network/protocol/http/tests/http_date_test_set.e @@ -33,20 +33,45 @@ feature -- Test routines create d.make_from_timestamp (d.timestamp) assert ("RFC 850", not d.has_error and then d.rfc850_string.same_string (s)) + s := "Sun, 06 Nov 1994 08:49:37 GMT" + create d.make_from_string (s) + assert ("ANSI C format", not d.has_error and then d.ansi_c_string.same_string ("Sun Nov 6 08:49:37 1994")) + s := "Sun Nov 6 08:49:37 1994" create d.make_from_string (s) - assert ("ANSI format", d.has_error) - - + assert ("ANSI C format", not d.has_error and then d.ansi_c_string.same_string (s)) -- Tolerance ... - s := "Sun, 06 Nov 1994 09:49:37 GMT+1" + s := "Sun, 06 November 1994 09:49:37 GMT+1" + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Sun, 06 Nov 1994 08:49:37 GMT")) + s := "Sun, 06 Nov 1994 09:49:37 GMT+1" + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Sun, 06 Nov 1994 08:49:37 GMT")) + + s := "Sun, 06 Nov 1994 07:49:37 GMT-1" create d.make_from_string (s) assert ("RFC 1123", not d.has_error and then d.string.same_string ("Sun, 06 Nov 1994 08:49:37 GMT")) + s := "Sun, 06 Nov 1994 10:19:37 GMT+1:30" + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Sun, 06 Nov 1994 08:49:37 GMT")) + + s := "Sun, 06 Nov 1994 07:19:37 GMT-1:30" + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Sun, 06 Nov 1994 08:49:37 GMT")) + + s := "Thu, 31 Jan 2013 15:35:00 GMT+5:45" -- NPT + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Thu, 31 Jan 2013 09:50:00 GMT")) + + s := "Thu, 31 Jan 2013 05:20:00 GMT-4:30" -- VET + create d.make_from_string (s) + assert ("RFC 1123", not d.has_error and then d.string.same_string ("Thu, 31 Jan 2013 09:50:00 GMT")) + end end diff --git a/library/server/wsf/router/wsf_file_system_handler.e b/library/server/wsf/router/wsf_file_system_handler.e index 5642842b..fc2ba7ce 100644 --- a/library/server/wsf/router/wsf_file_system_handler.e +++ b/library/server/wsf/router/wsf_file_system_handler.e @@ -203,7 +203,7 @@ feature -- Execution end if attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then - attached rfc1123_http_date_format_to_date (s_if_modified_since) as l_if_modified_since_date and then + attached 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) @@ -431,7 +431,7 @@ feature {NONE} -- implementation: date time Result := timestamp_to_date (f.date) end - rfc1123_http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME + 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"