5

I have a bit of Perl CGI code which I'm trying to run in the project web space of a SourceForge account. The code is able to set a browser cookie just fine when talking to IE, but the cookie is not set when talking to Firefox. When I test with Apache on "localhost", both browsers work fine. It's only on the remote SourceForge URL that Firefox craps out.

A search has turned up dozens of near-duplicate questions, but usually people have the exact opposite problem! (Firefox being fine and IE having the problem)

Here is the utility sub I'm calling to set cookies:

sub setCookie {
    my $name = shift;
    my $value = shift;
    my $expires = shift;
    my $path = shift;
    my $domain = shift;
    if( !defined( $expires ) ) {
        $expires = '+4h';
    }
    if( !defined( $path ) ) {
        $path = '/';
    }
    if( !defined( $domain ) ) {
        $domain = 'steveperkins.sourceforge.net';
    }
    my $cookie = CGI::cookie(
        -name    => $name,
        -value   => $value,
        -domain   => $domain,
        -expies => $expires,
        -path    => $path
    );
    $r->header_out('Set-cookie' => $cookie);
}

Any ideas? My first thought was some kind of subdomain issue, because my SourceForge project URL has a subdomain in it while "localhost" does not. I've experimented with setting the cookie domain to my specific subdomain, or to just the base "sourceforge.net". It doesn't seem to make a difference either way.

UPDATE: Someone in the comments below asked about the HTTP response headers. I've used the network traffic analyzer tool Wireshark to monitor the request and response headers for both IE and Firefox, and here's what they look like:

IE (works)

Request

GET http://myproject.sourceforge.net/cgi-bin/myscript.cgi?page=user&userID=1 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://myproject.sourceforge.net/cgi-bin/myscript.cgi
Accept-Language: en-us
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.5.30729; InfoPath.1; .NET CLR 3.0.30618)
Proxy-Connection: Keep-Alive
Host: myproject.sourceforge.net
Authorization: Basic [password omitted]

Response

HTTP/1.1 200 OK
Server: nginx/0.7.63
Date: Tue, 26 Oct 2010 18:23:49 GMT
Content-Type: text/html; charset=ISO-8859-1
Expires: Thu, 28 Oct 2010 18:23:49 GMT
Cache-Control: max-age=172800, proxy-revalidate
Transfer-Encoding: chunked
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Encoding: gzip
Set-Cookie: USER=1; domain=myproject.sourceforge.net; path=/

Firefox (doesn't work)

Request

GET http://myproject.sourceforge.net/cgi-bin/myscript.cgi HTTP/1.1
Host: myproject.sourceforge.net
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive
Cookie: __utma=191645736.1501259260.1287701281.1288028150.1288100562.10; __utmz=191645736.1288101011.10.10.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=sourceforge%20project%20web%20space%20basic%20auth; _jsuid=4215309712123065236
Authorization: Basic [password omitted]

Response

HTTP/1.1 200 OK
Server: nginx/0.7.63
Date: Tue, 26 Oct 2010 18:17:58 GMT
Content-Type: text/html; charset=ISO-8859-1
Expires: Thu, 28 Oct 2010 18:17:58 GMT
Cache-Control: max-age=172800, proxy-revalidate
Transfer-Encoding: chunked
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Encoding: gzip
Age: 0
Steve Perkins
  • 11,520
  • 19
  • 63
  • 95
  • `-expies` should be `-expires` btw – mfontani Oct 26 '10 at 15:33
  • Thanks, I've corrected it. Doesn't resolve the problem, though. – Steve Perkins Oct 26 '10 at 15:39
  • are you using `CGI` or `mod_perl`? – Nikhil Jain Oct 26 '10 at 16:04
  • Can you provide a dump of the HTTP headers when the cookie is set? – Pierre-Luc Simard Oct 26 '10 at 16:18
  • Step #1 of debugging cookies: check the clock on both the server and client(s). – hobbs Oct 26 '10 at 16:27
  • @Nikhil Jain: The servers are SourceForge's, so I'm not 100% sure. However, given the volume they handle I would assume mod_perl. What are you leading toward? – Steve Perkins Oct 26 '10 at 17:10
  • @Pierre-Luc Simard: I've updated the question text above to include the HTTP response. – Steve Perkins Oct 26 '10 at 17:11
  • @hobbs: Good point! I notice that the remote server is on GMT, while my browsing machine is on the local timezone. However, accounting for the timezone difference, the client shouldn't be ahead of the server or anything like that. Are there GMT-to-local issues with cookies that I'm overlooking? – Steve Perkins Oct 26 '10 at 17:16
  • @Steve Perkins, I would take a look at what's happening with both browser using Wireshark. It is possible that the Web Developer Toolbar does not show everything. – Pierre-Luc Simard Oct 26 '10 at 17:19
  • @Pierre-Luc Simard: Thanks for pointing me toward that Wireshark tool, it seems extremely useful. At any rate, I've used it to capture the full HTTP request and response for both IE and Firefox as they go across the wire. I've updated the question above to include this full information. The HTTP request sent by IE looks somewhat different than the one sent by Firefox, and indeed the response sent back to Firefox is completely missing the "Set-Cookie" header that is sent to IE. – Steve Perkins Oct 26 '10 at 18:36
  • @Steve Perkins, the URL seems different in both request. Would your code do things differently if no arguments are passed to your CGI (Firefox trace) vs when they are (IE trace). Also IE provides Cookies already where FF does not; it could be something with that too. – Pierre-Luc Simard Oct 26 '10 at 19:17

2 Answers2

5

I would say that you've got a bug in setting your expiration

if( !defined( $path ) ) {
    $expires = '/';
}

should be

if( !defined( $path ) ) {
    $path = '/';
}

Updated: Based on the information you provide above using wireshark I would check if the setCookie is actually called when Firefox comes in. (both URL are different btw, it could be an indication that the logic in your code skips the setCookie call based on the URL). Also try to use the same URL with both browser and see what happens.

Pierre-Luc Simard
  • 2,723
  • 19
  • 27
  • Ha! Fair enough, but I fixed that earlier... I was cut-n-pasting from an older copy. I've updated the code snippet in the question above, and fixing that bug doesn't fix the issue. – Steve Perkins Oct 26 '10 at 15:36
2

Argh! It turns out the issue is that there is only one cookie in play when running on "localhost", but there are multiple cookies in play when hosted on SourceForge's servers.

If you look at the Firefox request headers cut-n-pasted in the question above, you'll notice that there are several cookie name-value pairs... with each pair separated by semicolons. My code was failing to account for this, so all it saw was one giant mal-formed cookie.

I'm still not 100% sure why it was partially working in IE, and I may revisit this in the future to see if more can be learned. But it's basically a moot point for now. I altered the code to split on semicolons AND then split on equals signs, I'm processing cookies just fine now.

Thanks to everyone for your insight and suggestions! Pierre-Luc, I did give your answer an upvote out of gratitude for all the comments beneath it.

    sub getCookie {
    my $cookieName = shift;
    my %headers = $r->headers_in;
    my @keys = keys( %headers );
    foreach my $name ( @keys ) {
        if( $name eq 'Cookie') {
        my @semicolontokens = split( ';', $headers{$name} );
        foreach my $splitname ( @semicolontokens ) {
           $splitname =~ s/^\s+//;
           $splitname =~ s/\s+$//;
           my @pair = split( '=', $splitname );
               if( $pair[0] eq $cookieName ) {
               return $pair[1];
               }
           }
       }
   }
Steve Perkins
  • 11,520
  • 19
  • 63
  • 95
  • Thanks for updating us on your progress. I'd suggest you post updated code in your answer so people can see the final answer if they stumble on this page. – Pierre-Luc Simard Oct 27 '10 at 16:17