โค้ดกอล์ฟขั้นสูง - เขียนเซิร์ฟเวอร์ HTTP ขนาดเล็ก


39

ความท้าทายของคุณคือการเขียนเซิร์ฟเวอร์ HTTP code-golf ที่รับคำขอ 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
สิ่งนี้ควรรวมคำเตือนใหญ่ ๆ : อย่าเปิดเผยโซลูชันใด ๆ เหล่านี้ต่ออินเตอร์เฟสเครือข่ายสาธารณะ! เนื่องจากจุดมุ่งหมายคือการเขียนโค้ดกอล์ฟมากกว่าการรักษาความปลอดภัยพวกเขาจะไม่ปลอดภัยอย่างอันตราย (เช่นฉันคาดหวังว่าพวกเขาทุกคนจะอนุญาต..ในเส้นทางเป็นวิธีการแยกออกจากรากเอกสารที่กำหนดไว้)
Peter Taylor

8
บางคนประดิษฐ์สิ่งที่ดีกว่าเสมอ
Peter Taylor

21
@PeterTaylor ดังนั้นฉันควรหยุดใช้โซลูชันเพื่อแสดงบล็อกของฉันหรือไม่ : -O
Gareth

เป็นhttpโมดูลใน Node.js ตกลง
markasoftware

@Markasoftware คุณไม่สามารถใช้คลาสหรือฟีเจอร์ที่สร้างขึ้นเพื่อประมวลผลคำขอ HTTP (เช่น HttpListener ใน C #)
nyuszika7h

คำตอบ:


13

Ruby, 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 เพิ่มภาพหน้าจอ
Howard

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 - มันจะทำอะไรก็ได้!
Mr. Llama

อยากรู้อยากเห็น ... ทุกครั้งที่ฉันลองทำสิ่งนี้ncจะบอกฉันว่าไม่รองรับตัวเลือก -e ความคิดใด ๆ
tomsmeding

คุณอาจมีncจาก BusyBox ผมใช้GNU netcat
จัดการ

1
เกี่ยวกับtext/plainอะไร
ugoren

คุณหมายถึง @ugoren? ไฟล์ที่มี. txt extension และหน้าข้อผิดพลาดจะทำหน้าที่เป็น text / plain ดูที่นี่การทดสอบบางอย่าง: pastebin.com/ErNtvQzx (ละเว้นข้อผิดพลาดในการอ่านพวกเขาเป็นปฏิกิริยาปกติของลูกค้าnetcat)
จัดการ

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

ไฟล์สตรีมอย่างเฉื่อยชาเพื่อให้บริการไฟล์ขนาดใหญ่ไม่ได้ใช้หน่วยความจำมาก แต่นี้ยังหมายความว่าข้อผิดพลาดเพียงจุดเริ่มต้นของการส่งผ่าน (เช่นข้อผิดพลาดที่ได้รับอนุญาต) 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

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

ป้อนคำอธิบายรูปภาพที่นี่

เพิ่มการดาวน์โหลดรูปภาพเพื่อแสดงไฟล์ไบนารีทำงานอย่างถูกต้อง - มีปัญหาเล็กน้อยสำหรับพวกเขาที่จะเริ่มต้นด้วย

ทุกคนสามารถแนะนำวิธีที่สั้นกว่าในการอ่านอินพุตได้หรือไม่


5

Erlang (escript) 575

eslang Erlang สกปรกมาก ต้องมีหนึ่งบรรทัดว่างที่จุดเริ่มต้นของไฟล์เพื่อให้ทำงานได้ดี:

$ 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

@ โจอี้: ใช่ แต่ไม่มีทางที่ฉันจะสามารถแข่งขันกับคำตอบปัจจุบันได้ ฉันจะแสดงความคิดเห็นก่อน :)
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){}}}}}

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

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 ใช่ขอโทษเป็นบิตรีบเร่งที่จะไปที่ไหนสักแห่งก่อนหน้านี้
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()

ป้อนคำอธิบายรูปภาพที่นี่


afaik คุณสามารถใช้การเว้นวรรค 1 ครั้ง (แทน 4) เพื่อบันทึก 15 ตัวอักษร
James Vickery

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

... และหลักฐาน ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.