ソースコード書いてみた。


require "socket"
require "time"

port = 8001
server = TCPServer.open(port)

while true

  # TCPSocket オブジェクトを取得
  socket = server.accept

  req_headers = {}

  # HTTPリクエストが来るのを待って1行ずつ取得
  while line = socket.gets

    # リクエストの終了
    break if line == "\r\n"

    # リクエストヘッダの取得
    i = line.index(":")

    if i == nil
      # リクエスト行
      if line.split(" ")[0] == "GET"
        req_method = line.strip
      end
    else
      # リクエストヘッダ
      key = line[0, i].strip
      val = line[i..-1].strip
      req_headers[key] = val
    end

  end

  # アクセス元の情報を出力
  p socket.peeraddr

  # リクエスト情報の出力
  p req_method
  p req_headers

  # HTTPステータスを指定
  res_status = "HTTP/1.1 200 OK"

  # レスポンスヘッダの構築
  res_headers = {
    "Server" => "My Ruby Web Server",
    "Date" => Time.now.httpdate, # RFC1123
    "Content-Type" => "text/html; charset=UTF-8"
  }

  # レスポンスボディの構築
  res_body = "<html><body>hello, world</body></html>"

  # レスポンス出力
  socket.puts res_status
  res_headers.each{|key, val|
    socket.puts "#{key}: #{val}"
  }
  socket.puts "Connection: close"
  socket.puts "" # ヘッダとボディの間には空行が必要

  socket.puts res_body

  socket.close
end

server.close

webserver.rb というファイル名で保存して、 ruby コマンドで実行。


$ ruby webserver.rb

HTTPクライアントとして、他のターミナルなどから curl でアクセスしてみる。 --dump-header オプションを付けると、HTTPレスポンスヘッダも見ることができる。


$ curl --dump-header - http://localhost:8001/foo/bar/?a=b
HTTP/1.1 200 OK
Server: My Ruby Web Server
Date: Thu, 12 May 2016 00:42:36 GMT
Content-Type: text/html; charset=UTF-8
Connection: close

<html><body>hello, world</body></html>

Ruby 実行側では、リクエスト情報を出力できている。


$ ruby webserver.rb

["AF_INET", 49926, "127.0.0.1", "127.0.0.1"]
"GET /foo/bar/?a=b HTTP/1.1"
{"Host"=>": localhost:8001", "User-Agent"=>": curl/7.43.0", "Accept"=>": */*"}

今回の環境: Mac OS X El Capitan + Ruby 2.3.0


$ uname -mrsv
Darwin 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64

$ ruby -v
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]

この Web サーバの実装だとシングルスレッドで動作するので、できれば Thread クラスなどでマルチスレッド化したいところ。

tags: ruby

Posted by NI-Lab. (@nilab)