-rw-r--r-- | src/cookies.cc | 242 |
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); + } + } + +} |