Advanced Code Golf - Напишіть невеликий сервер HTTP


39

Ваше завдання - написати HTTP-сервер з кодовим гольфом, який приймає GET-запити. Очевидно, це не повинно бути повністю представленим, але він повинен обслуговувати файли з каталогу.

Правила:

  • Сервер HTTP повинен слухати на TCP-порту 36895 (0x901F)
  • Він повинен обслуговувати файли з /var/www* NIX-систем (наприклад, Linux) або C:\hgolfз Windows.
  • Ви можете ігнорувати всі вхідні заголовки HTTP, окрім GETсамого.
  • Якщо метод HTTP не GET, ви повинні надіслати назад код статусу "405 не підтримується" та тіло "405 не підтримується".
  • Якщо файл не існує, ви повинні надіслати назад код статусу "Файл 404 не знайдено" та тіло "404 Файл не знайдено".
  • Якщо файл існує, але його не вдалося прочитати з якихось причин, потрібно надіслати назад код статусу "500 Помилка сервера" та тіло "500 Помилка сервера".
  • Якщо користувач запитує /або будь-який інший існуючий корінь каталогів (наприклад, /foo/там, де fooіснує каталог /var/www/), дайте відповідь на порожній сторінці.
  • Ваша відповідь повинна містити принаймні мінімальні заголовки, щоб вміст відображався на Firefox 8.0 та Internet Explorer 8.0
  • Ви повинні відповісти Content-Typeнабором заголовків, але вам потрібно лише підтримувати розширення html => text/htmlта txt => text/plain. Для будь-якого іншого розширення файлу Надіслати application/octet-streamяк тип вмісту.
  • Ваш код повинен мати можливість передавати як ASCII, так і двійкові дані, хоча явно не потрібно їх розрізняти.
  • Ви не можете використовувати сторонні бібліотеки.
  • Ви не можете використовувати вбудовані класи або функції, призначені для обробки запитів HTTP (наприклад, HttpListenerу C #)
  • Якщо ваш код буде працювати лише в певній ОС завдяки API-сокету, який ви використовували, будь ласка, вкажіть це.

Рішення повинні містити зображення, на яких воно відображає браузерну сторінку HTML.

Якщо у вас виникли запитання, будь ласка, не соромтесь запитувати! :)


3
Сюди слід віднести великий застереження: не піддавайте жодне з цих рішень інтерфейсу загальнодоступної мережі! Оскільки мета - кодовий гольф, а не безпека, вони будуть небезпечно небезпечними. (Напр., Я очікую, що всі вони дозволять ..пройти шлях як спосіб виривання визначеного кореня документа).
Пітер Тейлор

8
Хтось завжди придумує кращого дурня.
Пітер Тейлор

21
@PeterTaylor Отже, я повинен перестати використовувати своє рішення для обслуговування свого блогу? : -О
Гарет

є httpмодуль в Node.js ок?
markasoftware

@Markasoftware Ви не можете використовувати вбудовані класи або функції, призначені для обробки запитів HTTP (наприклад, HttpListener в C #)
nyuszika7h

Відповіді:


13

Рубі, 383 символи

require 'socket'
v=TCPServer.new 36895
while c=v.accept
h="200 OK";m="";t=""
(c.gets=~/GET (\S*)/)?File.directory?(s='C:/hgolf'+$1)?0:File.exist?(s)?(h+="\r\nContent-Type: "+(s=~/\.txt$/?"text/plain":s=~/\.html$/?"text/html":"application/octet-stream");t="IO.copy_stream(s,c)"):(h=m="404 File Not Found"):(h=m="405 Not Supported")
c.puts "HTTP/1.1 #{h}\r\n\r\n"+m
eval(t)
c.close
end

Перебудова коду для упаковки суттєвої логіки в один рядок, що робить код коротшим. Тим не менш, я сподіваюсь, що цю версію можна буде пограти трохи далі.

введіть тут опис зображення

Примітка: я ще не реалізував "500 помилок сервера", тому що я не міг знайти відповідну структуру (можливо, упаковка всього в початок / порятунок / закінчення, але зчитування файлу виконується після надсилання заголовка).


Чи є у вас знімок екрана в дії? :)
Поліном

@Polynomial Додано скріншот.
Говард

8
Без 500 Server Errorцього він не відповідає вимогам, тому його не слід приймати.
Hynek -Pichi- Vychodil

26

Bash + netcat: 354 символи

  • Використовуються два файли сценаріїв.
  • Немає декодування URL-адреси, тому імена файлів із пробілами та спеціальними символами не підтримуються.
  • Один потік, тому одночасні запити можуть не працювати.
  • Тестований на Linux, чудово працює з Firefox та Opera.

webserver.sh (33 символи):

while :;do nc -lp36895 -e./w;done

w (321 символ):

read m p v
p=/var/www$p
t=text/plain
[[ -r $p ]]||s='500 Server Error'
[[ -e $p ]]||s='404 Not Found'
[[ $m != GET ]]&&s='405 Not Supported'
[[ $s||-d $p ]]||case ${p##*.} in
txt);;html)t=text/html;;*)t=application/octet-stream;;esac
echo "$v ${s-200 Ok}
Content-Type:$t
"
[[ $s ]]&&echo $s||{ [[ -d $p ]]&&echo||cat $p;}

Проба зразка:

bash-4.2$ ./webserver.sh 

Зразок відвідування (Firefox 19.0):

введіть тут опис зображення


Netcat - це зробить все, що завгодно!
Містер Лама

Цікаво ... щоразу, коли я спробую це, ncмені кажуть, що опція -e не підтримується. Якісь ідеї?
tomsmeding

Ви, мабуть, є ncз BusyBox. Я використовую GNUnetcat .
манатура

1
Про що text/plain?
ugoren

Що ти маєш на увазі @ugoren? Файли з розширенням .txt та сторінки помилок служать як текст / звичайна. Ознайомтеся з деякими тестами: pastebin.com/ErNtvQzx (Ігноруйте помилки читання, вони є звичайними реакціями клієнта netcat.)
manatwork

13

Хаскелл, 636

import Data.List;import Network;import System.Directory;import System.IO
main=listenOn(PortNumber 36895)>>=l;l s=(n s`catch`\_->u())>>l s
n s=do(h,_,_)<-accept s;z h>>=q.words>>=hPutStr h;hClose h
q("GET":p:_)=d$"/var/www"++p;q _=c"405 Not Supported"
d p=doesFileExist p>>=f p;e x|x=u$s""""|1<3=c"404 File Not Found"
f p x|x=t p`catch`\_e->c"500 Server Error"|1<3=doesDirectoryExist p>>=e
t p=fmap(s$"\nContent-Type: "++g p)$z=<<openBinaryFile p ReadMode
r s h b=["HTTP/1.0 ",s,h,"\n\n",b]>>=id
g p|b".html"p="text/html"|b".txt"p="text/plain"|1<3="application/octet-stream"
s=r"200 OK";c s=u$r s[]s;u=return;b=isSuffixOf;z=hGetContents

Файли ледве передаються, тому для обслуговування великих файлів не використовується багато пам'яті, але це також означає, що лише помилки на початку передачі (наприклад, помилки дозволу) призводять до появи a 500 Server Error.

Подання HTML-сторінки

Тестовано на Ubuntu 10.10. Може бути перенесена на Windows , шляхом зміни /var/wwwдо C:/hgolfі зміни main=до main=withSocketsDo$, так як бібліотека сокетів на Windows , вимагає чіткого ініціалізацію.

Негольована версія:

import Data.List
import Network
import System.Directory
import System.IO

root = "/var/www"

main = do
    s <- listenOn (PortNumber 36895)
    loop s

loop s = (next s `catch` \e -> return ()) >> loop s

next s = do
    (h, _, _) <- accept s
    hGetContents h >>= serve >>= hPutStr h
    hClose h

serve req =
    case words req of
        ("GET" : path : _) -> deliver (root ++ path)
        _ -> complain "405 Not Supported"

deliver path = do
    isFile <- doesFileExist path
    if isFile
        then transfer path `catch` (\e -> complain "500 Server Error")
        else do isDir <- doesDirectoryExist path
                if isDir
                    then return $ response "200 OK" [] ""
                    else complain "404 File Not Found"

transfer path = do
   body <- hGetContents =<< openBinaryFile path ReadMode
   return $ response "200 OK" ["Content-Type: " ++ guessType path] body

response status headers body =
  concat [unlines $ ("HTTP/1.0 " ++ status) : headers, "\n", body]

complain status = return $ response status [] status

guessType path
    | ".html" `isSuffixOf` path = "text/html"
    | ".txt"  `isSuffixOf` path = "text/plain"
    | otherwise                 = "application/octet-stream"

12

Python 2, 525 510 493 (згинаючи правила 483) символів

from socket import*
from os.path import*
a=socket(2,1)
a.bind(("",80))
a.listen(5)
while 1:
 c,_=a.accept();i=c.recv(512).split("\n")[0].split();z=i[1][1:];m=i[2]+(i[0]!="GET"and"405 Not Supported\n\n"*2or exists(z)-1and"404 File Not Found\n\n"*2or"200 OK\n")
 if(len(m)>16)+isdir(z)<1:
    try:f=open(z,"rb");m+="Content-Type: "+{".txt":"text/plain","html":"text/html"}.get(z[-4:],"application/octet-stream")+"\n\n"+f.read()
    except:m=i[2]+"500 Server Error\n\n"*2
 c.send(m);c.close()

Я заявив, що мені потрібно лише 483 символи, якщо я згорнув правила, тому що останню ;c.close()можна було пропустити. Це тому, що з моменту отримання наступного клієнта сокет все одно закривається. Це, звичайно, дещо збільшить час очікування (Firefox відобразить сторінку лише тоді, коли підключиться наступний клієнт, Chrome покаже її раніше, але продовжить завантажуватись), але правила не вимагають від мене негайного відповіді на запит , лише до зробіть це в якийсь момент .

Я не впевнений, чи буде це працювати на Unixes, тому що я використовував функцію send замість sendall і send не гарантує фактично надсилати все, що вона подається. Це працює в Windows.

Скріншот, який нічого не підтверджує, тому що немає способу сказати, чи використовував я Apache чи власний сервер для його створення


1
Ви також згинаєте правила, подаючи поточний каталог замість C: \ hgolf (8 символів) на порт 80 замість 36895 (3 символи).
манатура

7

Гроовий, 507 500 489 485

for(ServerSocket s=new ServerSocket(36895);;){t=s.accept()
o=t.outputStream
o<<"HTTP/1.1 "
e='\r\n'*2
d={o<<"$it$e$it"}
p=t.inputStream.newReader().readLine().split()
if(p[0]!='GET')d'405 Not Supported'else{f=new File('/var/www/',p[1])
if(!f.exists())d'404 File Not Found'else if(f.isFile()){x=f.name=~/\.(.*)/
o<<"200 OK\r\nContent-Type: ${[html:'text/html',txt:'text/plain'][!x?:x[0][1]]?:'application/octet-stream'}$e"
try{o.bytes=f.bytes}catch(t){d"500 Server Error"}}}
o.close()}

введіть тут опис зображення

Додано завантаження зображення, щоб показати, що двійкові файли працюють належним чином - для початку виникло кілька проблем.

Чи може хтось запропонувати коротший спосіб прочитати вхід?


5

Ерланг (ескрипт) 575

Дуже брудний ескрип Ерланга. На початку файлу повинен бути один порожній рядок, щоб добре працювати:

$ cat hgolf.erl

main(_)->{ok,L}=gen_tcp:listen(36895,[]),s(L).
s(L)->{ok,S}=gen_tcp:accept(L),receive{tcp,S,"GET "++R}->[F|_]=string:tokens("/var/www"++R," "),case case file:read_file_info(F)of{ok,{_,_,regular,read,_,_,_,_,_,_,_,_,_,_}}->a;{ok,_}->"500 Server Error";_->"404 File Not Found"end of a->h(S,"200 OK\r\nContent-Type: "++case lists:reverse(F)of"lmth."++_->"text/html";"txt."++_->"text/plain";_->"application/octet-stream"end,[]),file:sendfile(F,S);E->h(S,E,E)end;_->E="405 Not Supported",h(S,E,E)end,gen_tcp:close(S),s(L).
h(S,H,B)->gen_tcp:send(S,["HTTP/1.1 ",H,"\r\n\r\n",B]).

Як бігати

$ escript hgolf.erl

Знімок екрана

Редагувати:

Я вичавив 20 символів. caseнапрочуд коротша за функцію з навіть одним аргументом та трьома умовами.


btw Ви також можете написати це питання і в цьому питанні ( codegolf.stackexchange.com/questions/31647 )
masterX244

4

VB.NET, 7203

Зображення запущеного сервера

Ви можете визначити будь-який порт і будь-який базовий каталог, використовуючи --portі --base, відповідно.

Ні, це насправді не рішення для гольфу. Але будучи VB.NET, все одно немає сенсу. З іншого боку, цей має ще багато можливостей.

Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Text.RegularExpressions

Public Module Server
    Private Const READ_BUFFER_SIZE As Integer = 1024

#Region "Content-Type Identification"
    Private ReadOnly ContentTypes As New Dictionary(Of String, String) From {
            {".htm", "text/html"},
            {".html", "text/html"},
            {".js", "text/javascript"},
            {".css", "text/css"},
            {".png", "image/png"},
            {".jpg", "image/jpeg"},
            {".jpeg", "image/jpeg"},
            {".gif", "image/gif"}
        } 'Feel free to add more.

    ''' <summary>
    ''' Retrieves the Content-Type of the specified file.
    ''' </summary>
    ''' <param name="filepath">The file for which to retrieve the Content-Type.</param>
    Private Function GetContentType(ByVal filepath As String) As String
        Dim ext As String = IO.Path.GetExtension(filepath)

        If ContentTypes.ContainsKey(ext) Then _
            Return ContentTypes(ext)

        Return "text/plain"
    End Function
#End Region

#Region "Server Main()"
    Public Sub Main(ByVal args() As String)
        Try
            'Get a dictionary of options passed:
            Dim options As New Dictionary(Of String, String) From {
                {"--port", "8080"},
                {"--address", "127.0.0.1"},
                {"--base", String.Empty}
            }

            For i As Integer = 0 To args.Length - 2
                If args(i).StartsWith("-") AndAlso options.ContainsKey(args(i)) Then _
                    options(args(i)) = args(i + 1)
            Next

            'Get the base directory:
            Dim basedir As String = Path.Combine(My.Computer.FileSystem.CurrentDirectory, options("--base"))

            'Start listening:
            Dim s As New TcpListener(IPAddress.Parse(options("--address")), Integer.Parse(options("--port"))) 'Can be changed.
            Dim client As TcpClient

            s.Start()

            Do
                'Wait for the next TCP client, and accept the connection:
                client = s.AcceptTcpClient()

                'Read the data being sent to the server:
                Dim ns As NetworkStream = client.GetStream()
                Dim sendingData As New Text.StringBuilder()
                Dim rdata(READ_BUFFER_SIZE - 1) As Byte
                Dim read As Integer

                Do
                    read = ns.Read(rdata, 0, READ_BUFFER_SIZE)
                    sendingData.Append(Encoding.UTF8.GetString(rdata, 0, read))
                Loop While read = READ_BUFFER_SIZE

                'Get the method and requested file:
#If Not Debug Then
                Try
#End If
                If sendingData.Length > 0 Then
                    Dim data As String = sendingData.ToString()

                    Dim headers() As String = data.Split({ControlChars.Cr, ControlChars.Lf}, StringSplitOptions.RemoveEmptyEntries)
                    Dim basicRequestInfo() As String = headers(0).Split(" "c)
                    Dim method As String = basicRequestInfo(0)
                    Dim filepath As String = basicRequestInfo(1).Substring(1)
                    Dim actualFilepath As String = Path.Combine(basedir, Uri.UnescapeDataString(Regex.Replace(filepath, "\?.*$", "")).TrimStart("/"c).Replace("/"c, "\"c))
                    Dim httpVersion As String = basicRequestInfo(2)

                    'Set up the response:
                    Dim responseHeaders As New Dictionary(Of String, String)

                    Dim statusCode As String = "200"
                    Dim statusReason As String = "OK"
                    Dim responseContent() As Byte = {}

                    'Check the HTTP version - we only support HTTP/1.0 and HTTP/1.1:
                    If httpVersion <> "HTTP/1.0" AndAlso httpVersion <> "HTTP/1.1" Then
                        statusCode = "505"
                        statusReason = "HTTP Version Not Supported"
                        responseContent = Encoding.UTF8.GetBytes("505 HTTP Version Not Supported")
                    Else

                        'Attempt to check if the requested path is a directory; if so, we'll add index.html to it:
                        Try
                            If filepath = String.Empty OrElse filepath = "/" Then
                                actualFilepath = Path.Combine(basedir, "index.html")
                                filepath = "/"
                            ElseIf Directory.Exists(actualFilepath) Then
                                actualFilepath = Path.Combine(actualFilepath, "index.html")
                            End If
                        Catch
                            'Ignore the error; it will appear once again when we try to read the file.
                        End Try

                        'Check the method - we only support GET and HEAD:
                        If method = "GET" Then
                            'Make sure nobody's trying to hack the system by requesting ../whatever or an absolute path:
                            If filepath.Contains("..") Then
                                statusCode = "403"
                                statusReason = "Forbidden"
                                responseContent = Encoding.UTF8.GetBytes("403 Forbidden")
                                Console.WriteLine("Access to {0} was forbidden.", filepath)
                            ElseIf Not File.Exists(actualFilepath) Then
                                statusCode = "404"
                                statusReason = "Not Found"
                                responseContent = Encoding.UTF8.GetBytes("404 Not Found")
                                Console.WriteLine("A request for file {0} resulted in a 404 Not Found. The actual path was {1}.", filepath, actualFilepath)
                            Else
                                Try
                                    'Read the requested file:
                                    responseContent = File.ReadAllBytes(actualFilepath)

                                    'Get the requested file's length:
                                    responseHeaders.Add("Content-Length", responseContent.Length.ToString())

                                    'And get its content type too:
                                    responseHeaders.Add("Content-Type", GetContentType(actualFilepath))
                                Catch
                                    'Couldn't get the file's information - assume forbidden.
                                    statusCode = "403"
                                    statusReason = "Forbidden"
                                    responseContent = Encoding.UTF8.GetBytes("403 Forbidden")
                                End Try
                            End If
                        ElseIf method = "HEAD" Then
                            'Make sure nobody's trying to hack the system by requesting ../whatever or an absolute path:
                            If filepath.Contains("..") Then
                                statusCode = "403"
                                statusReason = "Forbidden"
                                responseContent = Encoding.UTF8.GetBytes("403 Forbidden")
                                Console.WriteLine("Access to {0} was forbidden.", filepath)
                            ElseIf Not File.Exists(actualFilepath) Then
                                statusCode = "404"
                                statusReason = "Not Found"
                                responseContent = Encoding.UTF8.GetBytes("404 Not Found")
                                Console.WriteLine("A request for file {0} resulted in a 404 Not Found.", filepath)
                            Else
                                Try
                                    'Get the requested file's length:
                                    responseHeaders.Add("Content-Length", New FileInfo(actualFilepath).Length.ToString())

                                    'And get its content type too:
                                    responseHeaders.Add("Content-Type", GetContentType(actualFilepath))
                                Catch
                                    'Couldn't get the file's information - assume forbidden.
                                    statusCode = "403"
                                    statusReason = "Forbidden"
                                    responseContent = Encoding.UTF8.GetBytes("403 Forbidden")
                                End Try
                            End If
                        Else
                            'Unknown method:
                            statusCode = "405"
                            statusReason = "Method Not Allowed"
                        End If

                        'Prepare the response:
                        Dim response As New List(Of Byte)

                        'Prepare the response's HTTP version and status:
                        response.AddRange(Encoding.UTF8.GetBytes("HTTP/1.1 " & statusCode & statusReason & ControlChars.CrLf))

                        'Prepare the response's headers:
                        Dim combinedResponseHeaders As New List(Of String)
                        For Each header As KeyValuePair(Of String, String) In responseHeaders
                            combinedResponseHeaders.Add(header.Key & ": " & header.Value)
                        Next
                        response.AddRange(Encoding.UTF8.GetBytes(String.Join(ControlChars.CrLf, combinedResponseHeaders.ToArray())))

                        'Prepare the response's content:
                        response.Add(13)
                        response.Add(10)
                        response.Add(13)
                        response.Add(10)
                        response.AddRange(responseContent)

                        'Finally, write the response:
                        ns.Write(response.ToArray(), 0, response.Count)
                    End If
                End If
#If Not Debug Then
                Catch ex As Exception
                    Console.WriteLine("Serious error while processing request:")
                    Console.WriteLine(ex.ToString())
                    Dim errorResponse() As Byte = Encoding.UTF8.GetBytes("HTTP/1.1 500 Internal Server Error" & ControlChars.CrLf & ControlChars.CrLf & "500 Internal Server Error")
                    ns.Write(errorResponse, 0, errorResponse.Length)
                End Try
#End If

                'And at last, close the connection:
                client.Close()
            Loop
        Catch ex As SocketException
            Console.WriteLine("SocketException occurred. Is the socket already in use?")
            Console.ReadKey(True)
        End Try
    End Sub
#End Region

End Module

Я навіть вирішив поставити його на GitHub :) https://github.com/minitech/DevServ


2
Ха-ха, VB.NET - це не саме мова вибору гольфіста. Досі приємно хоч.
Поліном

VB.NET може бути в гольф; є досить багато хитрощів, якими можна скористатися.
Joey

@Joey: Так, але я не можу конкурувати з поточними відповідями. Я б взяв коментарі спочатку :)
Ry-

@minitech Я трохи пограв у вашу відповідь і опублікував її ( codegolf.stackexchange.com/a/21757/15022 ), і, якщо ви хочете, скопіюйте код з цього тексту, і я видалю свою відповідь.
Зубна щітка

@minitech У рядку 46 коду на Github у вас є For i As Integer = 0 To args.Length - 2. Якщо додати Step 2до кінця цього рядка, ви збільшите лічильник на два замість 1.
Зубна щітка

4

C # (869)

Це спрацювало

using E=System.Text.Encoding;using System.IO;class C{static void Main(){var l=new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(16777343,36895));l.Start();while(0<1){using(var c=l.AcceptTcpClient()){try{string v="200 OK",r="",t="text/plain",p;var s=c.GetStream();var b=new byte[c.ReceiveBufferSize];s.Read(b,0,b.Length);var h=E.UTF8.GetString(b).Split();b=null;try{if(h[0]=="GET"){p="/var/www"+h[1];if(File.Exists(p)){b=File.ReadAllBytes(p);t=p.EndsWith(".txt")?t:p.EndsWith(".html")?"text/html":"application/octet-stream";}else if(!Directory.Exists(p)){v=r="404 Not Found";}}else{v=r="405 Not Supported";}}catch(IOException){v=r="500 Server Error";}b=b??E.UTF8.GetBytes(r);var m=E.UTF8.GetBytes("HTTP/1.1 "+v+"\r\nContent-Type:"+t+";charset=utf-8\r\nContent-Length:"+b.Length+"\r\n\r\n");s.Write(m,0,m.Length);s.Write(b,0,b.Length);}catch(IOException){}}}}}

Безумовно

using System.Text;
using System.IO;

class C {
    static void Main() {
        var listener = new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(16777343,36895));
        listener.Start();

        while (true){
            using (var client = listener.AcceptTcpClient()) {
                try {
                    string responseCode = "200 OK", responseBody = "", contentType = "text/plain", path;
                    var stream = client.GetStream();

                    var bytes = new byte[client.ReceiveBufferSize];
                    stream.Read(bytes,0,bytes.Length);

                    var requestHeaders = Encoding.UTF8.GetString(bytes).Split();
                    bytes = null;

                    try{
                        if(requestHeaders[0] == "GET"){
                            path = "/var/www" + requestHeaders[1];

                            if (File.Exists(path)) {
                                bytes = File.ReadAllBytes(path);
                                contentType = path.EndsWith(".txt") ? contentType : path.EndsWith(".html") ? "text/html" : "application/octet-stream";
                            } else if (!Directory.Exists(path)){
                                responseCode = responseBody = "404 Not Found";
                            }
                        } else {
                            responseCode = responseBody = "405 Not Supported";
                        }
                    } catch(IOException) {
                        responseCode = responseBody = "500 Server Error";
                    }
                    bytes = bytes ?? Encoding.UTF8.GetBytes(responseBody);

                    var responseHeader=Encoding.UTF8.GetBytes("HTTP/1.1 " + responseCode + "\r\nContent-Type:" + contentType + ";charset=utf-8\r\nContent-Length:" + bytes.Length + "\r\n\r\n");
                    stream.Write(responseHeader, 0, responseHeader.Length);
                    stream.Write(bytes, 0, bytes.Length);
                } catch(IOException) {
                    // If a client disconnects in the middle of a request (e.g. by refreshing the browser) an IOException is thrown.
                }
            }
        }
    }
}

3

Node.js - 636

Тільки тестований на Linux, не працює в Windows.

a=require
b=a('fs')
function c(){g+=o+h+i+j+f+f+o+f}
a('net').createServer(function(d){d.on('data',function(e){f='\r\n'
g='HTTP/1.1 '
h=f+'Content-Type: '
i='text/'
j='plain'
if(k=/^GET (\S+)/.exec((e+'').split(f)[0])){k=k[1]
l=k.slice(k.lastIndexOf('.')+1)
m='www'+k
if(b.existsSync(m)){if(b.lstatSync(m).isDirectory()){g+='200 OK'+h+i+j+f}else{try{n=b.readFileSync(m)
g+='200 OK'+h
if(l=='txt')g+=i+j
else if(l=='html')g+=i+l
else g+='application/octet-stream'
g+=f+f+n}catch(_){o='500 Server Error'
c()}}}else{o='404 File Not Found'
c()}}else{o='405 Not Supported'
c()}
d.write(g)
d.end()})
d.on('error',function(){})}).listen(36895)

Знімок екрана


2

Скала, 653 символи

import java.net._
import java.io._
object I extends App{val l=new ServerSocket(36895)
while(true){var (s,e,c,b)=(l.accept,"200 OK","text/html","")
var h=io.Source.fromInputStream(s.getInputStream).getLines.next.split(" ")
if(h(0)!="GET"){e="405 Not Supported"
b=e}else{var f=new File("/var/www"+h(1))
if(!f.isDirectory){if(f.exists){var q=h(1).split("\\.").last
if(q=="txt")c="text/plain"else if(q!="html")c="application/octet-stream"
try b=io.Source.fromFile(f).mkString catch{case _=>e="500 Server Error";b=e}}else{e="404 File Not Found"
b=e}}}
var o=new PrintWriter(s.getOutputStream)
o.print("HTTP/1.1 "+e+"\nContent-Encoding:"+c+"\n\n"+b)
o.close}}

Скріншот екрана, який працює на моєму MacBook:

Знімок екрана

Не чудово, але я буду трохи піти, коли я отримаю трохи пізніше.


Зробив знімок екрана в дії?
Поліном

@ Polynomial Так вибачте, трохи поспішав їхати кудись раніше.
Гарет

2

Пітон 3 - 655

from socket import*
import re
import threading
def x(c):
    u=str(c.recv(1024))
    if not'GET'in u:conn.sendall(t("HTTP/1.1 405 Not Supported"))
    u=re.search('GET [^\s]+ HTTP/1.1',u).group(0).split(" ")[1];u="/index.html" if u == "/" else u;e=u.split(".")[1]
    try:c.sendall(t("HTTP/1.1 200 OK\nContent-Type: "+({'txt':'text/plain','html':'text/html'}[e]if e in'txthtml'else'application/octet-stream')+"\n\n")+open("."+u,'rb').read())
    except:c.sendall(t("HTTP/1.1 404 File Not Found\n\n404 File Not Found"))
t=lambda s: bytes(s,'utf8')
s=socket(AF_INET,SOCK_STREAM)
s.bind(('',36895))
s.listen(10)
while 1:threading.Thread(target=x,args=[s.accept()[0]]).run()

введіть тут опис зображення


afaik, ви можете використовувати відступ 1-пробілу (а не 4), щоб зберегти 15 символів
Джеймс Вікері

1

VB.Net (3504 символи):

Imports System.IO:Imports System.Net:Imports System.Net.Sockets:Imports System.Text:Imports System.Text.RegularExpressions:Module x:Dim s=1024,ct As New Dictionary(Of String,String)From{{".htm","text/html"},{".html","text/html"},{".js","text/javascript"},{".css","text/css"},{".png","image/png"},{".jpg","image/jpeg"},{".jpeg","image/jpeg"},{".gif","image/gif"}}:Function b$(f$):Dim ext$=Path.GetExtension(f$):Return If(ct.ContainsKey(ext$),ct(ext$),"text/plain"):End Function:Sub Main(a$()):Try:Dim z As New Dictionary(Of String,String)From{{"--port","8080"},{"--address","127.0.0.1"},{"--base",""}}:For i As Integer=0 To a.Length-2:If a$(i).StartsWith("-")AndAlso z.ContainsKey(a$(i))Then:z(a$(i))=a$(i+1):Next:Dim b$=Path.Combine(My.Computer.FileSystem.CurrentDirectory,z("--base")),s As New TcpListener(IPAddress.Parse(z("--address")),Integer.Parse(z("--port"))),c As TcpServer:s.Start():Do:c=s.AcceptTcpServer():Dim ns As NetworkStream=c.GetStream():Dim sd As New Text.StringBuilder(),rd(s-1)As Byte,r As Integer:Do:r=ns.Read(rd,0,s):sd.Append(Encoding.UTF8.GetString(rd,0,r)):Loop While r=s:Try:If sd.Length>0 Then:Dim dd$=sd.ToString(),h$()=dd$.Split({ControlChars.Cr,ControlChars.Lf},StringSplitOptions.RemoveEmptyEntries),br$()=h$(0).Split(" "c),mt$=br$(0),f$=br$(1).Substring(1),af$=Path.Combine(b$,Uri.UnescapeDataString(Regex.Replace(f$,"\?.*$","")).TrimStart("/"c).Replace("/"c,"\"c)),hv$=br$(2),rh As New Dictionary(Of String,String),sc$="200",sr$="OK",rc()As Byte={}:If hv$<>"HTTP/1.0"AndAlso hv$<>"HTTP/1.1"Then:sc$="505":sr$="HTTP Version Not Supported":rc=Encoding.UTF8.GetBytes("505"&sr$):Else:Try:If f$=String.Empty OrElse f$="/"Then:af$=Path.Combine(b$,"index.html"):f$="/":ElseIf Directory.Exists(af$)Then:af$=Path.Combine(af$,"index.html"):End If:Catch:End Try:If mt$="GET"Then:If f$.Contains("..")Then:sc$="403":sr$="Forbidden":rc=Encoding.UTF8.GetBytes(sc$&" "&sr$):Console.WriteLine("{0} forbidden.",f$):ElseIf Not File.Exists(af$)Then:sc$="404":sr$="Not Found":rc=Encoding.UTF8.GetBytes(sc$&" "&sr$):Console.WriteLine("{0} resulted in 404 Not Found. Path {1}.",f$,af$):Else:Try:rc=File.ReadAllBytes(af$):rh.Add("Content-Length",rc.Length&""):rh.Add("Content-Type",b$(af$)):Catch:sc$="403":sr$="Forbidden":rc = Encoding.UTF8.GetBytes(sc$&" "&sr$):End Try:End If:ElseIf mt$="HEAD"Then:If f$.Contains("..")Then:sc$="403":sr$="Forbidden":rc=Encoding.UTF8.GetBytes(sc$&" "&sr$):Console.WriteLine("{0} forbidden.",f$):ElseIf Not File.Exists(af$)Then:sc$="404":sr$="Not Found":rc=Encoding.UTF8.GetBytes(sc$&" "&sr$):Console.WriteLine("404 Not Found: {0}",f$):Else:Try:rh.Add("Content-Length",New FileInfo(af$).Length&""):rh.Add("Content-Type",b$(af$)):Catch:sc$="403":sr$="Forbidden":rc = Encoding.UTF8.GetBytes(sc$&" "&sr$):End Try:End If:Else:sc$="405":sr$="Method Not Allowed":End If:Dim rr As New List(Of Byte):rr.AddRange(Encoding.UTF8.GetBytes("HTTP/1.1 "&sc$&sr$&ControlChars.CrLf)):Dim cr As New List(Of String):For Each h As KeyValuePair(Of String,String)In rh:cr.Add(h.Key&": "&h.Value):Next:rr.AddRange(Encoding.UTF8.GetBytes(String.Join(ControlChars.CrLf,cr.ToArray()))):rr.Add(13):rr.Add(10):rr.Add(13):rr.Add(10):rr.AddRange(rc):ns.Write(rr.ToArray(),0,rr.Count):End If:End If:Catch ex As Exception:Console.WriteLine("Error:"):Console.WriteLine(ex.ToString()):Dim e()As Byte=Encoding.UTF8.GetBytes("HTTP/1.1 500 Internal x Error"&ControlChars.CrLf &ControlChars.CrLf &"500 Internal x Error"):ns.Write(e,0,e.Length):End Try:c.Close():Loop:Catch:End Try:End Sub:End Module

Гольф від відповіді @ minitech.


1

Lua 5.1 в 435 434 байт

s=require'socket'.bind('*',36895)while{}do
c=s:accept()u=c:receive'*l':match'GET (.*) HTTP'f,_,e=io.open('/var/www'..u,'rb')d=z
x=z if f then d,_,x=f:read'*a'end
h=u and(x==21 and''or(e==2 and'404 File Not Found'or d
and('200 OK\r\nContent-Type:'..(({txt='text/plain',html='text/html'})[u:match'%.(.-)$']or'application/octet-stream'))or'500 Server Error'))or'405 Not Supported'c:send('HTTP/1.1 '..h..'\r\n\r\n'..(d
or h))c:close()end

... і доказ ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.