Added basic session support.
[jsvc.git] / src / dolda / jsvc / util / Cookie.java
1 package dolda.jsvc.util;
2
3 import dolda.jsvc.*;
4 import java.util.*;
5 import java.text.*;
6 import java.io.*;
7
8 public class Cookie {
9     private final static Map<Request, MultiMap<String, Cookie>> cache = new WeakHashMap<Request, MultiMap<String, Cookie>>();
10     public final static DateFormat datefmt;
11     static {
12         datefmt = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
13         datefmt.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
14     }
15     public final String name;
16     public String value;
17     public Date expires;
18     public String domain, path;
19     public boolean secure;
20     
21     public Cookie(String name, String value, Date expires, String domain, String path, boolean secure) {
22         if(!Http.istoken(name))
23             throw(new RuntimeException("Invalid cookie name: `" + name + "'"));
24         this.name = name;
25         this.value = value;
26         this.expires = expires;
27         this.domain = domain;
28         this.path = path;
29         this.secure = secure;
30     }
31     
32     public Cookie(String name) {
33         this(name, null, null, null, null, false);
34     }
35     
36     public Cookie(String name, String value) {
37         this(name, value, null, null, null, false);
38     }
39     
40     public String format() {
41         StringBuilder buf = new StringBuilder();
42         buf.append(Http.tokenquote(name));
43         buf.append('=');
44         buf.append(Http.tokenquote(value));
45         if(domain != null)
46             buf.append("; Domain=" + Http.tokenquote(domain));
47         if(path != null)
48             buf.append("; Path=" + Http.tokenquote(path));
49         if(expires != null)
50             buf.append("; Expires=" + Http.tokenquote(datefmt.format(expires)));
51         if(secure)
52             buf.append("; Secure");
53         return(buf.toString());
54     }
55
56     public void addto(Request req) {
57         req.outheaders().add("Set-Cookie", format());
58     }
59     
60     public static MultiMap<String, Cookie> parse(Request req) {
61         MultiMap<String, Cookie> ret = new WrappedMultiMap<String, Cookie>(new TreeMap<String, Collection<Cookie>>());
62         for(String in : req.inheaders().values("Cookie")) {
63             try {
64                 StringReader r = new StringReader(in);
65                 Cookie c = null;
66                 while(true) {
67                     String k = Http.tokenunquote(r);
68                     String v = Http.tokenunquote(r);
69                     if(k == null)
70                         break;
71                     if(k.equals("$Version")) {
72                         if(Integer.parseInt(v) != 1)
73                             throw(new Http.EncodingException("Unknown cookie format version"));
74                     } else if(k.equals("$Path")) {
75                         if(c != null)
76                             c.path = v;
77                     } else if(k.equals("$Domain")) {
78                         if(c != null)
79                             c.domain = v;
80                     } else {
81                         c = new Cookie(k, v);
82                         ret.add(k, c);
83                     }
84                 }
85             } catch(IOException e) {
86                 throw(new Error(e));
87             }
88         }
89         return(ret);
90     }
91     
92     public static MultiMap<String, Cookie> get(Request req) {
93         synchronized(cache) {
94             MultiMap<String, Cookie> ret = cache.get(req);
95             if(ret == null) {
96                 ret = parse(req);
97                 cache.put(req, ret);
98             }
99             return(ret);
100         }
101     }
102
103     public String toString() {
104         StringBuilder buf = new StringBuilder();
105         buf.append("Cookie(");
106         buf.append(format());
107         buf.append(")");
108         return(buf.toString());
109     }
110 }