Bug 1027216

Summary: PackageDeploymentServlet responds Last-Modified header in wrong format
Product: [JBoss] JBoss Enterprise BRMS Platform 5 Reporter: Toshiya Kobayashi <tkobayas>
Component: BRM (Guvnor)Assignee: manstis
Status: VERIFIED --- QA Contact:
Severity: high Docs Contact:
Priority: unspecified    
Version: BRMS 5.3.1CC: manstis, nwallace
Target Milestone: GA   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1022758    

Description Toshiya Kobayashi 2013-11-06 11:29:13 UTC
Description of problem:

org.drools.guvnor.server.files.PackageDeploymentServlet responds Last-Modified header in SimpleDateFormat "EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z" (RFC822TimeZone) so in case of non-GMT timezone, the value will be like this:

Tue, 05 Nov 2013 16:33:48 +0900

But this doesn't conform to RFC2616 which requires RFC 1123 format and GMT representation.

http://www.ietf.org/rfc/rfc2616.txt
====
3.3 Date/Time Formats

3.3.1 Full Date

   HTTP applications have historically allowed three different formats
   for the representation of date/time stamps:

      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

   The first format is preferred as an Internet standard and represents
   a fixed-length subset of that defined by RFC 1123 [8] (an update to
   RFC 822 [9]). The second format is in common use, but is based on the
   obsolete RFC 850 [12] date format and lacks a four-digit year.
   HTTP/1.1 clients and servers that parse the date value MUST accept
   all three formats (for compatibility with HTTP/1.0), though they MUST
   only generate the RFC 1123 format for representing HTTP-date values
   in header fields. See section 19.3 for further information.

      Note: Recipients of date values are encouraged to be robust in
      accepting date values that may have been sent by non-HTTP
      applications, as is sometimes the case when retrieving or posting
      messages via proxies/gateways to SMTP or NNTP.

   All HTTP date/time stamps MUST be represented in Greenwich Mean Time
   (GMT), without exception. For the purposes of HTTP, GMT is exactly
   equal to UTC (Coordinated Universal Time). This is indicated in the
   first two formats by the inclusion of "GMT" as the three-letter
   abbreviation for time zone, and MUST be assumed when reading the
   asctime format. HTTP-date is case sensitive and MUST NOT include
   additional LWS beyond that specifically included as SP in the
   grammar.

       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"

      Note: HTTP requirements for the date/time stamp format apply only
      to their usage within the protocol stream. Clients and servers are
      not required to use these formats for user presentation, request
      logging, etc.

14.29 Last-Modified

   The Last-Modified entity-header field indicates the date and time at
   which the origin server believes the variant was last modified.

       Last-Modified  = "Last-Modified" ":" HTTP-date
====

It results in returning an wrong value by HttpURLConnection.getLastModified() because HttpURLConnection.getLastModified() cannot parse RFC822TimeZone part ("+0900"). Finally, org.drools.io.impl.UrlResource stores the wrong LastModified time.

UrlResource.java:
====
    private long grabLastMod() throws IOException {
        // use File if possible, as http rounds milliseconds on some machines, this fine level of granularity is only really an issue for testing
        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504473
        if ( "file".equals( url.getProtocol() ) ) {
            File file = getFile();
            return file.lastModified();
        } else {
            URLConnection conn = getURL().openConnection();
            if ( conn instanceof HttpURLConnection) {
                ((HttpURLConnection) conn).setRequestMethod( "HEAD" );
            }
            long date =  conn.getLastModified();
            if (date == 0) {
                 try {
                     date = Long.parseLong(conn.getHeaderField("lastModified"));
                 } catch (Exception e) { /* well, we tried ... */ }
            }
            return date;
        }
    }
====

Correct format is rfc1123-date so the value should be like:

Wed, 06 Nov 2013 07:36:42 GMT

Steps to Reproduce:
1. Start BRMS under non-GMT timezone (e.g. start with -Duser.timezone=Asia/Tokyo)
2. Run a java class like this:

public class VerifyLastModified {

    public static void main(String[] args) throws Exception {

        URL url = new URL("http://localhost:8080/jboss-brms/org.drools.guvnor.Guvnor/package/defaultPackage/LATEST");
        URLConnection conn = url.openConnection();
        if (conn instanceof HttpURLConnection) {
            ((HttpURLConnection) conn).setRequestMethod("HEAD");
        }
        
        String headerField2 = conn.getHeaderField("last-modified");
        System.out.println("last-modified -> " + headerField2);
        System.out.println("last-modified -> " + Date.parse(headerField2));
        System.out.println("last-modified -> " + new Date(Date.parse(headerField2)));
        
        long lastModified = conn.getLastModified();
        System.out.println("conn.getLastModified -> " + lastModified);
        System.out.println("conn.getLastModified -> " + new Date(lastModified));
        
        String headerField1 = conn.getHeaderField("lastModified");
        System.out.println("lastModified -> " + headerField1);
        System.out.println("lastModified -> " + Long.parseLong(headerField1));
        System.out.println("lastModified -> " + new Date(Long.parseLong(headerField1)));

    }
}


Actual results:

last-modified header has RFC822TimeZone. 

conn.getLastModified() doesn't equal to the correct value (last-modified / lastModified)

example)
last-modified -> Wed, 06 Nov 2013 19:39:21 +0900
last-modified -> 1383734361000
last-modified -> Wed Nov 06 19:39:21 JST 2013
conn.getLastModified -> 1383766761000
conn.getLastModified -> Thu Nov 07 04:39:21 JST 2013
lastModified -> 1383734361812
lastModified -> 1383734361812
lastModified -> Wed Nov 06 19:39:21 JST 2013

Expected results:

last-modified header is GMT.

conn.getLastModified equals to the correct value (last-modified / lastModified)

example)
last-modified -> Wed, 06 Nov 2013 10:39:21 GMT
last-modified -> 1383734361000
last-modified -> Wed Nov 06 19:39:21 JST 2013
conn.getLastModified -> 1383734361000
conn.getLastModified -> Wed Nov 06 19:39:21 JST 2013
lastModified -> 1383734361812
lastModified -> 1383734361812
lastModified -> Wed Nov 06 19:39:21 JST 2013

Comment 1 Toshiya Kobayashi 2013-11-06 13:03:25 UTC
Sent a pull request:

https://github.com/droolsjbpm/guvnor/pull/106

Comment 4 Petr Široký 2014-02-13 15:16:43 UTC
Verified fixed in 5.3.1-P05. 

Output from the reproducer (just for reference):

last-modified -> Thu, 13 Feb 2014 14:41:06 GMT
last-modified -> 1392302466000
last-modified -> Thu Feb 13 15:41:06 CET 2014
conn.getLastModified -> 1392302466000
conn.getLastModified -> Thu Feb 13 15:41:06 CET 2014
lastModified -> 1392302466109
lastModified -> 1392302466109
lastModified -> Thu Feb 13 15:41:06 CET 2014