Unverified Commit c990eadd authored by Lauri Kasanen's avatar Lauri Kasanen Committed by Daniel Stenberg
Browse files

cookie: store cookies per top-level-domain-specific hash table

This makes libcurl handle thousands of cookies much better and speedier.

Closes #2440
parent 4073cd83
Loading
Loading
Loading
Loading
+154 −86
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ Example set of cookies:

#include "urldata.h"
#include "cookie.h"
#include "hash.h"
#include "strtok.h"
#include "sendf.h"
#include "slist.h"
@@ -142,6 +143,28 @@ static bool tailmatch(const char *cooke_domain, const char *hostname)
  return FALSE;
}

/*
 * Return true if the given string is an IP(v4|v6) address.
 */
static bool isip(const char *domain)
{
  struct in_addr addr;
#ifdef ENABLE_IPV6
  struct in6_addr addr6;
#endif

  if(Curl_inet_pton(AF_INET, domain, &addr)
#ifdef ENABLE_IPV6
     || Curl_inet_pton(AF_INET6, domain, &addr6)
#endif
    ) {
    /* domain name given as IP address */
    return TRUE;
  }

  return FALSE;
}

/*
 * matching cookie path and url path
 * RFC6265 5.1.4 Paths and Path-Match
@@ -217,6 +240,49 @@ pathmatched:
  return ret;
}

/*
 * Return the top-level domain, for optimal hashing.
 */
static const char *get_top_domain(const char * const domain, size_t *outlen)
{
  size_t len;
  const char *first, *last;

  if(!domain)
    return NULL;

  len = strlen(domain);
  first = memchr(domain, '.', len);
  last = memrchr(domain, '.', len);

  if(outlen)
    *outlen = len;

  if(first == last)
    return domain;

  first = memrchr(domain, '.', (size_t)(last - domain - 1));
  if(outlen)
    *outlen = len - (size_t)(first - domain) - 1;

  return first + 1;
}

/*
 * Hash this domain.
 */
static size_t cookiehash(const char * const domain)
{
  const char *top;
  size_t len;

  if(!domain || isip(domain))
    return 0;

  top = get_top_domain(domain, &len);
  return Curl_hash_str((void *) top, len, COOKIE_HASH_SIZE);
}

/*
 * cookie path sanitize
 */
@@ -303,14 +369,16 @@ static void remove_expired(struct CookieInfo *cookies)
{
  struct Cookie *co, *nx, *pv;
  curl_off_t now = (curl_off_t)time(NULL);
  unsigned int i;

  co = cookies->cookies;
  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
    co = cookies->cookies[i];
    pv = NULL;
    while(co) {
      nx = co->next;
      if(co->expires && co->expires < now) {
        if(!pv) {
        cookies->cookies = co->next;
          cookies->cookies[i] = co->next;
        }
        else {
          pv->next = co->next;
@@ -324,27 +392,6 @@ static void remove_expired(struct CookieInfo *cookies)
      co = nx;
    }
  }

/*
 * Return true if the given string is an IP(v4|v6) address.
 */
static bool isip(const char *domain)
{
  struct in_addr addr;
#ifdef ENABLE_IPV6
  struct in6_addr addr6;
#endif

  if(Curl_inet_pton(AF_INET, domain, &addr)
#ifdef ENABLE_IPV6
     || Curl_inet_pton(AF_INET6, domain, &addr6)
#endif
    ) {
    /* domain name given as IP address */
    return TRUE;
  }

  return FALSE;
}

/****************************************************************************
@@ -381,6 +428,7 @@ Curl_cookie_add(struct Curl_easy *data,
  time_t now = time(NULL);
  bool replace_old = FALSE;
  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
  size_t myhash;

#ifdef USE_LIBPSL
  const psl_ctx_t *psl;
@@ -838,7 +886,8 @@ Curl_cookie_add(struct Curl_easy *data,
  }
#endif

  clist = c->cookies;
  myhash = cookiehash(co->domain);
  clist = c->cookies[myhash];
  replace_old = FALSE;
  while(clist) {
    if(strcasecompare(clist->name, co->name)) {
@@ -924,7 +973,7 @@ Curl_cookie_add(struct Curl_easy *data,
    if(lastc)
      lastc->next = co;
    else
      c->cookies = co;
      c->cookies[myhash] = co;
    c->numcookies++; /* one more cookie in the jar */
  }

@@ -1137,8 +1186,9 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
  struct Cookie *mainco = NULL;
  size_t matches = 0;
  bool is_ip;
  const size_t myhash = cookiehash(host);

  if(!c || !c->cookies)
  if(!c || !c->cookies[myhash])
    return NULL; /* no cookie struct or no cookies in the struct */

  /* at first, remove expired cookies */
@@ -1147,7 +1197,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
  /* check if host is an IP(v4|v6) address */
  is_ip = isip(host);

  co = c->cookies;
  co = c->cookies[myhash];

  while(co) {
    /* only process this cookie if it is not expired or had no expire
@@ -1235,8 +1285,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
void Curl_cookie_clearall(struct CookieInfo *cookies)
{
  if(cookies) {
    Curl_cookie_freelist(cookies->cookies);
    cookies->cookies = NULL;
    unsigned int i;
    for(i = 0; i < COOKIE_HASH_SIZE; i++) {
      Curl_cookie_freelist(cookies->cookies[i]);
      cookies->cookies[i] = NULL;
    }
    cookies->numcookies = 0;
  }
}
@@ -1270,11 +1323,16 @@ void Curl_cookie_freelist(struct Cookie *co)
void Curl_cookie_clearsess(struct CookieInfo *cookies)
{
  struct Cookie *first, *curr, *next, *prev = NULL;
  unsigned int i;

  if(!cookies || !cookies->cookies)
  if(!cookies)
    return;

  first = curr = prev = cookies->cookies;
  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
    if(!cookies->cookies[i])
      continue;

    first = curr = prev = cookies->cookies[i];

    for(; curr; curr = next) {
      next = curr->next;
@@ -1294,7 +1352,8 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies)
        prev = curr;
    }

  cookies->cookies = first;
    cookies->cookies[i] = first;
  }
}


@@ -1307,9 +1366,12 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies)
 ****************************************************************************/
void Curl_cookie_cleanup(struct CookieInfo *c)
{
  unsigned int i;

  if(c) {
    free(c->filename);
    Curl_cookie_freelist(c->cookies);
    for(i = 0; i < COOKIE_HASH_SIZE; i++)
      Curl_cookie_freelist(c->cookies[i]);
    free(c); /* free the base struct as well */
  }
}
@@ -1358,6 +1420,7 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
  FILE *out;
  bool use_stdout = FALSE;
  char *format_ptr;
  unsigned int i;

  if((NULL == c) || (0 == c->numcookies))
    /* If there are no known cookies, we don't write or even create any
@@ -1383,7 +1446,8 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
        "# This file was generated by libcurl! Edit at your own risk.\n\n",
        out);

  for(co = c->cookies; co; co = co->next) {
  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
    for(co = c->cookies[i]; co; co = co->next) {
      if(!co->domain)
        continue;
      format_ptr = get_netscape_format(co);
@@ -1396,6 +1460,7 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
      fprintf(out, "%s\n", format_ptr);
      free(format_ptr);
    }
  }

  if(!use_stdout)
    fclose(out);
@@ -1409,12 +1474,14 @@ static struct curl_slist *cookie_list(struct Curl_easy *data)
  struct curl_slist *beg;
  struct Cookie *c;
  char *line;
  unsigned int i;

  if((data->cookies == NULL) ||
      (data->cookies->numcookies == 0))
    return NULL;

  for(c = data->cookies->cookies; c; c = c->next) {
  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
    for(c = data->cookies->cookies[i]; c; c = c->next) {
      if(!c->domain)
        continue;
      line = get_netscape_format(c);
@@ -1430,6 +1497,7 @@ static struct curl_slist *cookie_list(struct Curl_easy *data)
      }
      list = beg;
    }
  }

  return list;
}
+3 −1
Original line number Diff line number Diff line
@@ -45,9 +45,11 @@ struct Cookie {
  bool httponly;     /* true if the httponly directive is present */
};

#define COOKIE_HASH_SIZE 256

struct CookieInfo {
  /* linked list of cookies we know of */
  struct Cookie *cookies;
  struct Cookie *cookies[COOKIE_HASH_SIZE];

  char *filename;  /* file we read from/write to */
  bool running;    /* state info, for cookie adding information */
+2 −2
Original line number Diff line number Diff line
@@ -74,8 +74,6 @@ Cookie: empty=; mooo2=indeed2; mooo=indeed
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

www.fake.come	FALSE	/	FALSE	2022144953	cookiecliente	si
www.loser.com	FALSE	/	FALSE	2139150993	UID	99
%HOSTIP	FALSE	/	FALSE	1739150993	mooo	indeed
#HttpOnly_%HOSTIP	FALSE	/want	FALSE	1739150993	mooo2	indeed2
%HOSTIP	FALSE	/want	FALSE	0	empty	
@@ -84,6 +82,8 @@ www.loser.com FALSE / FALSE 2139150993 UID 99
%HOSTIP	FALSE	/	FALSE	0	ASPSESSIONIDQGGQQSJJ	GKNBDIFAAOFDPDAIEAKDIBKE
%HOSTIP	FALSE	/	FALSE	0	justaname	
%HOSTIP	FALSE	/want/	FALSE	0	simplyhuge	zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
www.fake.come	FALSE	/	FALSE	2022144953	cookiecliente	si
www.loser.com	FALSE	/	FALSE	2139150993	UID	99
</file>
</verify>
</testcase>