]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
cookies: track expiration in jar to optimize removals
authorDaniel Gustafsson <daniel@yesql.se>
Tue, 8 Jun 2021 07:16:58 +0000 (09:16 +0200)
committerDaniel Gustafsson <daniel@yesql.se>
Tue, 8 Jun 2021 07:31:19 +0000 (09:31 +0200)
Removing expired cookies needs to be a fast operation since we want to
be able to perform it often and speculatively. By tracking the timestamp
of the next known expiration we can exit early in case the timestamp is
in the future.

Closes: #7172
Reviewed-by: Daniel Stenberg <daniel@haxx.se>
lib/cookie.c
lib/cookie.h

index 537658fc6af1b79d222e435e3cee24a5683b05a7..4b99813bda7013203b8fa205ccae68dd5b6f308b 100644 (file)
@@ -371,7 +371,9 @@ static void strstore(char **str, const char *newstr)
  *
  * Remove expired cookies from the hash by inspecting the expires timestamp on
  * each cookie in the hash, freeing and deleting any where the timestamp is in
- * the past.
+ * the past.  If the cookiejar has recorded the next timestamp at which one or
+ * more cookies expire, then processing will exit early in case this timestamp
+ * is in the future.
  */
 static void remove_expired(struct CookieInfo *cookies)
 {
@@ -379,6 +381,20 @@ static void remove_expired(struct CookieInfo *cookies)
   curl_off_t now = (curl_off_t)time(NULL);
   unsigned int i;
 
+  /*
+   * If the earliest expiration timestamp in the jar is in the future we can
+   * skip scanning the whole jar and instead exit early as there won't be any
+   * cookies to evict.  If we need to evict however, reset the next_expiration
+   * counter in order to track the next one. In case the recorded first
+   * expiration is the max offset, then perform the safe fallback of checking
+   * all cookies.
+   */
+  if(now < cookies->next_expiration &&
+      cookies->next_expiration != CURL_OFF_T_MAX)
+    return;
+  else
+    cookies->next_expiration = CURL_OFF_T_MAX;
+
   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
     struct Cookie *pv = NULL;
     co = cookies->cookies[i];
@@ -395,6 +411,12 @@ static void remove_expired(struct CookieInfo *cookies)
         freecookie(co);
       }
       else {
+        /*
+         * If this cookie has an expiration timestamp earlier than what we've
+         * seen so far then record it for the next round of expirations.
+         */
+        if(co->expires && co->expires < cookies->next_expiration)
+          cookies->next_expiration = co->expires;
         pv = co;
       }
       co = nx;
@@ -1108,6 +1130,13 @@ Curl_cookie_add(struct Curl_easy *data,
     c->numcookies++; /* one more cookie in the jar */
   }
 
+  /*
+   * Now that we've added a new cookie to the jar, update the expiration
+   * tracker in case it is the next one to expire.
+   */
+  if(co->expires && (co->expires < c->next_expiration))
+    c->next_expiration = co->expires;
+
   return co;
 }
 
@@ -1143,6 +1172,11 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
     c->filename = strdup(file?file:"none"); /* copy the name just in case */
     if(!c->filename)
       goto fail; /* failed to get memory */
+    /*
+     * Initialize the next_expiration time to signal that we don't have enough
+     * information yet.
+     */
+    c->next_expiration = CURL_OFF_T_MAX;
   }
   else {
     /* we got an already existing one, use that */
index 460bbb10405c56867b07d8d811de51c18c3b9040..0ffe08e63a6f9d4f3d6003fcb905e4cf39ad301b 100644 (file)
@@ -65,6 +65,7 @@ struct CookieInfo {
   bool running;    /* state info, for cookie adding information */
   bool newsession; /* new session, discard session cookies on load */
   int lastct;      /* last creation-time used in the jar */
+  curl_off_t next_expiration; /* the next time at which expiration happens */
 };
 
 /* This is the maximum line length we accept for a cookie line. RFC 2109