ソースコード書いてみた。
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)
 
