The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

WebCache::ICP - Internet Cache Protocol client and server

SYNOPSIS

  use WebCache::ICP;

  $i = new WebCache::ICP;
  $i->opcode("OP_QUERY");
  $i->payload("http://www.w3.org/");
  $i->send(host => $host, port => $port);
  $i->recv;
  $i->dump;
  $i->hex_dump;

  $j = new WebCache::ICP($packet);
  $j->dump;

  $k = new WebCache::ICP;
  $k->server(callback => \&not_so_stubby);

  # sample callback, just prints URLs requested
  sub not_so_stubby {
    my ($self, $fd, $sin, $request) = @_;
    $self = $self->burst($request);
    print "url: " . $self->payload . "\n";
    $self->opcode("OP_MISS_NOFETCH");
    $self->send(fd => $fd, sin => $sin);
  }

DESCRIPTION

This Perl module implements client and server side support for the Internet Cache Protocol, as defined in RFCs 2186 and 2187. It provides methods for creating ICP packets, breaking the contents of ICP packets out into a Perl object, sending and receiving ICP packets, plus a simple ICP server which you can bolt your own callback functions onto.

NB This is just a first release, so expect the unexpected! Many features either don't work properly, or don't exist yet :-)

METHODS

ICP objects can either be created anew:

  $i = new WebCache::ICP;

Or created based on the contents of a variable, which it's assumed is a raw ICP packet, fresh off the wire:

  $i = new WebCache::ICP($packet);

If you don't supply a variable from which the ICP object should be created, we try to set you up with some sensible defaults - creating a query packet (opcode 2 or "OP_QUERY"), protocol version 2, a sequence number of 1, and no options.

Note that you can re-use a WebCache::ICP object over and over again, so as to avoid incurring the overheads of object creation and destruction with high volume client/server applications.

The following methods may be used on ICP objects to get or (with optional parameter) set the corresponding fields in the ICP packet:

$i->opcode

This method fetches the ICP opcode from the object. This is normally a number between 0 and 23. With a numeric parameter, the opcode for the object is set to this number. You can also supply one of the following strings as an alternative to the numeric parameter:

    "OP_INVALID"
    "OP_QUERY"
    "OP_HIT"
    "OP_MISS"
    "OP_ERR"
    "UNUSED5"
    "UNUSED6"
    "UNUSED7"
    "UNUSED8"
    "UNUSED9"
    "OP_SECHO"
    "OP_DECHO"
    "UNUSED12"
    "UNUSED13"
    "UNUSED14"
    "UNUSED15"
    "UNUSED16"
    "UNUSED17"
    "UNUSED18"
    "UNUSED19"
    "UNUSED20"
    "OP_MISS_NOFETCH"
    "OP_DENIED"
    "OP_HIT_OBJ"

Each of these will be interpolated into the appropriate ICP opcode internally by the WebCache::ICP module. We don't check the opcode number is valid, so if you wanted to, you could put opcodes greater than 23 in here...

$i->version

This method lets you set or fetch the ICP protocol version number. Currently this is set to 2 for most/all real world applications of ICP, but you could put the version number of your choice in here.

$i->length

This method lets you fetch the packet length of an ICP object when it's expressed as an on-the-wire packet. This may get a bit confused if you manipulate the contents of an ICP object, but it's 'guaranteed' to be correct if you create a new ICP object based on a raw packet.

$i->sequence

This method lets you fetch (or, with an optional parameter: set), the ICP sequence number.

$i->options

This method lets you fetch or set the options field for the ICP object. At the moment we simply expect you to supply a plausible value for this. In future there will probably be a more sophisticated way to set a particular option by name.

$i->option_data

This method lets you fetch or set the supplementary data associated with the options set for the ICP object. Again we assume that you know what you want to put in here!

$i->peer_addr

This method lets you fetch or set the source address associated with the ICP packet once sent/received down the wire. In practice this is normally set to all zeroes.

$i->payload

This method lets you fetch or set the payload proper of the ICP packet.

$i->assemble

This method turns an ICP object into an on-the-wire format packet, which is returned as its result. Note that it adds a 4 byte null filled prefix to the payload of ICP query packets. This is normally used in ICP to record the IP address of the machine which is causing the ICP query to be sent.

$i->burst

This method takes a raw ICP packet and turns it into an ICP object. It's used behind the scenes by the 'new' method when constructing an ICP object from an on-the-wire packet, but you can also call it directly.

$i->dump

This method dumps out the contents of the various ICP header fields and the payload, as interpreted by the WebCache::ICP module, to STDOUT. It's intended for use in debugging.

$i->hex_dump

This method tries to create an ICP on-the-wire format packet and then dump it out to STDOUT, based on the WebCache::ICP object being operated on.

$i->send

This method causes an ICP wire format packet to be sent. It can be used in a variety of ways depending on the options which it's called with, viz.

If you already have the packet you want to send, you can supply the 'packet' parameter:

  $i->send(packet => $p);

If not, the packet will be generated from the WebCache::ICP object being operated on, using the 'assemble' method.

If you already have an active socket for talking to the server, you can supply its file handle and packed IP address/port using the 'fd' and 'sin' parameters:

  $i->send(fd => \*SOCK, sin => $s);

If not, you'll need to supply the 'host' and 'port' parameters - the socket will automatically be created, and then destroyed after the packet is sent.

$i->recv

This method waits for an ICP packet to arrive, and then returns the raw packet for further processing. You can either pass the file handle of a socket which is already bound to the port:

  $i->recv(fd => \*SOCK);

or you can supply the port number yourself:

  $i->recv(port => 3131);

in which case the 'recv' method will take care of getting you a socket and destroying it later.

$i->server

This method uses pretty much all of the other code to implement a simple ICP server with user defined callbacks to be executed on receipt of an incoming ICP packet. Once it is called, the flow of execution will pass here, and the program will block waiting for packets to arrive...

You can either pass the file handle of a socket which is already bound to the port you want to listen on:

  $i->server(fd => \*SOCK);

or you can supply the port number yourself:

  $i->server(port => 3131);

in which case the 'server' method will take care of getting you a socket and destroying it later.

The ICP server comes with a simple built-in callback function, which always returns an ICP hit ("OP_HIT") response using the 'send' method, and logs the packet received to STDOUT using the 'dump' method. This default callback will be invoked if you run the server without nominating an alternative.

If you supply a reference to a function in the 'callback' parameter, the 'server' method will try to pass this the packet contents and peer info on receipt of each packet, e.g.

  $i->server(callback => \&not_so_stubby);

The return code from the callback function isn't used in the 'server' method. The callback function will be supplied with three parameters:

  1) the file descriptor which the packet was received on
  2) the packed IP address/port number from which it was received
  3) the raw on-the-wire ICP packet which was received

TODO

Need to factor in an opcode for ICP referrals.

Should have ability to get/set options by name.

Should be possible to run ICP over TCP - cf. HTCP, though.

Should allow for holding connection open when running over TCP.

Should be possible to have the ICP server running in non-blocking mode, possibly as part of a larger set of file handles using select.

BUGS

This is just a first release, so expect the unexpected!

The hex_dump method doesn't take account of the 4 byte client IP address prefix to the payload of ICP query packets.

We currently bind to INADDR_ANY when listening for incoming ICP packets, but we should probably let the luser choose which interface(s) to use.

Timeouts and corrupted packets aren't handled gracefully yet.

There's no way with the 'recv' method to find out the peer's IP address and port number, since we only return the raw packet contents.

Originally used IO::Socket, but stopped because it seemed to leak memory.

COPYRIGHT

Copyright (c) 1999, Martin Hamilton <martinh@gnu.org>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

It was developed by the JANET Web Cache Service, with funding from the Joint Information Systems Committee (JISC) of the UK Higher Education Funding Councils and TERENA, the Trans-European Research and Education Networking Association.

AUTHOR

Martin Hamilton <martinh@gnu.org>