[macruby-changes] [1952] MacRuby/branches/experimental/sample-macruby/Scripts/web_server.rb
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jun 29 00:25:59 PDT 2009
Revision: 1952
http://trac.macosforge.org/projects/ruby/changeset/1952
Author: lsansonetti at apple.com
Date: 2009-06-29 00:25:58 -0700 (Mon, 29 Jun 2009)
Log Message:
-----------
A very simple Web server written on top of Foundation's run loop and dispatching new requests to a pool of threads.
Added Paths:
-----------
MacRuby/branches/experimental/sample-macruby/Scripts/web_server.rb
Added: MacRuby/branches/experimental/sample-macruby/Scripts/web_server.rb
===================================================================
--- MacRuby/branches/experimental/sample-macruby/Scripts/web_server.rb (rev 0)
+++ MacRuby/branches/experimental/sample-macruby/Scripts/web_server.rb 2009-06-29 07:25:58 UTC (rev 1952)
@@ -0,0 +1,199 @@
+# A very simple Web server written on top of Foundation's run loop and
+# dispatching new requests to a pool of threads.
+
+framework 'Foundation'
+
+class HTTPMessage
+ attr_accessor :code, :content_type, :content
+
+ def data
+ str = "HTTP/1.1 #{code} "
+ str << code == 404 ? "Not Found\n" : "OK\n"
+
+ str << "Date: #{Time.now}\n"
+ str << "Server: #{__FILE__} (MacRuby, Mac OS X)\n"
+ str << "Content-Length: #{@content.length}\n"
+ str << "Keep-Alive: timeout=5, max=100\n"
+ str << "Connection: Keep-Alive\n"
+ str << "Content-Type: #{@content_type}"
+ if content_type.start_with?('text')
+ str << "; charset=utf-8"
+ end
+ str << "\n\n"
+
+ data = nil
+ if @content.is_a?(NSData)
+ data = str.dataUsingEncoding(NSUTF8StringEncoding)
+ data = data.mutableCopy
+ data.appendData(@content)
+ else
+ str << @content
+ data = str.dataUsingEncoding(NSUTF8StringEncoding)
+ end
+ data
+ end
+end
+
+class WebServer
+ def initialize(dir, port, debug)
+ @debug = debug
+ log "Preparing server to serve #{dir} on http://localhost:#{port}"
+
+ @dir = dir
+ @port = NSSocketPort.alloc.initWithTCPPort(port)
+ unless @port
+ raise "Can't open local TCP socket on port #{port}"
+ end
+ fd = @port.socket
+
+ @handle = NSFileHandle.alloc.initWithFileDescriptor fd,
+ closeOnDealloc:true
+
+ NSNotificationCenter.defaultCenter.addObserver self,
+ selector:'new_connection:',
+ name:NSFileHandleConnectionAcceptedNotification,
+ object:nil
+
+ @handle.acceptConnectionInBackgroundAndNotify
+ end
+
+ THREADS_POOL = 4
+
+ def run
+ log "Starting server"
+
+ @threads = []
+ @work_queue = []
+ @work_mutex = Mutex.new
+ THREADS_POOL.times do
+ @threads << Thread.new do
+ begin
+ loop do
+ sleep 0.1
+ handle = @work_mutex.synchronize { @work_queue.shift }
+ if handle
+ handle_request(handle)
+ end
+ end
+ rescue => e
+ puts "ERROR: #{e}"
+ end
+ end
+ end
+
+ NSRunLoop.currentRunLoop.run
+ end
+
+ def new_connection(notification)
+ dict = notification.userInfo
+ remote_handle = dict[NSFileHandleNotificationFileHandleItem]
+
+ @handle.acceptConnectionInBackgroundAndNotify
+
+ if remote_handle
+ @work_mutex.synchronize { @work_queue << remote_handle }
+ @threads.each { |t| t.wakeup }
+ end
+ end
+
+ private
+
+ def handle_request(handle)
+ data = handle.availableData
+ str = NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
+ if str.start_with?('GET ')
+ i = str.index(' ', 4)
+ path = str[4..(i-1)]
+ data = serve_data(path)
+ handle.writeData(data)
+ end
+ handle.closeFile
+ end
+
+ def serve_file(msg, path, real_path)
+ data = NSData.dataWithContentsOfFile(real_path)
+ msg.content = data
+ msg.content_type = case File.extname(real_path)
+ when '.gif'
+ 'image/gif'
+ when '.png'
+ 'image/png'
+ when '.jpg', '.jpeg'
+ 'image/jpg'
+ when '.html'
+ 'text/html'
+ else
+ 'text/plain'
+ end
+ end
+
+ def serve_directory(msg, path, real_path)
+ str = <<EOS
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+ <head>
+ <title>Index of #{path}</title>
+ </head>
+ <body>
+ <h1>Index of #{path}</h1>
+ <pre>
+EOS
+ Dir.entries(real_path).each do |entry|
+ entry_path = File.join(path, entry)
+ str << " <a href=\"#{entry_path}\">#{entry_path}</a>\n"
+ end
+ str << <<EOS
+ <hr>
+ </pre>
+</body>
+</html>
+EOS
+ msg.content_type = 'text/html'
+ msg.content = str
+ end
+
+ def serve_data(path)
+ real_path = File.join(@dir, path)
+ msg = HTTPMessage.new
+
+ if File.exist?(real_path)
+ msg.code = 200
+ if File.directory?(real_path)
+ serve_directory(msg, path, real_path)
+ else
+ serve_file(msg, path, real_path)
+ end
+ else
+ msg.code = 404
+ msg.content_type = 'text/html'
+ msg.content = <<EOS
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html>
+ <head>
+ <title>404 Not Found</title>
+ </head>
+ <body>
+ <h1>Not Found</h1>
+ <p>The requested URL #{path} was not found on this server.</p>
+ </body>
+</html>
+EOS
+ end
+
+ log "#{msg.code} #{real_path}"
+ msg.data
+ end
+
+ def log(msg)
+ puts "#{Thread.current} LOG: #{msg}" if @debug
+ end
+end
+
+dir, port, debug = ARGV[0], ARGV[1], ARGV[2]
+
+dir ||= '/'
+port ||= 8080
+debug ||= false
+
+server = WebServer.new(dir, port, debug)
+server.run
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090629/297d1a0f/attachment.html>
More information about the macruby-changes
mailing list