Advanced Code Golf - Schreiben Sie einen kleinen HTTP-Server

39

Ihre Herausforderung besteht darin, einen Code-Golf-HTTP-Server zu schreiben, der GET-Anforderungen akzeptiert. Es muss natürlich nicht voll funktionsfähig sein, aber es muss Dateien aus einem Verzeichnis bereitstellen.

Regeln:

  • Der HTTP-Server muss den TCP-Port 36895 (0x901F) überwachen
  • Es muss Dateien von einem /var/www* NIX-System (z. B. Linux) oder C:\hgolfunter Windows bereitstellen .
  • Sie können alle eingehenden HTTP-Header außer dem GETselbst ignorieren .
  • Wenn die HTTP-Methode nicht GET ist, müssen Sie den Statuscode "405 Not Supported" und den Body "405 Not Supported" zurücksenden.
  • Wenn die Datei nicht vorhanden ist, müssen Sie den Statuscode "404-Datei nicht gefunden" und den Text "404-Datei nicht gefunden" zurücksenden.
  • Wenn die Datei existiert, aber aus irgendeinem Grund nicht gelesen werden konnte, müssen Sie den Statuscode "500 Server Error" und den Body "500 Server Error" zurücksenden.
  • Wenn der Benutzer eine Anforderung /oder ein anderes vorhandenes Stammverzeichnis (z. B. /foo/wo ein Verzeichnis foovorhanden ist /var/www/) stellt, antworten Sie mit einer leeren Seite.
  • Ihre Antwort muss mindestens die Mindestanzahl an Headern enthalten, damit der Inhalt in Firefox 8.0 und Internet Explorer 8.0 angezeigt werden kann
  • Sie müssen mit dem Content-TypeHeader-Set antworten , müssen aber nur die Erweiterungen html => text/htmlund unterstützen txt => text/plain. Für jede andere Dateierweiterung senden Sie application/octet-streamals Inhaltstyp.
  • Ihr Code muss sowohl ASCII- als auch Binärdaten übertragen können, obwohl Sie nicht explizit zwischen beiden unterscheiden müssen.
  • Sie dürfen keine Bibliotheken von Drittanbietern verwenden.
  • Sie dürfen keine eingebauten Klassen oder Features zum Verarbeiten von HTTP-Anforderungen verwenden (z. B. HttpListenerin C #).
  • Wenn Ihr Code aufgrund der von Ihnen verwendeten Socket-APIs nur auf einem bestimmten Betriebssystem funktioniert, geben Sie dies bitte an.

Die Lösungen müssen ein Bild enthalten, das zeigt, wie eine HTML-Seite an einen Browser gesendet wird.

Wenn Sie Fragen haben, können Sie diese gerne stellen! :)

Polynom
quelle
3
Dies sollte eine große Einschränkung beinhalten: Setzen Sie keine dieser Lösungen einer öffentlichen Netzwerkschnittstelle aus! Da das Ziel eher Codegolf als Sicherheit ist, sind sie gefährlich unsicher. (ZB erwarte ich, dass sie alle ..den Pfad erlauben würden, um aus dem definierten Dokumentenstamm auszubrechen).
Peter Taylor
8
Jemand erfindet immer einen besseren Narren.
Peter Taylor
21
@PeterTaylor Also sollte ich aufhören, meine Lösung für die Bereitstellung meines Blogs zu verwenden? : -O
Gareth
ist das httpModul in Node.js in Ordnung?
Markasoftware
@Markasoftware Sie dürfen keine eingebauten Klassen oder Features zur Verarbeitung von HTTP-Anforderungen verwenden (z. B. HttpListener in C #)
nyuszika7h

Antworten:

13

Ruby, 383 Zeichen

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

Die Umstrukturierung des Codes, um die wesentliche Logik in eine Zeile zu packen, wodurch der Code kürzer wird. Trotzdem hoffe ich, dass diese Version ein bisschen weiter entwickelt werden kann.

Bildbeschreibung hier eingeben

Hinweis: Ich habe den "500 Server Error" noch nicht implementiert, weil ich keine geeignete Struktur gefunden habe (möglicherweise wurde alles in begin / rescue / end gepackt, aber das Lesen der Datei erfolgt, nachdem der Header gesendet wurde).

Howard
quelle
Hast du einen Screenshot davon in Aktion? :)
Polynom
@Polynomial Screenshot hinzugefügt.
Howard
8
Ohne 500 Server Errores passt nicht Anforderungen, so sollte es nicht akzeptiert werden.
Hynek-Pichi-Vychodil
26

Bash + Netcat: 354 Zeichen

  • Es werden zwei Skriptdateien verwendet.
  • Keine URL-Dekodierung, daher werden Dateinamen mit Leerzeichen und Sonderzeichen nicht unterstützt.
  • Single-Threaded, daher können gleichzeitige Anforderungen fehlschlagen.
  • Getestet unter Linux, funktioniert gut mit Firefox und Opera.

webserver.sh (33 Zeichen):

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

w (321 Zeichen):

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;}

Probelauf:

bash-4.2$ ./webserver.sh 

Beispielbesuch (Firefox 19.0):

Bildbeschreibung hier eingeben

Mann bei der Arbeit
quelle
Netcat - es wird alles tun!
Mr. Llama
Neugierig ... wenn ich es versuche, ncwird mir mitgeteilt, dass die Option -e nicht unterstützt wird. Irgendwelche Ideen?
Tomsmeding
Sie haben wahrscheinlich ncvon BusyBox. Ich benutze GNUnetcat .
Manatwork
1
Was ist text/plain?
Ugoren
Was meinst du mit @ugoren? Dateien mit der Erweiterung .txt und Fehlerseiten werden beide als Text / Plain angezeigt. Sehen Sie sich hier einige Tests an: pastebin.com/ErNtvQzx (Ignorieren Sie die Lesefehler, sie sind die üblichen Reaktionen des Kunden netcat.)
manatwork
13

Haskell, 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

Dateien werden träge gestreamt, so dass das Servieren großer Dateien nicht viel Speicherplatz beansprucht. Dies bedeutet jedoch auch, dass nur Fehler beim Start der Übertragung (z. B. Berechtigungsfehler) zu einem Fehler führen 500 Server Error.

Bereitstellen einer HTML-Seite

Getestet unter Ubuntu 10.10. Kann durch Ändern von /var/wwwin C:/hgolfund Ändern main=von in Windows portiert werden main=withSocketsDo$, da die Sockets-Bibliothek unter Windows eine explizite Initialisierung erfordert.

Ungolfed-Version:

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"
Hammar
quelle
12

Python 2, 525 510 493 (Biegen der Regeln 483) Zeichen

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()

Ich gab an, dass ich nur 483 Zeichen benötige, wenn ich die Regeln verbiege, weil das letzte ;c.close()weggelassen werden könnte. Dies liegt daran, dass der Socket in dem Moment, in dem der nächste Client akzeptiert wird, sowieso geschlossen wird. Dies wird natürlich die Wartezeit etwas verlängern (Firefox zeigt die Seite nur an, wenn der nächste Client eine Verbindung herstellt, Chrome zeigt sie vorher an, lädt sie jedoch weiter), aber nach den Regeln muss ich nicht sofort auf die Anfrage antworten, sondern nur auf tun Sie dies irgendwann .

Ich bin nicht sicher, ob dies unter Unixes funktioniert, da ich send anstelle von sendall verwendet habe und send nicht garantiert, dass tatsächlich alles gesendet wird, was gespeist wird. Es funktioniert unter Windows.

Der Screenshot, der nichts beweist, da nicht erkennbar ist, ob ich ihn mit Apache oder meinem eigenen Server erstellt habe

cemper93
quelle
1
Sie verbiegen die Regeln auch, indem Sie das aktuelle Verzeichnis anstelle von C: \ hgolf (8 Zeichen) auf Port 80 anstelle von 36895 (3 Zeichen) bereitstellen.
Manatwork
7

Groovy, 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()}

Bildbeschreibung hier eingeben

Der Bild-Download wurde hinzugefügt, um zu zeigen, dass Binärdateien ordnungsgemäß funktionieren. Zunächst gab es einige Probleme mit ihnen.

Kann jemand einen kürzeren Weg vorschlagen, um die Eingabe zu lesen?

Armand
quelle
5

Erlang (escript) 575

Sehr schmutziges Erlang-Eskript. Am Anfang der Datei muss eine Leerzeile stehen, damit sie richtig funktioniert:

$ 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]).

Wie läuft man?

$ escript hgolf.erl

Bildschirmfoto

Bearbeiten:

Ich habe 20 Zeichen ausgepresst. caseist überraschend kürzer als function mit nur einem Argument und drei Sätzen.

Hynek-Pichi-Vychodil
quelle
Übrigens könnten Sie das Ding auch in dieser Frage posten ( codegolf.stackexchange.com/questions/31647 )
masterX244 22.06.14
4

VB.NET, 7203

Server läuft

Sie können jeden Port und jedes Basisverzeichnis mit --portund definieren --base.

Nein, das ist keine wirkliche Golflösung. Aber VB.NET zu sein, macht sowieso keinen Sinn. Auf der positiven Seite hat dieser viel mehr Funktionen.

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

Ich habe sogar beschlossen, es auf GitHub zu stellen :) https://github.com/minitech/DevServ

Ry-
quelle
2
Haha, ja, VB.NET ist nicht gerade die Sprache der Wahl für Golfer. Trotzdem schön.
Polynom
VB.NET kann golfen werden; Es gibt einige Tricks, die Sie anwenden können.
Joey
@ Joey: Ja, aber ich kann auf keinen Fall mit den aktuellen Antworten mithalten. Ich würde die Kommentare zuerst
herausnehmen
@minitech Ich habe Ihre Antwort ein wenig golfen und gepostet ( codegolf.stackexchange.com/a/21757/15022 ), und wenn Sie möchten, kopieren Sie den Code daraus und ich werde meine Antwort löschen.
Zahnbürste
@minitech In Zeile 46 des Codes von Github haben Sie For i As Integer = 0 To args.Length - 2. Wenn Sie Step 2das Ende dieser Zeile ergänzen , erhöhen Sie den Zähler um zwei statt um 1.
Zahnbürste
4

C # (869)

Es funktionierte

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){}}}}}

Ungolfed

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.
                }
            }
        }
    }
}
ICR
quelle
3

Node.js - 636

Nur unter Linux getestet, funktioniert nicht unter 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)

Bildschirmfoto

nyuszika7h
quelle
2

Scala, 653 Zeichen

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}}

Ein Screenshot davon läuft auf meinem MacBook:

Bildschirmfoto

Nicht großartig, aber ich werde versuchen, es ein bisschen zu zerdrücken, wenn ich etwas später Zeit habe.

Gareth
quelle
Hast du einen Screenshot davon in Aktion?
Polynom
@Polynomial Ja, sorry, war etwas in Eile, irgendwohin früher zu gehen.
Gareth
2

Python 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()

Bildbeschreibung hier eingeben

Tuomas Laakkonen
quelle
afaik Sie können 1-Leerzeichen Einrückung (anstatt 4) verwenden, um 15 Zeichen zu speichern
James Vickery
1

VB.Net (3504 Zeichen):

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

Aus der Antwort von @ minitech.

Zahnbürste
quelle
1

Lua 5.1 in 435 434 Bytes

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

... und der Beweis ...

thenumbernine
quelle