Introducing HTTP::CookieMonster

If you've ever had the pleasure of poking around in your WWW::Mechanize or LWP::UserAgent cookie_jar, you'll know it's not an entirely painless process. It's certainly not impossible, but it feels a bit like jumping through hoops. The cookie_jar functionality in LWP::UserAgent and the modules which inherit from it is provided by HTTP::Cookies. Before I go any further, I'd like to thanks Gisle Aas for HTTP::Cookies, which is a very important bit of code. This isn't a complaint about HTTP::Cookies, but rather an attempt to make it even more accessible.

Before we do anything with cookies, let's make one request to ensure we have some cookies in our cookie_jar.

    use WWW::Mechanize;
    my $mech = WWW::Mechanize->new;
    $mech->get('http://www.nytimes.com');

nytimes.com sets a couple of cookies by default and they are now in your cookie_jar. You can now get a list of your cookies (as objects) with just a "use" statement and 1 additional line of code.

    use HTTP::CookieMonster qw( cookies );
    my @cookies = cookies( $mech->cookie_jar );

Now you can iterate over your cookies. Available methods are documented at https://metacpan.org/module/HTTP::CookieMonster::Cookie.

    foreach my $cookie ( @cookies ) {
        print $cookie->key, $cookie->val, $cookie->expires, "\n";
    }

Maybe you just want to get one particular cookie without having to loop over an array. The NYT website serves an "RMID" cookie.

    my $cookie = cookies( $mech->cookie_jar, 'RMID' );

There's one caveat here. It's quite possible that you could have multiple cookies in your cookie jar with the same name, since there's nothing to stop many different domains (or subdomains) from serving up cookies with the same name. If you're not sure, get an array of cookies instead.

    my @cookies = cookies( $mech->cookie_jar, 'RMID' );

If you get more than one cookie, you'll need to figure out which one you really want. Calling cookies($cookie_jar, 'cookie_name') in scalar context will return the first matching cookie. Calling it in list context will return all cookies in the order in which HTTP::Cookies provides them.

Now, let's say you want to mangle your cookies. That's easy to do via the OO interface.

    use HTTP::CookieMonster;
    my $monster = HTTP::CookieMonster->new( $mech->cookie_jar );
    my $cookie = $monster->get_cookie('cookie_name');
    print $cookie->val;

$cookie->val('random stuff');
$monster->set_cookie( $cookie );

# now fetch a page using the mangled cookie
$mech->get( $url );

Now that we can easily read our cookies, we can also easily test them:

    use Test::Most;
    ok( $cookie->secure, "this is a secure cookie" );

It's also simple to add a new cookie to the jar:

    use HTTP::CookieMonster::Cookie;
    my $cookie = HTTP::CookieMonster::Cookie->new
       key       => 'cookie-name',
       val       => 'cookie-val',
       path      => '/',
       domain    => '.somedomain.org',
       path_spec => 1,
       secure    => 0,
       expires   => 1376081877
    );

$monster->set_cookie( $cookie );
$mech->get( $url );

HTTP::CookieMonster will re-examine your cookie_jar every time you call it. This means that it can keep up with changes to the cookie_jar which happen outside of your code. Just think of it as a simple wrapper around HTTP::Cookies. The interface is not yet set in stone, but I think it's quite useable already. Feedback is most welcome.

Leave a comment

About Olaf Alders

user-pic I hack on iCPAN, MetaCPAN and other fun stuff.