Raku Land

Cookie::Jar

zef:jjatria

NAME

Cookie::Jar - A minimalist HTTP user agent cookie jar

SYNOPSIS

use Cookie::Jar;

my $cookies = Cookie::Jar.new;

$cookies.add: 'https://example.com', 'foo=123; Domain=example.com';
$cookies.add: 'https://example.com', 'bar=234';

say "{ .name } -> { .value }" for $cookies.get: 'GET', 'https://example.com';
# OUTPUT:
# foo -> 123
# bar -> 234

# The 'bar' cookie does not apply to this subdomain
say $cookies.header: 'GET', 'https://www.example.com';
# OUTPUT:
# foo=123

# Cookies can persist on filesystem
my $other = $jar.save('cookie.jar').load('cookie.jar');

DESCRIPTION

This is a minimal class to manage HTTP cookies. It can be used together with a HTTP client class like HTTP::Tiny to manage cookies received in HTTP responses and determine the cookies that apply on new requests.

Unlike other alternatives available at the time of writing, user agents desiring to integrate with this class do not need to use a specific class to represent the request, the response, or the cookie: all that is required is the request URL and the headers received from the server.

The operations on this library should be thread-safe.

METHODS

Some of the methods below take a request URL parameter. In compliance with RFC 6265, these are all canonicalised to their ASCII "punycode" representation before processing.

Likewise, the methods in this class that return cookies (like get and dump described below) return a read-only opaque cookie object with the following available methods:

creation-time

A DateTime marking the moment the cookie was created. This could be before the jar was created if the cookie was loaded from external storage with load. This attribute is internal, and does not conflict with an extension of the same name.

domain

Returns a Str with the cookie's domain. If the cookie did not have a domain initially, this will be the domain the cookie was received from.

expired

Returns a Bool that will be true if the cookie is persistent and its expiration date is in the past. It can optionally take a DateTime object to be used instead of the current timestamp.

expires

Returns a DateTime with the cookie's expiration time. This might be a type object if the cookie is not persistent.

host-only

Returns a Bool specifying whether the cookie should apply to domains other than the one in its "domain" attribute. This attribute can also be called as hostonly.

http-only

Returns a Bool specifying whether this cookie should be included in non-HTTP requests (this is of little use for Raku user-agents). This attribute can also be called as httponly.

last-access-time

Returns a DateTime specifying when the cookie was last matched for a given URL (using the get or header methods). This attribute is internal, and does not conflict with an extension of the same name.

name

Returns a Str with the cookie name. This attribute is internal, and does not conflict with an extension of the same name.

path

Returns a Str with the path the cookie applies to.

persistent

Returns a Bool which will be true if the cookie has a defined expiration date.

secure

Returns a Bool specifying whether this cookie should be sent over non-secure channels. Cookies with this attribute set to a true value will only be matched for HTTPS requests.

value

Returns a Str with the value of the cookie. No effort is made by Cookie::Jar to parse or otherwise decode this value. This attribute is internal, and does not conflict with an extension of the same name.

get

Takes a key that will be matched case-insensitively to a cookie attribute. The key can be any of the ones mentioned above, or the name of any of the cookie's extensions. If the cookie does not have an attribute with that name this method will return False. Otherwise, the value of the attribute will be returned, or True if the attribute has no value. If the cookie was set with an attribute with the same name as one of the fields described as "internal" above (eg. 'creation-time' and 'last-access-time', etc), using that key with this method will return the extension.

new

method new () returns Cookie::Jar

Creates a new Cookie::Jar object. The constructor takes no parameters.

To construct a Cookie::Jar with cookies that have been saved on a previous session (with the save method), see the load method below.

add

method add (
    Str:D $url,
    Str:D $cookie-string,
) returns Bool

Add a new cookie to the internal storage.

This method takes a URL and a Set-Cookie header string (received from making a request to that URL) and adds the cookie to the jar. If the cookie is expired, any matching cookie is deleted as described in RFC 6265 § 4.1.2.

If the PublicSuffix module is available, it will be used to validate cookie domains and reject supercookies.

Returns True if the cookie was successfully added, or False otherwise.

dump

method dump () returns Hash

This method offers some introspection by returns a Hash representation of the internal storage used by the jar.

The data structure store cookies indexed by nested domain, path, and cookie name keys, as in the following example:

{
    'example.com' => {
        '/path' => {
            'SID' => ..., # see below for details on cookie values
        }
    },
}

Cookies in this structure (the parts represented by the ellipsis) will be returned as an internal read-only Cookie object. For details on this Cookie object, see above.

get

method get (
    Str:D $method,
    Str:D $url,
) returns List

Takes a request method and URL and returns a (possibly empty) list of cookies that would apply to a request made to that URL, as described in RFC 6265 § 5.4. The order of the cookies in the list follows the same order they would take in a Cookie header sent with that request, as per the document above.

The method must be one of CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, or TRACE, and will be matched case-sensitively, as per HTTP/1.1 specification.

Elements in the list will be internal read-only Cookie objects. For details on this Cookie object, see above.

header

method header (
    Str:D $method,
    Str:D $url,
) returns Str

Takes a request method and URL and returns a (possibly empty) string suitable to be used as the Cookie header sent to a request made to that URL. The order of the cookies in the string follows the order described in RFC 6265 § 5.4.

The method must be one of CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, or TRACE, and will be matched case-sensitively, as per HTTP/1.1 specification.

This method calls get internally.

clear

multi method clear ( ) returns Cookie::Jar

multi method clear (
    Str $url,
       *@names,
) returns Cookie::Jar

Clear the internal storage of the jar.

When called with no arguments, all values will be deleted.

This method can alternatively be called with a URL and a possibly empty list of cookie names. In this case cookies that would match a request made to that URL (as per the same rules used by the get method), and possibly one of the names provided, then those matching cookies will be deleted.

save

method save (
    IO()    $path,
    Bool() :$netscape,
    Bool() :$all,
) returns Cookie::Jar

Save the internal storage into a cookie file. This file will is suitable to be read with the load method below. Only persistent cookies will be saved, unless the :all flag is set to a true value.

By default, the file will be written using an internal format used by Cookie::Jar. If the :netscape flag is set to a true value, the file will instead use the Netscape HTTP Cookie File format, compatible with those created by libcurl.

This method returns the calling object.

load

method load (
    IO()    $path,
    Bool() :$netscape,
) returns Cookie::Jar

Load cookies from a cookie file, like that generated with save.

If called on an existing instance, the internal storage will be cleared before reading the input file. This method can also be called on a Cookie::Jar type object to be used as a constructor.

In both cases, this method will return a Cookie::Jar object with the cookies loaded from the read file.

By default, the file is assumed to use the internal Cookie::Jar format. If the file is found to have a Netscape HTTP Cookie File header instead (like the one generated by libcurl), the file will be parsed as such. Setting the :netscape flag to a true value enforces this, such that providing a file missing this header is an error.

LIMITATIONS

Cookie::Jar aims to be conditionally compliant with the HTTP State Management Mechanism RFC 6265.

It aims to meet all "MUST" requirements of the specification, but only some of the "SHOULD" requirements.

Some particular limitations of note include:

SEE ALSO

Cro::HTTP::Client::CookieJar

Shipped as part of Cro::HTTP and is used by that client. The client cannot be configured to use another cookie jar.

HTTP::Cookies

Shipped as part of HTTP::UserAgent and is used by that client. The client cannot be configured to use another cookie jar.

Additional Features

Cookie::Baker

Cookie::Baker provides two functions, one of which, bake-cookie, can be used to convert a Hash into a string that can be used as a Cookie header. This can be used together with the add method to add a cookie from a set of key-value pairs.

PublicSuffix

If this distribution is installed, Cookie::Jar will use it to verify cookie domains against the list of public suffixes, as described in step 5 of RFC 6265 § 5.3. This should help avoid supercookies.

AUTHOR

José Joaquín Atria jjatria@cpan.org

ACKNOWLEDGEMENTS

Other than the cookie jars mentioned above, the code and API in this distribution takes inspiration from a number of similar Perl libraries. In particular:

This module owes a debt of gratitude to their authors and those who have contributed to them, and to their choice to make their code and work publicly available.

COPYRIGHT AND LICENSE

Copyright 2022 José Joaquín Atria

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.