JanGaJan.com

Is fun? JOY!

RubyのProcessを利用したWebサーバーを作る

積読消化シリーズです。
なるほどUnixプロセス ― Rubyで学ぶUnixの基礎
簡単なWebサーバーを作ります。

  • telnetでアクセスするWebサーバー
  • ブラウザでアクセスするWebサーバー
  • forkしたProcessでリクエストを処理するWebサーバー

telnetでアクセスするWebサーバー

まずはtelnetでアクセスするためのシンプルなWebサーバーを用意します。

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env ruby

require 'socket'

# 127.0.0.1(localhost)で8080ポートで立ち上げる
socket = TCPServer.open '127.0.0.1', 8080

loop {
  connection = socket.accept
  connection.puts 'Hello Readers!'
  connection.close
}

サーバーを起動し、ターミナルを立ち上げて、telnetでアクセスしてみます。

1
2
telnet 127.0.0.1 8080
=> Hello Readers!

ただし、このままだとブラウザでhttp://127.0.0.1:8080にアクセスできません。
次はブラウザでアクセスできるように修正します。

ブラウザでアクセスするWebサーバー

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env ruby

require 'socket'
require 'time'

socket = TCPServer.open '127.0.0.1', 8080

loop {
  connection = socket.accept
  # response headerを設定
  connection.puts ['HTTP/1.1 200 OK',
                   "Date: #{Time.now.httpdate}",
                   'Server: Ruby',
                   'Content-Type: text/html; charset=utf-8',
                   "Content-Length: #{response_body.length}\r\n\r\n"].join("\r\n")
  # response bodyを設定
  connection.puts 'Hello Readers!'
  connection.close
}

レスポンスのヘッダー情報を追加しました。
ヘッダーとボディは改行\r\nで区切ります。

これでブラウザでアクセスできるようになりました。

forkしたProcessでリクエストを処理するWebサーバー

続いては、アクセスを子プロセスで処理するように変更します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env ruby                           
require 'socket'
require 'time'

socket = TCPServer.open '127.0.0.1', 8080

worker_pid = fork do
  # 子プロセスで処理
  loop {
    response_body = 'Hello Browser!'
    connection = socket.accept
    connection.puts ['HTTP/1.1 200 OK',
                   "Date: #{Time.now.httpdate}",
                   'Server: Ruby',
                   'Content-Type: text/html; charset=utf-8',
                   "Content-Length: #{response_body.length}\r\n\r\n"].join("\r\n")
    connection.puts response_body
    connection.close
  }
end

# Signalを受け取ったらprocessをkillする
[:INT, :QUIT].each do |signal|
  Signal.trap(signal) {
    Process.kill(signal, worker_pid)
  }
end

# 親プロセスの処理を待ちにする
Process.waitpid worker_pid

forkで子プロセスを生成し、リクエストを子プロセスで処理しています。
親プロセスは子プロセスが処理が完了するまでwaitします。
(子プロセスはloop処理を行っているため、シグナルを受け取るまでは生き続けます)

Comments