Advanced Code Golf - Viết một máy chủ HTTP nhỏ


39

Thách thức của bạn là viết một máy chủ HTTP mã golf chấp nhận các yêu cầu GET. Nó rõ ràng không phải là đầy đủ tính năng, nhưng nó phải phục vụ các tệp từ một thư mục.

Quy tắc:

  • Máy chủ HTTP phải lắng nghe trên cổng TCP 36895 (0x901F)
  • Nó phải phục vụ các tệp từ /var/wwwtrên các hệ thống * NIX (ví dụ Linux) hoặc C:\hgolftrên Windows.
  • Bạn có thể bỏ qua tất cả các tiêu đề HTTP đến ngoại trừ GETchính nó.
  • Nếu phương thức HTTP không NHẬN, bạn phải gửi lại mã trạng thái "405 Không được hỗ trợ" và nội dung "405 Không được hỗ trợ".
  • Nếu tệp không tồn tại, bạn phải gửi lại mã trạng thái "Không tìm thấy tệp 404" và phần thân của "Không tìm thấy tệp 404".
  • Nếu vì lý do nào đó tệp tồn tại nhưng không thể đọc được, bạn phải gửi lại mã trạng thái "500 Lỗi máy chủ" và phần thân của "Lỗi 500 máy chủ".
  • Nếu người dùng yêu cầu /hoặc bất kỳ thư mục gốc nào khác (ví dụ: /foo/nơi thư mục footồn tại /var/www/), hãy trả lời với một trang trống.
  • Phản hồi của bạn phải chứa ít nhất các tiêu đề tối thiểu để cho phép nội dung được hiển thị trên Firefox 8.0 và Internet Explorer 8.0
  • Bạn phải trả lời với bộ Content-Typetiêu đề, nhưng bạn chỉ phải hỗ trợ các tiện ích mở rộng html => text/htmltxt => text/plain. Đối với bất kỳ phần mở rộng tệp nào khác, Gửi application/octet-streamdưới dạng loại nội dung.
  • Mã của bạn phải có khả năng chuyển cả dữ liệu nhị phân và dữ liệu nhị phân, mặc dù bạn không nhất thiết phải phân biệt giữa hai.
  • Bạn không thể sử dụng thư viện của bên thứ 3.
  • Bạn không được sử dụng các lớp hoặc tính năng được xây dựng được thiết kế để xử lý các yêu cầu HTTP (ví dụ: HttpListenertrong C #)
  • Nếu mã của bạn sẽ chỉ hoạt động trên một HĐH cụ thể do API ổ cắm bạn đã sử dụng, vui lòng nêu rõ điều này.

Các giải pháp phải bao gồm một hình ảnh cho thấy nó đang phục vụ trang HTML cho trình duyệt.

Nếu bạn có bất kỳ câu hỏi, xin vui lòng hỏi! :)


3
Điều này nên bao gồm một cảnh báo lớn: không để lộ bất kỳ giải pháp nào trong số này cho giao diện mạng công cộng! Vì mục đích là mã golf chứ không phải an ninh, nên chúng sẽ không an toàn một cách nguy hiểm. (Ví dụ, tôi hy vọng rằng tất cả chúng sẽ cho phép ..trong đường dẫn như một cách thoát ra khỏi tài liệu gốc được xác định).
Peter Taylor

8
Một người luôn luôn phát minh ra một kẻ ngốc tốt hơn.
Peter Taylor

21
@PeterTaylor Vì vậy, tôi nên ngừng sử dụng giải pháp của mình để phục vụ blog của mình? : -O
Gareth

httpmô-đun trong Node.js ok?
markasoftware

@Markasoftware Bạn không được sử dụng các lớp hoặc tính năng được xây dựng được thiết kế để xử lý các yêu cầu HTTP (ví dụ: httpListener trong C #)
nyuszika7h

Câu trả lời:


13

Ruby, 383 ký tự

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

Việc tái cấu trúc mã để đóng gói logic thiết yếu thành một dòng làm cho mã ngắn hơn. Tuy nhiên, tôi hy vọng phiên bản này có thể được chơi gôn thêm một chút nữa.

nhập mô tả hình ảnh ở đây

Lưu ý: Tôi chưa thực hiện "Lỗi máy chủ 500" vì tôi không thể tìm thấy cấu trúc phù hợp (có thể đóng gói mọi thứ vào đầu / cứu / kết thúc nhưng việc đọc tệp được thực hiện sau khi tiêu đề được gửi).


Bạn có một ảnh chụp màn hình của nó trong hành động? :)
Đa thức

@Polynomial Đã thêm ảnh chụp màn hình.
Howard

8
Không có 500 Server Errornó không phù hợp với yêu cầu nên không được chấp nhận.
Hynek -Pichi- Vychodil

26

Bash + netcat: 354 ký tự

  • Hai tập tin script được sử dụng.
  • Không giải mã URL, do đó tên tệp có khoảng trắng và ký tự đặc biệt không được hỗ trợ.
  • Luồng đơn, vì vậy các yêu cầu đồng thời có thể thất bại.
  • Đã thử nghiệm trên Linux, hoạt động độc đáo với Firefox và Opera.

webserver.sh (33 ký tự):

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

w (321 ký tự):

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

Chạy mẫu:

bash-4.2$ ./webserver.sh 

Truy cập mẫu (Firefox 19.0):

nhập mô tả hình ảnh ở đây


Netcat - nó sẽ làm bất cứ điều gì!
Ông Llama

Tò mò ... bất cứ khi nào tôi thử điều này nccho tôi biết rằng tùy chọn -e không được hỗ trợ. Có ý kiến ​​gì không?
tomsmeding

Bạn có thể có nctừ BusyBox. Tôi sử dụng GNUnetcat .
manatwork

1
Thế còn text/plain?
ugoren

Ý bạn là gì @ugoren? Các tệp có phần mở rộng và lỗi .txt đều được phục vụ dưới dạng văn bản / thuần túy. Xem ở đây một số bài kiểm tra: pastebin.com/ErNtvQzx (Bỏ qua các lỗi đọc, chúng là các phản ứng thông thường của khách hàng 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

Các tệp được truyền phát một cách lười biếng, do đó, việc phục vụ các tệp lớn không sử dụng nhiều bộ nhớ, nhưng điều này cũng có nghĩa là chỉ xảy ra lỗi khi bắt đầu truyền (ví dụ: lỗi cấp phép) dẫn đến a 500 Server Error.

Phục vụ trang HTML

Đã thử nghiệm trên Ubuntu 10.10. Có thể được chuyển sang Windows bằng cách thay đổi /var/wwwđể C:/hgolfvà thay đổi main=để main=withSocketsDo$, vì thư viện socket trên Windows đòi hỏi khởi rõ ràng.

Phiên bản bị đánh cắp:

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 (bẻ cong quy tắc 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()

Tôi đã tuyên bố rằng tôi chỉ cần 483 ký tự nếu tôi bẻ cong các quy tắc bởi vì cuối cùng ;c.close()có thể được bỏ qua. Điều này là do thời điểm khách hàng tiếp theo được chấp nhận, ổ cắm vẫn đóng. Điều này tất nhiên sẽ tăng thời gian chờ đợi một chút (Firefox sẽ chỉ hiển thị trang khi ứng dụng khách tiếp theo kết nối, Chrome sẽ hiển thị trước đó, nhưng sẽ tiếp tục tải), nhưng các quy tắc không yêu cầu tôi phải trả lời yêu cầu ngay lập tức , chỉ để làm như vậy tại một số điểm .

Tôi không chắc chắn liệu điều này có hoạt động trên Unix không vì tôi đã sử dụng gửi thay vì sendall và gửi không đảm bảo thực sự gửi mọi thứ mà nó được cung cấp. Nó không hoạt động trên Windows.

Ảnh chụp màn hình không chứng minh bất cứ điều gì vì không có cách nào để biết liệu tôi đã sử dụng Apache hay máy chủ của riêng tôi để tạo ra nó


1
Bạn cũng đang bẻ cong các quy tắc bằng cách phục vụ thư mục hiện tại thay vì C: \ hgolf (8 ký tự) trên cổng 80 thay vì 36895 (3 ký tự).
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()}

nhập mô tả hình ảnh ở đây

Đã thêm tải xuống hình ảnh để hiển thị các tệp nhị phân đang hoạt động đúng - bắt đầu có một số vấn đề với chúng.

Bất cứ ai có thể đề nghị một cách ngắn hơn để đọc đầu vào?


5

Erlang (bản thảo) 575

Erlang rất bẩn. Phải có một dòng trống ở đầu tệp để hoạt động tốt:

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

Cách chạy

$ escript hgolf.erl

Ảnh chụp màn hình

Chỉnh sửa:

Tôi đã vắt ra 20 ký tự. caselà đáng ngạc nhiên ngắn hơn chức năng với thậm chí một đối số và ba mệnh đề.


btw bạn cũng có thể đăng điều đó trong câu hỏi này ( codegolf.stackexchange.com/questions/31647 )
masterX244

4

VB.NET, 7203

Hình ảnh máy chủ đang chạy

Bạn có thể xác định bất kỳ cổng và bất kỳ thư mục cơ sở bằng cách sử dụng --port--base, tương ứng.

Không, đây không thực sự là một giải pháp chơi gôn. Nhưng là VB.NET, thực sự không có điểm nào. Về mặt tích cực, cái này có nhiều tính năng hơn.

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

Tôi thậm chí đã quyết định đưa nó lên GitHub :) https://github.com/minitech/DevServ


2
Haha, vâng, VB.NET không chính xác là ngôn ngữ của người chơi golf. Vẫn tốt đẹp mặc dù.
Đa thức

VB.NET có thể được đánh gôn; có khá nhiều thủ thuật bạn có thể sử dụng.
Joey

@Joey: Vâng, nhưng không có cách nào tôi có thể cạnh tranh với các câu trả lời hiện tại. Tôi sẽ lấy ý kiến ​​ra trước :)
Ry-

@minitech Tôi đã đánh golf câu trả lời của bạn một chút và đăng nó ( codegolf.stackexchange.com/a/21757/15022 ), và nếu bạn muốn, hãy sao chép mã từ đó và tôi sẽ xóa câu trả lời của tôi.
Bàn chải đánh răng

@minitech Trên dòng 46 của mã trên Github, bạn có For i As Integer = 0 To args.Length - 2. Nếu bạn thêm Step 2vào cuối dòng đó, bạn tăng bộ đếm lên hai thay vì 1.
Bàn chải đánh răng

4

C # (869)

Nó đã làm việc

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

Bị đánh cắp

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

Chỉ được thử nghiệm trên Linux, sẽ không hoạt động trên 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)

Ảnh chụp màn hình


2

Scala, 653 ký tự

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

Ảnh chụp màn hình của nó chạy trên MacBook của tôi:

Ảnh chụp màn hình

Không tuyệt lắm, nhưng tôi sẽ thử nghiền nát nó một chút khi tôi có một thời gian sau đó.


Có một ảnh chụp màn hình của nó trong hành động?
Đa thức

@Polynomial Vâng xin lỗi, đã vội vàng đi đâu đó sớm hơn.
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()

nhập mô tả hình ảnh ở đây


afaik bạn có thể sử dụng thụt lề 1 không gian (chứ không phải 4) để lưu 15 ký tự
James Vickery

1

VB.Net (3504 ký tự):

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

Đánh gôn từ câu trả lời của @ minitech.


1

Lua 5.1 trong 435 434 byte

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

... và bằng chứng ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.