#!/usr/bin/perl -w
# md5_serve.pl - Don Yang (uguu.org)
#
# Serve a binary file on HTTP, send it once and compute MD5.
#
# ./md5_serve.pl [port]
#
# If port is not specified, 80 is used. File type is always
# application/octet-stream.
#
# This works fine on small files, but on large files, it really slows
# down the server trying to compute MD5 and send packets at the same
# time.
#
# 09/22/07
use strict;
use Digest::MD5;
use Socket;
use constant READ_BUFFER_SIZE => 0x10000;
die "$0 [port]\n" unless $#ARGV == 0 || $#ARGV == 1;
# Set port
my $port = 80;
if( $#ARGV == 1 )
{
$port = $ARGV[1];
unless( $port =~ /^\d+$/ && $port > 0 )
{
die "Invalid port $ARGV[1]\n";
}
}
# Open input
my $infile = $ARGV[0];
my $size = -s $infile;
die "$infile is empty\n" if $size <= 0;
my $file_handle;
open $file_handle, "< $infile" or die "$!\n";
# Start listening
my $server;
die $! unless(
socket($server, PF_INET, SOCK_STREAM, getprotobyname("tcp")) &&
bind($server, sockaddr_in($port, INADDR_ANY)) &&
listen($server, 5) &&
binmode($server) );
print "Serving $infile on port $port\nFile size: $size bytes\n";
# Wait for connection
my $rin = "";
vec($rin, fileno($server), 1) = 1;
my ($rout, $client);
while( select($rout = $rin, undef, undef, undef) )
{
if( vec($rout, fileno($server), 1) )
{
my $addr = accept($client, $server);
my ($j, $i) = sockaddr_in($addr);
print "Sending file to ", inet_ntoa($i), ":$j\n";
last;
}
}
# Process request
my $request;
binmode($client);
die $! unless defined(recv($client, $request, 1024, 0));
print "Request:\n$request\n";
die "Not a GET request\n" unless $request =~ /^GET /;
# Send header
my $data =
"HTTP/1.0 200 OK\015\012" .
"Content-Type: application/octet-stream\015\012" .
"Content-Length: $size\015\012" .
"Connection: close\015\012\015\012";
defined(send($client, $data, 0)) or die $!;
# Send and digest file data
my $md5 = Digest::MD5->new;
my $read_size;
my $send_size = 0;
my $time = time;
for(my $i = 0; $i < $size; $i += $read_size)
{
$read_size = ($size - $i > READ_BUFFER_SIZE) ? READ_BUFFER_SIZE : $size - $i;
read($file_handle, $data, $read_size);
defined(send($client, $data, 0)) or die $!;
$md5->add($data);
$send_size += $read_size;
print "\rsend $send_size";
}
# Print final checksum
$time = time - $time;
print
"\nSent $send_size bytes in $time seconds\n",
"MD5: ", $md5->hexdigest, "\n";
# Cleanup
shutdown($client, 2);
shutdown($server, 2);
close($client);
close($server);
close($file_handle);