用Ruby编写一个简单的Web服务器

5
我想在 Ruby 中为开发目的创建一个非常简单的Web服务器(不,我不想使用现成的解决方案)。
下面是代码:
#!/usr/bin/ruby

require 'socket'

server = TCPServer.new('127.0.0.1', 8080)

while connection = server.accept
  headers = []
  length  = 0

  while line = connection.gets
    headers << line

    if line =~ /^Content-Length:\s+(\d+)/i
      length = $1.to_i
    end

    break if line == "\r\n"
  end

  body = connection.readpartial(length)

  IO.popen(ARGV[0], 'r+') do |script|
    script.print(headers.join + body)
    script.close_write
    connection.print script.read
  end

  connection.close
end

这个想法是从命令行运行这个脚本,提供另一个脚本,该脚本将在其标准输入上接收请求,并在其标准输出上返回完整的响应。
到目前为止还不错,但结果证明这非常脆弱,在第二个请求中会出现错误:
/usr/bin/serve:24:in `write': Broken pipe (Errno::EPIPE)
    from /usr/bin/serve:24:in `print'
    from /usr/bin/serve:24
    from /usr/bin/serve:23:in `popen'
    from /usr/bin/serve:23

有什么想法可以改进上述代码,使其易于使用吗?
版本:Ubuntu 9.10(2.6.31-20-generic),Ruby 1.8.7(2009年6月12日补丁级别174)[i486-linux]

Joó,由于你和我得到了如此不同的结果,也许你应该在问题中添加你的Ruby和操作系统版本。 - Wayne Conrad
3个回答

5
问题似乎出现在子脚本中,因为你提问中的父脚本在我的系统上运行正常(Debian Squeeze,Ruby 1.8.7 patchlevel 249):
我创建了一个名为bar.rb的虚拟子脚本:
#!/usr/bin/ruby1.8

s = $stdin.read
$stderr.puts s
print s

我随后运行了您的脚本,将虚拟脚本的路径传递给它:

$ /tmp/foo.rb /tmp/bar.rb

我使用wget命令进行了访问:
$ wget localhost:8080/index

然后看到了虚拟脚本的输出:

GET /index HTTP/1.0^M
User-Agent: Wget/1.12 (linux-gnu)^M
Accept: */*^M
Host: localhost:8080^M
Connection: Keep-Alive^M
^M

我看到wget接收到了它发送的内容:
$ cat index
GET /index HTTP/1.0
User-Agent: Wget/1.12 (linux-gnu)
Accept: */*
Host: localhost:8080
Connection: Keep-Alive

无论我用wget打了多少次,它都能正常工作。

我尝试了你的子脚本,第一次运行就出现了错误。可能是某些操作系统级别的差异在起作用。 - Joó Ádám
好的,我找到了问题所在。它在子脚本中:我只是简单地打印了一个基本的HTTP响应,而你先读取了stdin。现在的问题是为什么这样做是必要的。 - Joó Ádám

3

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接