3

我正在编写一个小型网络服务器。我想阅读HTTP Request. 它在没有身体参与的情况下起作用。但是当发送正文时,我无法以令人满意的方式阅读正文的内容。

我通过读取来自客户端的数据TCPSocket。该TCPSocket::gets方法读取直到接收到正文的数据。HTTP 请求正文的结束没有分隔符或EOF发送信号。HTTP/1.1 规范 - 第 4.4 节列出了五种获取消息长度的情况。第 1 点)有效。第 2) 点和第 4) 点与我的申请无关。第 5 点)不是一个选项,因为我需要发送回复。

我可以读取该Content-Length字段的值。read(contentlength)但是当我试图通过or来“说服” TCPSocket 读取 HTTP 请求的最后一部分时rcv(contentlength),我没有成功。逐行阅读,直到\r\n分隔标题和正文的工作,但在那之后我被卡住了 - 至少在我想要的方式上。

所以我的问题是:

有没有可能像我在代码中想要的那样做?有没有更好的方法来实现我HTTP Request正确阅读的目标(我真的希望)?

这是可运行的代码。我想工作的部分在评论中。

#!/usr/bin/ruby
require 'socket'

server = TCPServer.new 2000
loop do
  Thread.start(server.accept) do |client|
    hascontent = false
    contentlength = 0
    content = ""
    request = ""
    #This seems to work, but I'm not really happy with it, too much is happening in
    #the loop
    while(buf = client.readpartial(4096))
       request = request + buf
       split = request.split("\r\n")
       puts request
       puts request.dump
       puts split.length
       puts split.inspect
       if(request.index("\r\n\r\n")>0)
         break
       end
    end
#This part is commented out because it doesn't work
=begin
    while(line = client.gets)
       puts ":" + line
       request = request + line
       if(line.start_with?("Content-Length"))
         hascontent = true
         split = line.split(' ')
         contentlength = split[1]
       end
       if(line == "\r\n" and !hascontent)
         break
       end
       if(line == "\r\n" and hascontent)
         puts "Trying to get content :P"
         puts contentlength
         puts content.length
         puts client.inspect
         #tried read, with and without parameter, rcv, also with and 
         #without param and their nonblocking couterparts
         #where does my thought process go in the wrong direction  
         while(readin = client.readpartial(contentlength))
           puts readin
           content = content + readin
         end
         break
       end
    end
=end
    puts request
    client.close
 end
4

1 回答 1

2

所以...过去 2 个小时我也遇到了这个问题,所以我对 Socket API 做了一些研究。原来SocketextendsBasicSocket有一个方法recvmsg。当我尝试调用它时,我得到以下信息:

["GET / HTTP/1.1\r\nHost: localhost:12357\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9\r\n\r\n", #<Addrinfo: empty-sockaddr SOCK_STREAM>, 0]

IE 我完整的 HTTP 请求、发件人的地址信息和任何其他 ruby​​ 标志。

您可以使用 recvmsg 读取整个 HTTP 请求:

raw_request = client.recvmsg()
request = /(?<METHOD>\w+) \/(?<RESOURCE>[^ ]*) HTTP\/1.\d\r\n(?<HEADERS>(.+\r\n)*)(?:\r\n)?(?<BODY>(.|\s)*)/i.match(raw_request)
p request["BODY"]

我不知道如何做到这一点,recvmsg但我很高兴该功能存在。

于 2018-05-21T22:33:03.970 回答