fnCreateCookie delete cookie logic is flawed (HTTP 400: Size of request headers is too long)

fnCreateCookie delete cookie logic is flawed (HTTP 400: Size of request headers is too long)

rasmusvhansenrasmusvhansen Posts: 4Questions: 0Answers: 0
edited February 2012 in Bug reports
When using DataTables.Net and bStateSave I occasionally get an "HTTP 400. The size of the request headers is too long" error.
Once this has occurred it keeps occuring until I delete the cookies.

After doing a lot of debugging I finally realized that the problem was that the function creates a cookie using the current path, but when it comes to deleting the oldest cookie it ALSO uses current path and NOT the path with which the cookie was created.

An example to reproduce the error:

1. The oldest SpryMedia_DataTables_ cookie is created in some obscure subpath e.g (/My/Very/Long/Obscure/Path/)
2. All subsequent requests occur on a different path e.g. (/My/Path/)
3. When the limit of 4KB is hit fnCreateCookie will try to delete the oldest cookie, but since it uses the current path (/My/Path/) the cookie is never deleted and at some point a "HTTP 400. The size of the request headers is too long" error is shown which cannot be recovered from without deleting cookies.

I solved this in my project by always creating and deleting cookies using the root path ("/"). This is not an optimal solution, but a lot better than getting an error which the user probably does not know how to recover from.

Here is the edited function:

[code]
function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
{
var date = new Date();
date.setTime( date.getTime()+(iSecs*1000) );

/*
* Shocking but true - it would appear IE has major issues with having the path not having
* a trailing slash on it. We need the cookie to be available based on the path, so we
* have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
* patch to use at least some of the path
*/
var aParts = window.location.pathname.split('/');
var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
var sFullCookie, oData;

if ( fnCallback !== null )
{
oData = (typeof $.parseJSON == 'function') ?
$.parseJSON( sValue ) : eval( '('+sValue+')' );
sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(), "/");
}
else
{
sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
"; expires=" + date.toGMTString() +"; path=/";
}

/* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
* belonging to DataTables. This is FAR from bullet proof
*/
var sOldName="", iOldTime=9999999999999;
var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length :
sFullCookie.length + document.cookie.length;

if ( iLength+10 > 4096 ) /* Magic 10 for padding */
{
var aCookies =document.cookie.split(';');
for ( var i=0, iLen=aCookies.length ; i

Replies

  • allanallan Posts: 63,383Questions: 1Answers: 10,449 Site admin
    I'm probably being blind, but I can't see the difference! Can you give me a diff of the change? I think this is probably quite a sensible change to make. I hated working with cookie paths...

    Allan
  • rasmusvhansenrasmusvhansen Posts: 4Questions: 0Answers: 0
    Sure thing,

    Here is a patch. Basically all I did was replace all instances of
    [code]aParts.join('/') + "/"[/code]
    in the function with
    [code]"/"[/code]

    The line numbers may not match the newest version of datatables, but I checked that the function is the same in the newest version.

    [code]
    --- jquery.dataTables-orig.js 2012-02-02 09:19:29.931385800 +0100
    +++ jquery.dataTables-fixed.js 2012-02-02 09:19:52.593385800 +0100
    @@ -6040,13 +6040,12 @@
    {
    oData = (typeof $.parseJSON == 'function') ?
    $.parseJSON( sValue ) : eval( '('+sValue+')' );
    - sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
    - aParts.join('/')+"/" );
    + sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(), "/");
    }
    else
    {
    sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
    - "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
    + "; expires=" + date.toGMTString() +"; path=/";
    }

    /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
    @@ -6066,11 +6065,13 @@
    /* It's a DataTables cookie, so eval it and check the time stamp */
    var aSplitCookie = aCookies[i].split('=');
    try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); }
    - catch( e ) { continue; }
    + catch( e ) {
    + continue;
    + }

    if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime )
    {
    - sOldName = aSplitCookie[0];
    + sOldName = $.trim(aSplitCookie[0]);
    iOldTime = oData.iCreate;
    }
    }
    @@ -6078,8 +6079,7 @@

    if ( sOldName !== "" )
    {
    - document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
    - aParts.join('/') + "/";
    + document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/";
    }
    }

    [/code]
  • rasmusvhansenrasmusvhansen Posts: 4Questions: 0Answers: 0
    edited February 2012
    Oh and please disregard the $.trim and reformatting of catch clause. Those were just some changes I made in the first attempts to debug what was going on.
This discussion has been closed.