Bugfixed cookie parsing.
[jsvc.git] / src / dolda / jsvc / util / Cookie.java
CommitLineData
141e5e3c
FT
1package dolda.jsvc.util;
2
3import dolda.jsvc.*;
4import java.util.*;
5import java.text.*;
6import java.io.*;
7
8public class Cookie {
d5dd6a2d 9 private final static Map<Request, MultiMap<String, Cookie>> cache = new WeakHashMap<Request, MultiMap<String, Cookie>>();
141e5e3c
FT
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 {
5e8bab52 64 PushbackReader r = new PushbackReader(new StringReader(in));
141e5e3c
FT
65 Cookie c = null;
66 while(true) {
67 String k = Http.tokenunquote(r);
5e8bab52
FT
68 Misc.eatws(r);
69 if((k == null) || (r.read() != '='))
70 throw(new Http.EncodingException("Illegal cookie header format"));
141e5e3c 71 String v = Http.tokenunquote(r);
141e5e3c
FT
72 if(k.equals("$Version")) {
73 if(Integer.parseInt(v) != 1)
74 throw(new Http.EncodingException("Unknown cookie format version"));
75 } else if(k.equals("$Path")) {
76 if(c != null)
77 c.path = v;
78 } else if(k.equals("$Domain")) {
79 if(c != null)
80 c.domain = v;
81 } else {
82 c = new Cookie(k, v);
83 ret.add(k, c);
84 }
5e8bab52
FT
85 Misc.eatws(r);
86 int sep = r.read();
87 if(sep < 0)
88 break;
89 if(sep != ';')
90 throw(new Http.EncodingException("Illegal cookie header format"));
141e5e3c
FT
91 }
92 } catch(IOException e) {
93 throw(new Error(e));
94 }
95 }
96 return(ret);
97 }
98
d5dd6a2d
FT
99 public static MultiMap<String, Cookie> get(Request req) {
100 synchronized(cache) {
101 MultiMap<String, Cookie> ret = cache.get(req);
102 if(ret == null) {
103 ret = parse(req);
104 cache.put(req, ret);
105 }
106 return(ret);
107 }
108 }
109
141e5e3c
FT
110 public String toString() {
111 StringBuilder buf = new StringBuilder();
112 buf.append("Cookie(");
113 buf.append(format());
114 buf.append(")");
115 return(buf.toString());
116 }
117}