Un Uniform Resource Identifier (URI)
es una cadena de caracteres que identifica o da nombre a un recurso.
Una URI puede ser un Uniform Resource Locator (URL) o
un Uniform Resource Name (URN) o ambos.
Un URN es una URI que identifica el recurso dentro del contexto de un espacio
de nombres particular. Por ejemplo la URN urn:isbn:0-395-36341-1
identifica un libro pero - a diferencia de lo que ocurre en una URL -
no nos dice como localizarlo.
El ejemplo de descarga de páginas HTML introducido en la sección 3.10 pone de manifiesto un problema: El intento de analizar los componentes de un Uniform Resource Locator (URL) utilizando expresiones regulares está condenado al fracaso. Los URLs tiene una estructura definida en RFC 2396 y RFC 2732.
Estos son los componentes de una URL típica y sus nombres:
http://user:pass@example.com:992/animal/bird?species=seagull#wings \___/ \________/\_________/\__/\__________/\______________/\____/ | | | | | | | protocol login hosts port path query anchor/fragment
Para analizar una URL se debe usar el módulo URI . Veamos un ejemplo de uso:
pp2@nereida:~/src/perl/LWP$ cat -n uri.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use URI; 4 5 my $surl = shift || die "Se esperaba una URL\n"; 6 my $url = URI->new($surl); 7 8 print "Scheme: '",$url->scheme(),"'\n"; 9 print "Userinfo: '",$url->userinfo(),"'\n"; 10 print "Host: '",$url->host(),"'\n"; 11 print "Port: '",$url->port(),"'\n"; 12 print "Path: '",$url->path(),"'\n"; 13 print "Query: '",$url->query(),"'\n"; 14 print "Fragment: '",$url->fragment(),"'\n";Siguen las salidas de dos ejecuciones del programa anterior:
pp2@nereida:~/src/perl/LWP$ uri.pl http://user:pass@example.com:992/animal/bird?species=seagull#wings Scheme: 'http' Userinfo: 'user:pass' Host: 'example.com' Port: '992' Path: '/animal/bird' Query: 'species=seagull' Fragment: 'wings' pp2@nereida:~/src/perl/LWP$ uri.pl http://example.com/animal/bird#wings Scheme: 'http' Use of uninitialized value in print at ./uri.pl line 9. Userinfo: '' Host: 'example.com' Port: '80' Path: '/animal/bird' Use of uninitialized value in print at ./uri.pl line 13. Query: '' Fragment: 'wings'
Los únicos caracteres permitidos en el path de una URL son
los que casan con m{[\w:_.!~*',:@&+$()/]}
mientras que en
el query son los que pertenecen al lenguaje
m{[\w_.!~*'()-}
. Cualquier otro carácter debe ser
codificado URL (también conocido como
Percent-encoding y URL encoding): expresado mediante
un símbolo de tanto por ciento seguido de los dígitos hexadecimales
para ese carácter.
La función uri_escape en el módulo URI::Escape realiza el codificado URL de cualesquiera caracteres que deban ser codificados:
pp2@nereida:~/src/perl/LWP$ perl -MURI::Escape -wde 0 main::(-e:1): 0 DB<1> $nombre = uri_escape("Juan Hernández") DB<2> $query = "name=$nombre+age=35+country=Spain" DB<3> print $query name=Juan%20Hern%E1ndez+age=35+country=Spain
La función uri_escape_utf8 codifica los caracteres en UTF-8 antes de escaparlos.
El método clone nos permite copiar un objeto URI:
pp2@nereida:~/src/perl/LWP$ perl -wd uri.pl http://user:pass@example.com:992/animal/bird?species=seagull#wings main::(uri.pl:5): my $surl = shift || die "Se esperaba una URL\n"; DB<1> c 8 main::(uri.pl:8): print "Scheme: '",$url->scheme(),"'\n"; DB<2> $copy = $url->clone() DB<3> x ($url, $copy) 0 URI::http=SCALAR(0x83fd460) -> 'http://user:pass@example.com:992/animal/bird?species=seagull#wings' 1 URI::http=SCALAR(0x83fdc88) -> 'http://user:pass@example.com:992/animal/bird?species=seagull#wings'
DB<4> $copy->path('/weblogs') DB<5> x ($copy->path, $url->path) 0 '/weblogs' 1 '/animal/bird'
Al tratar un objeto de la clase URI
como una cadena
se obtiene la cadena que describe la URL:
DB<6> print "$copy" http://user:pass@example.com:992/weblogs?species=seagull#wings
canonical normaliza una URI:
pp2@nereida:~/src/perl/LWP$ perl -MURI -wde 0 DB<1> $u = URI->new('HTTP://user:pass@example.com:992/animal/bird?species=%41eagull%2ewings') DB<2> $w = $u->canonical() DB<3> x ($u, $w) 0 URI::http=SCALAR(0x83ffc84) -> 'HTTP://user:pass@example.com:992/animal/bird?species=%41eagull%2ewings' 1 URI::http=SCALAR(0x84004ac) -> 'http://user:pass@example.com:992/animal/bird?species=Aeagull.wings'
eq
dice si las dos URIs son iguales. dos URIs son iguales
si su representación canónica es la misma:
DB<4> p $u->eq($w) 1Si lo que se quiere ver es que las dos referencias son iguales se debe usar el operador
==
DB<5> p $u == $w DB<6> $z = $u DB<7> p $u == $z 1El operador
eq
devuelve TRUE
si las dos cadenas asociadas son iguales:
DB<8> p $u eq $z 1 DB<9> p $u eq $w
Recuerde que las componentes que describen el fichero y directorio diferencian entre el uso de mayúsculas y minúsculas (si es que el S.O. lo hace) Esto no ocurre con la componente que define el dominio.
Métodos auxiliares de utilidad son path_query , path_segments , host_port y default_port :
pp2@nereida:~/src/perl/LWP$ perl -MURI -wde 0 main::(-e:1): 0 DB<1> $u = URI->new('http://user:pass@example.com:992/animal/bird?species=seagull#wings') DB<2> x $u->path_query 0 '/animal/bird?species=seagull' DB<3> x $u->path_segments # contexto de lista 0 '' 1 'animal' 2 'bird' DB<4> x scalar($u->path_segments) # contexto escalar 0 '/animal/bird' DB<5> x $u->host_port 0 'example.com:992' DB<6> x $u->default_port 0 80
En general, cuando una componente no existe
el correspondiente método devuelve undef
:
DB<7> $v = URI->new('http://example.com/index.html') DB<8> print "not defined" unless defined($v->query) not defined
Sin embargo ciertas componentes no tiene sentido en ciertas
clases de URLs. Por ejemplo un URL del tipo mailto:
no dispone de una componente host
. En tal caso, la llamada
al método host produce una excepción:
DB<9> $m = URI->new('mailto:name@domain.es') DB<10> print $m->host Can't locate object method "host" via package "URI::mailto" \ at (eval 18)[/usr/share/perl/5.8/perl5db.pl:628] line 2.La excepción puede evitarse comprobando el esquema o usando el método
UNIVERSAL
can
:
DB<11> $x = $m->can('host')? $m->host : "No host" DB<12> print $x No host
En los primeros tiempos de internet los argumentos de una
llamada a un procesador de un formulario (queries) eran cadenas
separadas por el signo '+
'
pp2@nereida:~/src/perl/LWP$ perl -MURI -wde 0 DB<1> $v = URI->new('http://www.example.com/search.pl?LWP+module+perl') DB<2> x $v->query_keywords() 0 'LWP' 1 'module' 2 'perl'El método query_keywords permite obtener la lista de argumentos cuando la llamada ocurre de acuerdo con este protocolo. Posteriormente se ha adoptado una forma de llamada con nombres:
DB<4> $w = URI->new('http://www.example.com/search.pl?lib=LWP&word=module&language=perl') DB<5> x $w->query_form() 0 'lib' 1 'LWP' 2 'word' 3 'module' 4 'language' 5 'perl'en este caso usamos el método query_form
Una URL absoluta comienza con el esquema y contiene todas las componentes que el esquema requiere. Por ejemplo, la URL
http://nereida.deioc.ull.es/~pp2/perlexamples/node37.htmles absoluta.
Cualquier URL que no comienza con un esquema es relativa. Para determinar el recurso al que se refiere una URL relativa es necesario prefijar una URL base absoluta. Se usa una notación que semeja la notación Unix para describir la jerarquía de archivos:
Las siguientes URL son relativas:
perlexamples.css
node38.html
/PP2/cgisearch.pl
../apuntes/node127.html
$abs = URI->new_abs($relative, $base);Por ejemplo:
casiano@beowulf:/tmp/Net-Server-0.96/examples$ perl -MURI -dwe 0 DB<1> $base = 'http://nereida.deioc.ull.es/~pp2/perlexamples/node37.html' DB<2> $can = URI->new_abs('../apuntes/node127.html', $base)->canonical DB<3> x $can 0 URI::http=SCALAR(0x83e75c0) -> 'http://nereida.deioc.ull.es/~pp2/apuntes/node127.html' DB<4> p $can http://nereida.deioc.ull.es/~pp2/apuntes/node127.html
El método rel permite la conversión de URLs absolutas en relativas:
$rel = $absolute->rel($absolute_base);Por ejemplo:
casiano@beowulf:/tmp/Net-Server-0.96/examples$ perl -MURI -dwe 0 DB<1> $pp2_37 = URI->new('http://nereida.deioc.ull.es/~pp2/perlexamples/node37.html') DB<2> $pl_37 = URI->new('http://nereida.deioc.ull.es/~pl/perlexamples/node37.html') DB<3> p $pl_37->rel($pp2_37) ../../~pl/perlexamples/node37.html