View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.client;
20  
21  import java.text.ParseException;
22  import java.text.SimpleDateFormat;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.TimeZone;
28  import java.util.concurrent.TimeUnit;
29  
30  import org.eclipse.jetty.http.HttpCookie;
31  
32  public class HttpCookieParser
33  {
34      private static final String[] DATE_PATTERNS = new String[]
35              {
36                      "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'",
37                      "EEE',' dd MMM yyyy HH:mm:ss 'GMT'",
38                      "EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
39              };
40  
41      private HttpCookieParser()
42      {
43      }
44  
45      public static List<HttpCookie> parseCookies(String headerValue)
46      {
47          if (headerValue.toLowerCase(Locale.ENGLISH).contains("expires="))
48          {
49              HttpCookie cookie = parseCookie(headerValue, 0);
50              if (cookie != null)
51                  return Collections.singletonList(cookie);
52              else
53                  return Collections.emptyList();
54          }
55          else
56          {
57              List<HttpCookie> result = new ArrayList<>();
58              List<String> cookieStrings = splitCookies(headerValue);
59              for (String cookieString : cookieStrings)
60              {
61                  HttpCookie cookie = parseCookie(cookieString, 1);
62                  if (cookie != null)
63                      result.add(cookie);
64              }
65              return result;
66          }
67      }
68  
69      private static List<String> splitCookies(String headerValue)
70      {
71          // The comma is the separator, but only if it's outside double quotes
72          List<String> result = new ArrayList<>();
73          int start = 0;
74          int quotes = 0;
75          for (int i = 0; i < headerValue.length(); ++i)
76          {
77              char c = headerValue.charAt(i);
78              if (c == ',' && quotes % 2 == 0)
79              {
80                  result.add(headerValue.substring(start, i));
81                  start = i + 1;
82              }
83              else if (c == '"')
84              {
85                  ++quotes;
86              }
87          }
88          result.add(headerValue.substring(start));
89          return result;
90      }
91  
92      private static HttpCookie parseCookie(String cookieString, int version)
93      {
94          String[] cookieParts = cookieString.split(";");
95  
96          String nameValue = cookieParts[0];
97          int equal = nameValue.indexOf('=');
98          if (equal < 1)
99              return null;
100 
101         String name = nameValue.substring(0, equal).trim();
102         String value = nameValue.substring(equal + 1);
103         String domain = null;
104         String path = "/";
105         long maxAge = -1;
106         boolean httpOnly = false;
107         boolean secure = false;
108         String comment = null;
109         for (int i = 1; i < cookieParts.length; ++i)
110         {
111             try
112             {
113                 String[] attributeParts = cookieParts[i].split("=", 2);
114                 String attributeName = attributeParts[0].trim().toLowerCase(Locale.ENGLISH);
115                 String attributeValue = attributeParts.length < 2 ? "" : attributeParts[1].trim();
116                 switch (attributeName)
117                 {
118                     case "domain":
119                     {
120                         domain = attributeValue;
121                         break;
122                     }
123                     case "path":
124                     {
125                         path = attributeValue;
126                         break;
127                     }
128                     case "max-age":
129                     {
130                         maxAge = Long.parseLong(attributeValue);
131                         break;
132                     }
133                     case "expires":
134                     {
135                         maxAge = parseDate(attributeValue);
136                         break;
137                     }
138                     case "secure":
139                     {
140                         secure = true;
141                         break;
142                     }
143                     case "httponly":
144                     {
145                         httpOnly = true;
146                     }
147                     case "comment":
148                     {
149                         comment = attributeValue;
150                         break;
151                     }
152                     case "version":
153                     {
154                         version = Integer.parseInt(attributeValue);
155                         break;
156                     }
157                     default:
158                     {
159                         // Ignore
160                         break;
161                     }
162                 }
163             }
164             catch (NumberFormatException x)
165             {
166                 // Ignore
167             }
168         }
169 
170         return new HttpCookie(name, value, domain, path, maxAge, httpOnly, secure, comment, version);
171     }
172 
173     private static long parseDate(String attributeValue)
174     {
175         for (String pattern : DATE_PATTERNS)
176         {
177             try
178             {
179                 SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, Locale.US);
180                 dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
181                 long result = TimeUnit.MILLISECONDS.toSeconds(dateFormat.parse(attributeValue).getTime() - System.currentTimeMillis());
182                 if (result < 0)
183                     return 0;
184             }
185             catch (ParseException x)
186             {
187                 // Ignore and continue
188             }
189         }
190         return 0;
191     }
192 }