summaryrefslogtreecommitdiffabout
path: root/src/cookies.cc
Side-by-side diff
Diffstat (limited to 'src/cookies.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--src/cookies.cc242
1 files changed, 242 insertions, 0 deletions
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 @@
+#include "kingate/cookies.h"
+#include "kingate/util.h"
+#include "kingate/exception.h"
+
+namespace kingate {
+
+ /*
+ * RFC 2109:
+ * av-pairs = av-pair *(";" av-pair)
+ * av-pair = attr ["=" value] ; optional value
+ * attr = token
+ * value = word
+ * word = token | quoted-string
+ */
+
+ /* RFC 2109:
+ *
+ * The origin server effectively ends a session by sending the client a
+ * Set-Cookie header with Max-Age=0.
+ *
+ * An origin server may include multiple Set-Cookie headers in a response.
+ * Note that an intervening gateway could fold multiple such headers into a
+ * single header.
+ *
+ *
+ * set-cookie = "Set-Cookie:" cookies
+ * cookies = 1#cookie
+ * cookie = NAME "=" VALUE *(";" cookie-av)
+ * NAME = attr
+ * VALUE = value
+ * cookie-av = "Comment" "=" value
+ * | "Domain" "=" value
+ * | "Max-Age" "=" value
+ * | "Path" "=" value
+ * | "Secure"
+ * | "Version" "=" 1*DIGIT
+ *
+ *
+ * The origin server should send the following additional HTTP/1.1
+ * response headers, depending on circumstances:
+ *
+ * * To suppress caching of the Set-Cookie header: Cache-control: no-
+ * cache="set-cookie".
+ *
+ * and one of the following:
+ *
+ * * To suppress caching of a private document in shared caches: Cache-
+ * control: private.
+ *
+ * * To allow caching of a document and require that it be validated
+ * before returning it to the client: Cache-control: must-revalidate.
+ *
+ * * To allow caching of a document, but to require that proxy caches
+ * (not user agent caches) validate it before returning it to the
+ * client: Cache-control: proxy-revalidate.
+ *
+ * * To allow caching of a document and request that it be validated
+ * before returning it to the client (by "pre-expiring" it):
+ * Cache-control: max-age=0. Not all caches will revalidate the
+ * document in every case.
+ *
+ * HTTP/1.1 servers must send Expires: old-date (where old-date is a
+ * date long in the past) on responses containing Set-Cookie response
+ * headers unless they know for certain (by out of band means) that
+ * there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
+ * other Cache-Control directives that permit caching by HTTP/1.1
+ * proxies in addition to the Expires: old-date directive; the Cache-
+ * Control directive will override the Expires: old-date for HTTP/1.1
+ * proxies.
+ *
+ */
+
+ void cookie::_set_string(const string& p,const string& v) {
+ (*this)[p]=v;
+ }
+
+ void cookie::set_comment(const string& c) {
+ _set_string("comment",c);
+ }
+ void cookie::set_domain(const string& d) {
+ _set_string("domain",d);
+ }
+ void cookie::set_max_age(const string& ma) {
+ _set_string("max-age",ma);
+ }
+ void cookie::set_path(const string& p) {
+ _set_string("path",p);
+ }
+ void cookie::set_secure(bool s) {
+ if(s)
+ _set_string("secure","");
+ else
+ erase("secure");
+ }
+
+ void cookie::set_expires(const string& e) {
+ (*this)["expires"] = e;
+ }
+
+ const string& cookie::_get_string(const string& s) const {
+ const_iterator i = find(s);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No parameter set");
+ return i->second;
+ }
+
+ const string& cookie::get_comment() const {
+ return _get_string("comment");
+ }
+ const string& cookie::get_domain() const {
+ return _get_string("domain");
+ }
+ const string& cookie::get_max_age() const {
+ return _get_string("max-age");
+ }
+ const string& cookie::get_path() const {
+ return _get_string("path");
+ }
+ bool cookie::get_secure() const {
+ return find("secure")!=end();
+ }
+
+ const string& cookie::get_expires() const {
+ return _get_string("expires");
+ }
+
+ bool cookie::has_comment() const {
+ return find("comment")!=end();
+ }
+ bool cookie::has_domain() const {
+ return find("domain")!=end();
+ }
+ bool cookie::has_max_age() const {
+ return find("max-age")!=end();
+ }
+ bool cookie::has_path() const {
+ return find("path")!=end();
+ }
+
+ bool cookie::has_expires() const {
+ return find("expires")!=end();
+ }
+
+ void cookie::unset_comment() {
+ erase("comment");
+ }
+ void cookie::unset_domain() {
+ erase("domain");
+ }
+ void cookie::unset_max_age() {
+ erase("max-age");
+ }
+ void cookie::unset_path() {
+ erase("path");
+ }
+
+ void cookie::unset_expires() {
+ erase("expires");
+ }
+
+ string cookie::set_cookie_header_rfc2109() const {
+ string rv = name + "=" + http_quoted_string(value);
+ for(const_iterator i=begin();i!=end();++i) {
+ if(i->first=="secure") {
+ rv += "; secure";
+ }else{
+ rv += "; "+i->first+"="+http_quote(i->second);
+ }
+ }
+ rv += "; Version=1";
+ return rv;
+ }
+
+ string cookie::set_cookie_header() const {
+ string rv = name + "=" + value;
+ for(const_iterator i=begin();i!=end();++i) {
+ if(i->first=="secure") {
+ rv += "; secure";
+ }else{
+ rv += "; "+i->first+"="+i->second;
+ }
+ }
+ return rv;
+ }
+
+ bool cookies_t::has_cookie(const key_type& n) const {
+ return find(n)!=end();
+ }
+
+ const cookie& cookies_t::get_cookie(const key_type& n) const {
+ const_iterator i=find(n);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No cookie with such name found");
+ return i->second;
+ }
+
+ cookie& cookies_t::get_cookie(const key_type& n) {
+ iterator i=find(n);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No cookie with such name found");
+ return i->second;
+ }
+
+ void cookies_t::parse_cookies(const string& s) {
+ string str = s;
+ while(!str.empty()) {
+ string::size_type sc = str.find(';');
+ string s;
+ if(sc==string::npos) {
+ s = str;
+ str.erase();
+ }else{
+ s = str.substr(0,sc);
+ str.erase(0,sc+1);
+ }
+ string::size_type nsp=s.find_first_not_of(" \t");
+ if((nsp!=string::npos) && nsp)
+ s.erase(0,nsp);
+ string::size_type eq=s.find('=');
+ if(eq==string::npos)
+ continue;
+ string n = s.substr(0,eq);
+ s.erase(0,eq+1);
+ nsp = n.find_last_not_of(" \t");
+ n.erase(nsp+1);
+ nsp = s.find_first_not_of(" \t");
+ string v;
+ if(nsp!=string::npos)
+ v = s.substr(nsp);
+ else
+ v = s;
+ nsp = v.find_last_not_of(" \t");
+ if(nsp==string::npos)
+ v.erase();
+ else
+ v.erase(nsp+1);
+ cookie& c = (*this)[n];
+ c.set_name(n); c.set_value(v);
+ }
+ }
+
+}