#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use Cwd qw(abs_path); use File::Spec; use URI::Escape qw(uri_unescape); my $port = $ARGV[0] || 8765; my $root = abs_path($ARGV[1] || 'website'); my %mime = ( html => 'text/html; charset=utf-8', txt => 'text/plain; charset=utf-8', css => 'text/css; charset=utf-8', js => 'text/javascript; charset=utf-8', json => 'application/json; charset=utf-8', webp => 'image/webp', png => 'image/png', jpg => 'image/jpeg', jpeg => 'image/jpeg', svg => 'image/svg+xml', ico => 'image/x-icon', woff2 => 'font/woff2', ); my $server = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'tcp', Listen => 10, ReuseAddr => 1, ) or die "无法启动本地网站服务:$!\n"; sub respond { my ($client, $status, $label, $type, $body) = @_; binmode $client; print $client "HTTP/1.1 $status $label\r\n"; print $client "Content-Type: $type\r\n"; print $client "Content-Length: " . length($body) . "\r\n"; print $client "Cache-Control: no-cache\r\n"; print $client "Connection: close\r\n\r\n"; print $client $body; } while (my $client = $server->accept()) { my $request = <$client> || ''; while (my $line = <$client>) { last if $line =~ /^\r?\n$/; } my ($method, $url) = split /\s+/, $request; if (!$url) { close $client; next; } $url =~ s/\?.*$//; $url = uri_unescape($url); $url =~ s{^/+}{}; $url = 'index.html' if $url eq ''; $url =~ s{/}{/}g; my $candidate = File::Spec->catfile($root, split('/', $url)); if ($candidate !~ /\.[^\/]+$/) { if (-f "$candidate.html") { $candidate .= '.html'; } elsif (-f File::Spec->catfile($candidate, 'index.html')) { $candidate = File::Spec->catfile($candidate, 'index.html'); } } my $full = abs_path($candidate); if (!$full || index($full, $root) != 0 || !-f $full) { my $not_found = File::Spec->catfile($root, '404.html'); if (-f $not_found) { open my $fh, '<:raw', $not_found; local $/; my $body = <$fh>; close $fh; respond($client, 404, 'Not Found', $mime{html}, $body); } else { respond($client, 404, 'Not Found', $mime{txt}, '404 Not Found'); } close $client; next; } open my $fh, '<:raw', $full or do { respond($client, 500, 'Internal Server Error', $mime{txt}, '读取网站文件失败'); close $client; next; }; local $/; my $body = <$fh>; close $fh; my ($ext) = $full =~ /\.([^.]+)$/; $ext = lc($ext || ''); respond($client, 200, 'OK', $mime{$ext} || 'application/octet-stream', $body); close $client; }