#!/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);