Permalink
Browse files

added more for thread pool server

  • Loading branch information...
1 parent d376056 commit 1f7742a75beec74ce9211c75e88b830830be5c15 David Balatero committed May 16, 2009
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+# Usage: pooled_server [document root] [port = 3000] [num threads = 20]
+
+require 'socket'
+require File.dirname(__FILE__) + "/../lib/http_server"
+
+# Initialize the router
+router = HttpServer::Router.new(ARGV[0])
+router.add_handler("/time") do |request, response|
+ response.body = "The time is: #{Time.now}"
+end
+
+pool = HttpServer::ThreadPool.new(router, (ARGV[2] || 20))
+server = TCPServer.new(ARGV[1] || 3000)
+
+while socket = server.accept
+ pool.run_request(socket)
+end
@@ -0,0 +1,13 @@
+module HttpServer
+ module ServerMethods
+ def read_http(io)
+ raw_request = ""
+ io.each_line do |line|
+ break if line == "\r\n"
+ raw_request << line
+ end
+ raw_request
+ end
+ end
+end
+
@@ -0,0 +1,60 @@
+module HttpServer
+ class ThreadPool
+ include HttpServer::ServerMethods
+
+ def initialize(router, num_threads = 20)
+ @num_threads = num_threads
+ @available = ThreadGroup.new
+ @thread_available = ConditionVariable.new
+ @thread_mutex = Mutex.new
+ @router = router
+
+ num_threads.times do
+ @available.add(Thread.new(&thread_block))
+ end
+ end
+
+ def run_request(socket)
+ thread = nil
+ while thread.nil?
+ @thread_mutex.synchronize do
+ thread = get_thread
+ @thread_available.wait(@thread_mutex) if thread.nil?
+ end
+ end
+ thread[:socket] = socket
+ thread[:router] = @router
+ thread.run
+ end
+
+ def available_threads
+ @available.list.size
+ end
+
+ private
+ def get_thread
+ @available.list.select { |t| t.stop? }.first
+ end
+
+ def thread_block
+ lambda {
+ while true
+ if Thread.current[:socket].nil?
+ Thread.stop
+ end
+
+ socket = Thread.current[:socket]
+ raw = read_http(socket)
+ request = HttpServer::Request.new(raw)
+ response = Thread.current[:router].handle_request(request)
+ socket.puts(response.raw_http)
+ socket.close if socket.respond_to?(:close)
+
+ # Reset this to available
+ @thread_available.signal
+ Thread.stop
+ end
+ }
+ end
+ end
+end
@@ -0,0 +1,17 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
+
+describe HttpServer::ServerMethods do
+ include HttpServer::ServerMethods
+
+ describe "read_http" do
+ it "should read up to a \r\n" do
+ s = StringIO.new(
+ "GET / HTTP/1.1\r\n" +
+ "Set-Cookie: fdasjklfi2fkldsafkjdlsa\r\n" +
+ "\r\n")
+ result = read_http(s)
+ result.should =~ /^GET/
+ result.should =~ /Set-Cookie/
+ end
+ end
+end
@@ -0,0 +1,35 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
+
+describe HttpServer::ThreadPool do
+ describe "initialize" do
+ it "should take a router" do
+ lambda {
+ HttpServer::ThreadPool.new
+ }.should raise_error
+ end
+ end
+
+ describe "available_threads" do
+ it "should return 20 by default" do
+ tp = HttpServer::ThreadPool.new(mock)
+ tp.available_threads.should == 20
+ end
+ end
+
+ describe "run_request" do
+ before(:each) do
+ @router = HttpServer::Router.new("/tmp")
+ @router.add_handler("/time") do |request, response|
+ response.body = "The time is: #{Time.now}"
+ end
+ @pool = HttpServer::ThreadPool.new(@router, 5)
+ end
+
+ it "should successfully do an HTTP GET" do
+ socket = StringIO.new("GET /time HTTP/1.1\r\n\r\n")
+ @pool.run_request(socket)
+ # this seems like a race condition
+ socket.string.should =~ /OK/
+ end
+ end
+end

0 comments on commit 1f7742a

Please sign in to comment.