From 4766b3245e375c21d3898bdf39d9459770cb9e75 Mon Sep 17 00:00:00 2001 From: Joshua Ballanco Date: Wed, 23 Jun 2010 06:07:04 +0000 Subject: [PATCH] Bringing in a few ancillary changes from the gcd-ify branch git-svn-id: http://svn.macosforge.org/repository/ruby/ControlTower/trunk@4269 23306eb0-4c56-4727-a40e-e92c0eb68959 --- .gitignore | 2 +- HACKING.rdoc | 74 ++++++++++++++++++++++++++++++++ README | 9 ---- README.rdoc | 31 +++++++++++++ TODO | 20 +++++++++ bin/control_tower | 12 ++---- ext/CTParser/CTParser.m | 6 ++- lib/control_tower/rack_socket.rb | 1 - lib/control_tower/server.rb | 1 + sample/file_upload.ru | 17 ++++++++ sample/simple_hello.ru | 7 +++ 11 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 HACKING.rdoc delete mode 100644 README create mode 100644 README.rdoc create mode 100644 TODO create mode 100644 sample/file_upload.ru create mode 100644 sample/simple_hello.ru diff --git a/.gitignore b/.gitignore index f9b1aa2..aea267c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ *.bundle *.o -pkg/* +pkg diff --git a/HACKING.rdoc b/HACKING.rdoc new file mode 100644 index 0000000..d794e8b --- /dev/null +++ b/HACKING.rdoc @@ -0,0 +1,74 @@ +== Hacking Control Tower + +Control Tower is still in very early development. It is being developed as part +of the MacRuby project, so be sure to familiarize yourself with MacRuby's +HACKING.rdoc, as all guidelines there apply here as well. If you have any ideas +or suggestions for improvements, please communicate them with the MacRuby +developer's list at . You can also find more +information at the MacRuby website (http://www.macruby.org/). + + +== CAUTION! AVERTISSEMENT! VOORZICHTIG! 注意! + +DO NOT EDIT http11_parser.c! THIS FILE CONTAINS MACHINE GENERATED CODE. +N'EDITEZ PAS http11_parser.c! Ce fichier a été généré automatiquement. +WIJZIG http11_parser.c NIET! Dit bestand bevat MACHINE gegenereerde code. +http11_parser.cを編集しないでください!このファイルが機械生成されいます。 + +If you really must, you can recreate http11_parser.c from http11_parser.rl using +Ragel (not included). It would also be acceptable to replace the parser all at +once, but editing it is not likely to ever be a good idea. + + +== Sample Code + +There are two samples that you can run to explore ControlTower's behavior on GET +and POST requests located in the 'sample' directory. To use these samples, first +build and install the Control Tower gem: + +> rake gem +> sudo macgem install pkg/control_tower-0.1-universal-darwin-10.gem + +Then, to try a GET request, start the 'simple_hello.ru' rack-up config like so: + +> control_tower -R sample/simple_hello.ru + +and test it with a utility such as curl like so: + +> curl http://localhost:8080/ + +This should reply with a very traditional string and a read-out of the rack +environment generated by your request. To try a POST request, start the +'file_upload.ru' config as above, then use curl (or similar tool) to send a post +with some file content like so: + +> curl -F "file=@README.rdoc" http://localhost:8080/ + +This command tells curl to send the file as a form parameter, and the reply +should contain the content of the rack 'params' variable constructed from this +parameter. In particular, when the parameter is named 'file', the +'file_upload.ru' sample will return the contents of the file. + + +== Debugging + +=== Environment variables + +Currently, there is only one environment variable specifically for debugging +Control Tower: + +* CT_DEBUG: This will turn on debug logging until we get a better logger. + + +== Known Issues + +* Error compiling Regular Expression in Rack::Request + Workaround: Modify line 150 in rack/request.rb like so +- form_vars.sub!(/\0\z/, '') ++ form_vars.slice!(-1) if form_vars[-1] == "\0" + +* Problem with Sinatra > 1.0 using Rack.release + Workaround: Modify line 39 in sinatra/base.rb like so +- if Rack.release < '1.1' ++ if Rack.version < '1.1' + diff --git a/README b/README deleted file mode 100644 index 5810f70..0000000 --- a/README +++ /dev/null @@ -1,9 +0,0 @@ -Control Tower - -Copyright (c) 2009-2010, Apple Inc -Author: Joshua Ballanco - -SYNOPSIS -Control Tower is a Web application server for Rack-based Ruby applications. It -is (or will be) composed of three major components: a networking layer, an HTTP -parser, and a Rack interface. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..8a3247e --- /dev/null +++ b/README.rdoc @@ -0,0 +1,31 @@ +== Control Tower +Control Tower is a web application server for Rack-based MacRuby applications based on Apple's Grand Central Dispatch libraries. + +It is composed of three major components: A Grand Central Dispatch based networking layer, the Mongrel HTTP parser, and Rack web +application interface. It is currently very much a work in progress! + +=== Installation +From the root directory of the project, run: + + $ rake package + $ sudo macgem install pkg/control_tower-0.1-universal-darwin-10.gem + +=== Usage +There are currently only 4 supported command line options: + +* -R : Where you specify the Rackup config file to run +* -h : Hostname for the server (Control Tower will only respond to requests to this host) +* -p : Port # for the server +* -c : Use this to enable serving requests to a GCD concurrent queue + +=== License +Control Tower is covered by the Ruby license. See COPYING for more details. + +=== Credits +Control Tower's parser was stolen Thin which stole it from Mongrel (http://mongrel.rubyforge.org) originially written by Zed Shaw. +Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw You can redistribute it and/or +modify it under the terms of the GPL. + +Thin is copyright Marc-Andre Cournoyer + +Control Tower is copyright (c) 2009-2010, Apple Inc diff --git a/TODO b/TODO new file mode 100644 index 0000000..d6ed639 --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +For 0.1: + +[ ] Logging! + [ ] An ASL-based Logger class + [ ] Request logging (e.g. appache_access.log) + [ ] Error logging (e.g. appache_error.log) + [ ] Debug logging +[ ] Testing! + [ ] Parser test cases + [ ] GET test cases + [ ] POST test cases + [ ] Concurrency testing +[ ] Handle broken request pipes +[ ] Don't reset peer connections +[ ] Protect against malformed Content-Length in headers + +For future: + +[ ] Improve body loading +[ ] Fully-async file upload diff --git a/bin/control_tower b/bin/control_tower index 202abfe..81a54e7 100644 --- a/bin/control_tower +++ b/bin/control_tower @@ -9,7 +9,8 @@ require 'optparse' @options = { :rackup => './config.ru', :port => '8080', - :host => 'localhost' + :host => 'localhost', + :concurrent => false } OptionParser.new do |opts| @@ -25,7 +26,7 @@ OptionParser.new do |opts| @options[:host] = host end - opts.on("-c", "--[no]-concurrency", "Handle requests concurrently") do |concurrent| + opts.on("-c", "--[no-]concurrency", "Handle requests concurrently") do |concurrent| @options[:concurrent] = concurrent end end.parse! @@ -35,16 +36,11 @@ unless File.exist? File.expand_path(@options[:rackup]) exit 1 end -unless File.exist? File.expand_path(@options[:rackup]) - puts "We only know how to deal with Rack-up configs for now" - exit 1 -end - # Under construction...everything is development! ENV['RACK_ENV'] = 'development' rackup_config = File.read(File.expand_path(@options[:rackup])) -app = eval("Rack::Builder.new { #{rackup_config} }.to_app") +app = eval("Rack::Builder.new { #{rackup_config} }").to_app # Let's get to business! server = ControlTower::Server.new(app, @options) diff --git a/ext/CTParser/CTParser.m b/ext/CTParser/CTParser.m index f2e3811..3bd986d 100644 --- a/ext/CTParser/CTParser.m +++ b/ext/CTParser/CTParser.m @@ -81,7 +81,8 @@ void header_done(void *env, const char *at, size_t length) if (colon_pos.location != NSNotFound) { serverName = [hostString substringToIndex:colon_pos.location]; serverPort = [hostString substringFromIndex:(colon_pos.location + 1)]; - } else { + } + else { serverName = [NSString stringWithString:hostString]; serverPort = @"80"; } @@ -104,7 +105,8 @@ void header_done(void *env, const char *at, size_t length) NSMutableString *body = [environment objectForKey:@"rack.input"]; if (body != nil) { [body appendString:[[NSString alloc] initWithBytes:at length:length encoding:NSASCIIStringEncoding]]; - } else { + } + else { NSLog(@"Hmm...you seem to have body data but no where to put it. That's probably an error."); } diff --git a/lib/control_tower/rack_socket.rb b/lib/control_tower/rack_socket.rb index aa8881b..4d8a32b 100644 --- a/lib/control_tower/rack_socket.rb +++ b/lib/control_tower/rack_socket.rb @@ -14,7 +14,6 @@ def initialize(host, port, server, concurrent) @server = server @socket = TCPServer.new(host, port) @status = :closed # Start closed and give the server time to start - prepare_environment #if concurrent # @env['rack.multithread'] = true diff --git a/lib/control_tower/server.rb b/lib/control_tower/server.rb index 322d7f6..c946b2e 100644 --- a/lib/control_tower/server.rb +++ b/lib/control_tower/server.rb @@ -20,6 +20,7 @@ def start end def handle_request(env) + env wrap_output(*@app.call(env)) end diff --git a/sample/file_upload.ru b/sample/file_upload.ru new file mode 100644 index 0000000..bb92c4a --- /dev/null +++ b/sample/file_upload.ru @@ -0,0 +1,17 @@ +require 'rack' + +class Uploader + def call(env) + params = Rack::Request.new(env).params + response = params.map do |k,v| + if k == 'file' && v[:tempfile] + "#{k} => File Contents: #{v[:tempfile].read}" + else + "#{k} => #{v.inspect}" + end + end.join("\n") + "\n" + [200, { 'Content-Type' => 'text/plain' }, response] + end +end + +run Uploader.new diff --git a/sample/simple_hello.ru b/sample/simple_hello.ru new file mode 100644 index 0000000..10f462b --- /dev/null +++ b/sample/simple_hello.ru @@ -0,0 +1,7 @@ +class Hello + def call(env) + [200, { 'Content-Type' => 'text/plain' }, "Hello, world! Your environment is #{env}"] + end +end + +run Hello.new