gam3 has asked for the wisdom of the Perl Monks concerning the following question:

I am sure there is a package to compare 2 URIs, but I can't find it.

/bob?joe=1&jack=2 should match http://host/bob?jack=2&joe=1

Assuming that the base is http://host/index.html or some such.

Has this been written?

-- gam3
A picture is worth a thousand words, but takes 200K.

Replies are listed 'Best First'.
Re: Compare URIs
by ikegami (Patriarch) on Oct 18, 2005 at 16:02 UTC
    URI has an eq function/method but it doesn't consider ...?joe=1&jack=2 and ...?jack=2&joe=1 to be equivalent.
    use strict; use warnings; use URI (); my $base_uri = 'http://host/index.html'; my $uri1 = '/bob?joe=1&jack=2'; my $uri2 = 'http://host/bob?jack=2&joe=1'; $uri1 = URI->new($uri1)->abs($base_uri); $uri2 = URI->new($uri2)->abs($base_uri); print($uri1->canonical, "\n"); print($uri2->canonical, "\n"); print(URI::eq($uri1, $uri2) ? "equal" : "not equal", "\n");

    Output:

    http://host/bob?joe=1&jack=2 http://host/bob?jack=2&joe=1 not equal
      To be more clear, one should not assume that the order or uniqueness of CGI parameters is arbitrary. Changing the order or enforcing the uniqueness may break some web applications. CGI itself has no rules about parameter ordering or uniqueness. Your application may add such rules or assumptions, but that's up to you.

      --
      [ e d @ h a l l e y . c c ]

        I have read (in some spec, long ago) that web browsers may return fields in any order, which means the order of the CGI parameters *is* arbitrary. I can't find where I saw this, though.

        Update:

        If what I said was once the case, it is no longer. Quoting HTML docs (emphasis is mine):

        The control names/values are listed in the order they appear in the document. The name is separated from the value by `=' and name/value pairs are separated from each other by `&'.

        So halley is correct. HTTP clients and HTTP proxies should consider ...?joe=1&jack=2 and ...?jack=2&joe=1 to be different. (HTTP servers can do whatever they want, of course.)

Re: Compare URIs *params*
by jeffa (Bishop) on Oct 18, 2005 at 16:14 UTC

    This worked for me ...

    use URI; use URI::QueryParam; use Data::Compare; my $uri1 = URI->new('/bob?joe=1&jack=2'); my $uri2 = URI->new('http://host/bob?jack=2&joe=1'); print "URI's are ", Compare( {map {($_ => $uri1->query_param($_))} $uri1->query_param}, {map {($_ => $uri2->query_param($_))} $uri2->query_param}, ) ? "" : "not ", "identical.\n" ;
    The idea is to turn the URI's params into hash references via URI::QueryParam and map and compare them with Data::Compare. Assuming you only want to compare the params, of course. ;)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      I haven't installed that module, but could you please check what your program reporst for this inpu:

      my $uri1 = URI->new('/bob?joe=1&jack=2&jim=4&jim=3'); my $uri2 = URI->new('http://host/bob?jack=2&joe=1&jim=3&jim=4');

      s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
      +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e

        Ahhhh ... throwing monkey wrenches into my code, are we? ;)

        This, of course, is not going to work, because you have a key collision with 'jim'. If you dump the hash refs out, you get something like:

        $VAR1 = { 'jack' => '2', 'joe' => '1', '3' => undef, 'jim' => '4' }; $VAR2 = { '4' => undef, 'jack' => '2', 'joe' => '1', 'jim' => '3' };
        I think there is a way to coerce URI::QueryParam into producing a data structure like so:
        $VAR1 = { 'jack' => '2', 'joe' => '1', 'jim' => ['3','4'] };
        but the point is growing moot, as halley pointed out and ikegami (and myself) how now discovered. :/

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: Compare URIs
by reasonablekeith (Deacon) on Oct 18, 2005 at 16:12 UTC
    with the caveats as described above (++), you could pull, and sort the parameters. You're then left with a simple string comparison.
    use URI; my $u1 = URI->new('http://host/bob?jack=2&joe=1'); my $u2 = URI->new('http://host/bob?joe=1&jack=2'); if ( sort_query($u1->query()) ne sort_query($u2->query())) { print "They're different all right\n"; } sub sort_query { join('&', sort split /&/, $_[0]) }
    UPDATE: Ack, updated after ikegami's comments.
    ---
    my name's not Keith, and I'm not reasonable.