summaryrefslogtreecommitdiffabout
path: root/src
authorMichael Krelin <hacker@klever.net>2005-05-09 11:00:28 (UTC)
committer Michael Krelin <hacker@klever.net>2005-05-09 11:00:28 (UTC)
commit43d47575878e4eaf3c8da84bf609fcd0bde595fb (patch) (unidiff)
treef7ec4d1f0d0a01b43feb5c9b4f414e870036522c /src
parentd9578a5ae0ac4e44ff5e3c13d3f39f400f51bcf2 (diff)
downloadkingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.zip
kingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.tar.gz
kingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.tar.bz2
1. http headers container added
2. preliminary cookies support 3. absolutely useless http_quoted_string and http_quote utility functions added
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/Makefile.am4
-rw-r--r--src/cgi_gateway.cc4
-rw-r--r--src/cookies.cc242
-rw-r--r--src/headers.cc40
-rw-r--r--src/util.cc53
5 files changed, 342 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 12bb1f8..2d462c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,9 +10,11 @@ LDADD = ${KONFORKA_LIBS}
10 10
11libkingate_la_SOURCES = \ 11libkingate_la_SOURCES = \
12 cgi_gateway.cc \ 12 cgi_gateway.cc \
13 cgi_interface.cc \ 13 cgi_interface.cc \
14 util.cc 14 util.cc \
15 cookies.cc \
16 headers.cc
15libkingate_la_LDFLAGS = -version-info 2:0:0 17libkingate_la_LDFLAGS = -version-info 2:0:0
16 18
17libkingate_fcgi_la_SOURCES = \ 19libkingate_fcgi_la_SOURCES = \
18 fastcgi.cc 20 fastcgi.cc
diff --git a/src/cgi_gateway.cc b/src/cgi_gateway.cc
index 30410f2..ab48f78 100644
--- a/src/cgi_gateway.cc
+++ b/src/cgi_gateway.cc
@@ -26,8 +26,12 @@ namespace kingate {
26 parse_query(qs,post); 26 parse_query(qs,post);
27 } 27 }
28 b_parsed_content = true; 28 b_parsed_content = true;
29 } 29 }
30 // Parse cookies
31 try {
32 cookies.parse_cookies(get_meta("HTTP_COOKIE"));
33 }catch(exception_notfound& enf) { }
30 } 34 }
31 35
32 bool cgi_gateway::has_GET(const string& n) const { 36 bool cgi_gateway::has_GET(const string& n) const {
33 return get.find(n) != get.end(); 37 return get.find(n) != get.end();
diff --git a/src/cookies.cc b/src/cookies.cc
new file mode 100644
index 0000000..40a0c8b
--- a/dev/null
+++ b/src/cookies.cc
@@ -0,0 +1,242 @@
1#include "kingate/cookies.h"
2#include "kingate/util.h"
3#include "kingate/exception.h"
4
5namespace kingate {
6
7 /*
8 * RFC 2109:
9 * av-pairs = av-pair *(";" av-pair)
10 * av-pair = attr ["=" value] ; optional value
11 * attr = token
12 * value = word
13 * word = token | quoted-string
14 */
15
16 /* RFC 2109:
17 *
18 * The origin server effectively ends a session by sending the client a
19 * Set-Cookie header with Max-Age=0.
20 *
21 * An origin server may include multiple Set-Cookie headers in a response.
22 * Note that an intervening gateway could fold multiple such headers into a
23 * single header.
24 *
25 *
26 * set-cookie = "Set-Cookie:" cookies
27 * cookies = 1#cookie
28 * cookie = NAME "=" VALUE *(";" cookie-av)
29 * NAME = attr
30 * VALUE = value
31 * cookie-av = "Comment" "=" value
32 * | "Domain" "=" value
33 * | "Max-Age" "=" value
34 * | "Path" "=" value
35 * | "Secure"
36 * | "Version" "=" 1*DIGIT
37 *
38 *
39 * The origin server should send the following additional HTTP/1.1
40 * response headers, depending on circumstances:
41 *
42 * * To suppress caching of the Set-Cookie header: Cache-control: no-
43 * cache="set-cookie".
44 *
45 * and one of the following:
46 *
47 * * To suppress caching of a private document in shared caches: Cache-
48 * control: private.
49 *
50 * * To allow caching of a document and require that it be validated
51 * before returning it to the client: Cache-control: must-revalidate.
52 *
53 * * To allow caching of a document, but to require that proxy caches
54 * (not user agent caches) validate it before returning it to the
55 * client: Cache-control: proxy-revalidate.
56 *
57 * * To allow caching of a document and request that it be validated
58 * before returning it to the client (by "pre-expiring" it):
59 * Cache-control: max-age=0. Not all caches will revalidate the
60 * document in every case.
61 *
62 * HTTP/1.1 servers must send Expires: old-date (where old-date is a
63 * date long in the past) on responses containing Set-Cookie response
64 * headers unless they know for certain (by out of band means) that
65 * there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
66 * other Cache-Control directives that permit caching by HTTP/1.1
67 * proxies in addition to the Expires: old-date directive; the Cache-
68 * Control directive will override the Expires: old-date for HTTP/1.1
69 * proxies.
70 *
71 */
72
73 void cookie::_set_string(const string& p,const string& v) {
74 (*this)[p]=v;
75 }
76
77 void cookie::set_comment(const string& c) {
78 _set_string("comment",c);
79 }
80 void cookie::set_domain(const string& d) {
81 _set_string("domain",d);
82 }
83 void cookie::set_max_age(const string& ma) {
84 _set_string("max-age",ma);
85 }
86 void cookie::set_path(const string& p) {
87 _set_string("path",p);
88 }
89 void cookie::set_secure(bool s) {
90 if(s)
91 _set_string("secure","");
92 else
93 erase("secure");
94 }
95
96 void cookie::set_expires(const string& e) {
97 (*this)["expires"] = e;
98 }
99
100 const string& cookie::_get_string(const string& s) const {
101 const_iterator i = find(s);
102 if(i==end())
103 throw exception_notfound(CODEPOINT,"No parameter set");
104 return i->second;
105 }
106
107 const string& cookie::get_comment() const {
108 return _get_string("comment");
109 }
110 const string& cookie::get_domain() const {
111 return _get_string("domain");
112 }
113 const string& cookie::get_max_age() const {
114 return _get_string("max-age");
115 }
116 const string& cookie::get_path() const {
117 return _get_string("path");
118 }
119 bool cookie::get_secure() const {
120 return find("secure")!=end();
121 }
122
123 const string& cookie::get_expires() const {
124 return _get_string("expires");
125 }
126
127 bool cookie::has_comment() const {
128 return find("comment")!=end();
129 }
130 bool cookie::has_domain() const {
131 return find("domain")!=end();
132 }
133 bool cookie::has_max_age() const {
134 return find("max-age")!=end();
135 }
136 bool cookie::has_path() const {
137 return find("path")!=end();
138 }
139
140 bool cookie::has_expires() const {
141 return find("expires")!=end();
142 }
143
144 void cookie::unset_comment() {
145 erase("comment");
146 }
147 void cookie::unset_domain() {
148 erase("domain");
149 }
150 void cookie::unset_max_age() {
151 erase("max-age");
152 }
153 void cookie::unset_path() {
154 erase("path");
155 }
156
157 void cookie::unset_expires() {
158 erase("expires");
159 }
160
161 string cookie::set_cookie_header_rfc2109() const {
162 string rv = name + "=" + http_quoted_string(value);
163 for(const_iterator i=begin();i!=end();++i) {
164 if(i->first=="secure") {
165 rv += "; secure";
166 }else{
167 rv += "; "+i->first+"="+http_quote(i->second);
168 }
169 }
170 rv += "; Version=1";
171 return rv;
172 }
173
174 string cookie::set_cookie_header() const {
175 string rv = name + "=" + value;
176 for(const_iterator i=begin();i!=end();++i) {
177 if(i->first=="secure") {
178 rv += "; secure";
179 }else{
180 rv += "; "+i->first+"="+i->second;
181 }
182 }
183 return rv;
184 }
185
186 bool cookies_t::has_cookie(const key_type& n) const {
187 return find(n)!=end();
188 }
189
190 const cookie& cookies_t::get_cookie(const key_type& n) const {
191 const_iterator i=find(n);
192 if(i==end())
193 throw exception_notfound(CODEPOINT,"No cookie with such name found");
194 return i->second;
195 }
196
197 cookie& cookies_t::get_cookie(const key_type& n) {
198 iterator i=find(n);
199 if(i==end())
200 throw exception_notfound(CODEPOINT,"No cookie with such name found");
201 return i->second;
202 }
203
204 void cookies_t::parse_cookies(const string& s) {
205 string str = s;
206 while(!str.empty()) {
207 string::size_type sc = str.find(';');
208 string s;
209 if(sc==string::npos) {
210 s = str;
211 str.erase();
212 }else{
213 s = str.substr(0,sc);
214 str.erase(0,sc+1);
215 }
216 string::size_type nsp=s.find_first_not_of(" \t");
217 if((nsp!=string::npos) && nsp)
218 s.erase(0,nsp);
219 string::size_type eq=s.find('=');
220 if(eq==string::npos)
221 continue;
222 string n = s.substr(0,eq);
223 s.erase(0,eq+1);
224 nsp = n.find_last_not_of(" \t");
225 n.erase(nsp+1);
226 nsp = s.find_first_not_of(" \t");
227 string v;
228 if(nsp!=string::npos)
229 v = s.substr(nsp);
230 else
231 v = s;
232 nsp = v.find_last_not_of(" \t");
233 if(nsp==string::npos)
234 v.erase();
235 else
236 v.erase(nsp+1);
237 cookie& c = (*this)[n];
238 c.set_name(n); c.set_value(v);
239 }
240 }
241
242}
diff --git a/src/headers.cc b/src/headers.cc
new file mode 100644
index 0000000..89ac519
--- a/dev/null
+++ b/src/headers.cc
@@ -0,0 +1,40 @@
1#include "kingate/headers.h"
2#include "kingate/exception.h"
3
4namespace kingate {
5
6 void headers::set_header(const key_type& h,const mapped_type& c) {
7 erase(h);
8 insert(value_type(h,c));
9 }
10 void headers::add_header(const key_type& h,const mapped_type& c) {
11 insert(value_type(h,c));
12 }
13 void headers::unset_header(const key_type& h) {
14 erase(h);
15 }
16 const headers::mapped_type& headers::get_header(const key_type& h) const {
17 const_iterator i=find(h);
18 if(i==end())
19 throw exception_notfound(CODEPOINT,"No such header");
20 return i->second;
21 }
22 headers::mapped_type& headers::get_header(const key_type& h) {
23 // XXX: or should it fail if there's no such thing, unlike operator[]?
24 iterator i=find(h);
25 if(i==end())
26 i = insert(value_type(h,""));
27 return i->second;
28 }
29
30 pair<headers::const_iterator,headers::const_iterator> headers::get_headers(const key_type& h) const {
31 return equal_range(h);
32 }
33 pair<headers::iterator,headers::iterator> headers::get_headers(const key_type& h) {
34 return equal_range(h);
35 }
36
37 bool headers::has_header(const key_type& h) const {
38 return find(h)!=end();
39 }
40}
diff --git a/src/util.cc b/src/util.cc
index 3166e62..48e486a 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -49,5 +49,58 @@ namespace kingate {
49 } 49 }
50 } 50 }
51 return rv; 51 return rv;
52 } 52 }
53
54 /*
55 * RFC 2616:
56 *
57 * separators = "(" | ")" | "<" | ">" | "@"
58 * | "," | ";" | ":" | "\" | <">
59 * | "/" | "[" | "]" | "?" | "="
60 * | "{" | "}" | SP | HT
61 */
62
63 /*
64 * RFC 2616:
65 *
66 * token = 1*<any CHAR except CTLs or separators>
67 */
68
69 static const char *quotible_chars =
70 "\001\002\003\004\005\006\007\010"
71 "\011\012\013\014\015\016\017\020"
72 "\021\022\023\024\025\026\027\030"
73 "\031\032\033\034\035\036\037\040"
74 "()<>@,;:\\\"/[]?={}" /* separator chars (except for SP and HT mentioned elsewhere */
75 "\177"
76 ;
77
78 /*
79 * RFC 2616:
80 *
81 * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
82 * qdtext = <any TEXT except <">>
83 *
84 * The backslash character ("\") MAY be used as a single-character
85 * quoting mechanism only within quoted-string and comment constructs.
86 *
87 * quoted-pair = "\" CHAR
88 */
89
90 string http_quoted_string(const string& str) {
91 string rv = str;
92 string::size_type sp=0;
93 for(string::size_type q=rv.find('"');(q=rv.find('"',q))!=string::npos;q+=2)
94 rv.insert(q,1,'\\');
95 rv.insert(0,1,'"');
96 rv += '"';
97 return rv;
98 }
99
100 string http_quote(const string& str) {
101 if(str.find_first_of(quotible_chars)==string::npos)
102 return str;
103 return http_quoted_string(str);
104 }
105
53} 106}