Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial revision of ControlTower project

git-svn-id: http://svn.macosforge.org/repository/ruby/ControlTower/trunk@3975 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
commit ce4c0dd00e1cc147c64bc3aa88396adf0b58af2d 1 parent c4990fa
lsansonetti@apple.com authored
View
9 README
@@ -0,0 +1,9 @@
+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.
View
80 Rakefile
@@ -0,0 +1,80 @@
+require 'rubygems'
+require 'rake/gempackagetask'
+
+CT_VERSION = '0.1'
+
+GEM_SPEC = Gem::Specification.new do |spec|
+ spec.platform = Gem::Platform.local
+ spec.name = 'control_tower'
+ spec.summary = "A Rack-based HTTP server for MacRuby"
+ spec.description = <<-DESCRIPTION
+ Control Tower is a Rack-based HTTP server designed to work with MacRuby. It can
+ be used by calling to its Rack::Handler class, or by running the control_tower
+ executable with a Rackup configuration file (see the control tower help for more
+ details).
+ DESCRIPTION
+ spec.version = CT_VERSION
+ spec.add_runtime_dependency 'rack', '>= 1.0.1'
+ spec.files = %w(
+ lib/control_tower.rb
+ lib/control_tower/rack_socket.rb
+ lib/control_tower/server.rb
+ lib/rack/handler/control_tower.rb
+ lib/CTParser.bundle
+ bin/control_tower
+ )
+ spec.executable = 'control_tower'
+end
+
+verbose(true)
+
+desc "Same as all"
+task :default => :all
+
+desc "Build everything"
+task :all => ['build', 'gem']
+
+desc "Build CTParser"
+task :build do
+ gcc = RbConfig::CONFIG['CC']
+ cflags = RbConfig::CONFIG['CFLAGS'] + ' ' + RbConfig::CONFIG['ARCH_FLAG']
+
+ Dir.chdir('ext/CTParser') do
+ sh "#{gcc} #{cflags} -fobjc-gc CTParser.m -c -o CTParser.o"
+ sh "#{gcc} #{cflags} http11_parser.c -c -o http11_parser.o"
+ sh "#{RbConfig::CONFIG['LDSHARED']} CTParser.o http11_parser.o -o CTParser.bundle"
+ end
+end
+
+desc "Clean packages and extensions"
+task :clean do
+ sh "rm -rf pkg ext/CTParser/*.o ext/CTParser/*.bundle lib/*.bundle"
+end
+
+desc "Install as a standard library"
+task :stdlib_install => [:build] do
+ prefix = (ENV['DESTDIR'] || '')
+ dest = File.join(prefix, RbConfig::CONFIG['sitelibdir'])
+ mkdir_p(dest)
+ sh "ditto lib \"#{dest}\""
+ dest = File.join(prefix, RbConfig::CONFIG['sitearchdir'])
+ mkdir_p(dest)
+ sh "cp ext/CTParser/CTParser.bundle \"#{dest}\""
+end
+
+file 'ext/CTParser/CTParser.bundle' => 'build'
+
+file 'lib/CTParser.bundle' => ['ext/CTParser/CTParser.bundle'] do
+ FileUtils.cp('ext/CTParser/CTParser.bundle', 'lib/CTParser.bundle')
+end
+
+Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
+ pkg.need_zip = false
+ pkg.need_tar = true
+end
+
+desc "Run Control Tower"
+task :run do
+ sh "macruby -I./lib -I./ext/CTParser bin/control_tower"
+end
+
View
57 bin/control_tower
@@ -0,0 +1,57 @@
+#!/usr/bin/env macruby
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+require 'control_tower'
+require 'optparse'
+
+# Some default values
+@options = {
+ :rackup => './config.ru',
+ :port => '8080',
+ :host => 'localhost'
+}
+
+OptionParser.new do |opts|
+ opts.on("-R", "--rackup [FILE]", "Rack-up Configuration File") do |rackup|
+ @options[:rackup] = rackup
+ end
+
+ opts.on("-p", "--port [SERVER_PORT]", Integer, "Port on which to run the server") do |port|
+ @options[:port] = port
+ end
+
+ opts.on("-h", "--host [HOSTNAME]", "Hostname for the server") do |host|
+ @options[:host] = host
+ end
+
+ opts.on("-c", "--[no]-concurrency", "Handle requests concurrently") do |concurrent|
+ @options[:concurrent] = concurrent
+ end
+end.parse!
+
+unless File.exist? File.expand_path(@options[:rackup])
+ puts "We only know how to deal with Rack-up configs for now"
+ 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}\n )}.to_app", TOPLEVEL_BINDING)
+
+# Let's get to business!
+server = ControlTower::Server.new(app, @options)
+if server
+ puts "You are cleared for take-off!"
+ server.start
+else
+ puts "Mayday! Mayday! Eject! Eject!\n#{$!}"
+ exit 1
+end
View
37 ext/CTParser/CTParser.h
@@ -0,0 +1,37 @@
+/*
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+ */
+
+#include "http11_parser.h"
+#import <Foundation/Foundation.h>
+
+// TODO - We should grab this from a plist somewhere...
+#define SERVER_SOFTWARE @"Control Tower v0.1"
+
+@interface CTParser : NSObject
+{
+ http_parser *_parser;
+ NSString *_body;
+}
+
+@property(copy) NSString *body;
+
+- (id)init;
+- (void)reset;
+
+- (NSNumber *)parseData:(NSString *)dataBuf forEnvironment:(NSDictionary *)env startingAt:(NSNumber *)startingPos;
+- (NSNumber *)parseData:(NSString *)dataBuf forEnvironment:(NSDictionary *)env;
+
+- (BOOL)errorCond;
+- (BOOL)finished;
+- (NSNumber *)nread;
+
+- (void)finalize;
+
+@end
+
+// Describe enough of the StringIO interface to write to one
+@interface RBStringIO : NSObject
+- (void)write:(NSString *)dataBuf;
+@end
View
194 ext/CTParser/CTParser.m
@@ -0,0 +1,194 @@
+/*
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+ */
+
+#import "CTParser.h"
+
+#pragma mark Parser Callbacks
+
+#define DEF_MAX_LENGTH(N, val) const size_t MAX_##N##_LENGTH = val
+
+#define VALIDATE_MAX_LENGTH(len, N)\
+ if(len > MAX_##N##_LENGTH) {\
+ [NSException raise:@"ParserFieldLengthError"\
+ format:@"HTTP element " # N " is longer than the " # len " character allowed length."];\
+ }
+
+#define PARSE_FIELD(field)\
+ void parse_##field (void *env, const char *at, size_t length)\
+ {\
+ VALIDATE_MAX_LENGTH(length, field)\
+ [(NSMutableDictionary *)env setObject:[[NSString alloc] initWithBytes:at\
+ length:length\
+ encoding:NSUTF8StringEncoding ]\
+ forKey:@"" #field];\
+ return;\
+ }
+
+// Max field lengths
+DEF_MAX_LENGTH(FIELD_NAME, 256);
+DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
+DEF_MAX_LENGTH(REQUEST_METHOD, 256);
+DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
+DEF_MAX_LENGTH(FRAGMENT, 1024);
+DEF_MAX_LENGTH(PATH_INFO, 1024);
+DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
+DEF_MAX_LENGTH(HTTP_VERSION, 256);
+DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
+
+void parse_HTTP_FIELD(void *env, const char *field, size_t flen, const char *value, size_t vlen)
+{
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
+ [(NSMutableDictionary *)env setObject:[[NSString alloc] initWithBytes:value
+ length:vlen
+ encoding:NSUTF8StringEncoding]
+ forKey:[@"HTTP_" stringByAppendingString:[[NSString alloc] initWithBytes:field
+ length:flen
+ encoding:NSUTF8StringEncoding]]];
+ return;
+}
+
+// Parsing callback functions
+PARSE_FIELD(REQUEST_METHOD);
+PARSE_FIELD(REQUEST_URI);
+PARSE_FIELD(FRAGMENT);
+PARSE_FIELD(PATH_INFO);
+PARSE_FIELD(QUERY_STRING);
+PARSE_FIELD(HTTP_VERSION);
+
+void header_done(void *env, const char *at, size_t length)
+{
+ NSMutableDictionary *environment = (NSMutableDictionary *)env;
+ NSString *contentLength = [environment objectForKey:@"HTTP_CONTENT_LENGTH"];
+ if (contentLength != nil) {
+ [environment setObject:contentLength forKey:@"CONTENT_LENGTH"];
+ }
+
+ NSString *contentType = [environment objectForKey:@"HTTP_CONTENT_TYPE"];
+ if (contentType != nil) {
+ [environment setObject:contentType forKey:@"CONTENT_TYPE"];
+ }
+
+ [environment setObject:@"CGI/1.2" forKey:@"GATEWAY_INTERFACE"];
+
+ NSString *hostString = [environment objectForKey:@"HTTP_HOST"];
+ NSString *serverName = nil;
+ NSString *serverPort = nil;
+ if (hostString != nil) {
+ NSRange colon_pos = [hostString rangeOfString:@":"];
+ if (colon_pos.location != NSNotFound) {
+ serverName = [hostString substringToIndex:colon_pos.location];
+ serverPort = [hostString substringFromIndex:(colon_pos.location + 1)];
+ } else {
+ serverName = [NSString stringWithString:hostString];
+ serverPort = @"80";
+ }
+ [environment setObject:serverName forKey:@"SERVER_NAME"];
+ [environment setObject:serverPort forKey:@"SERVER_PORT"];
+ }
+
+ [environment setObject:@"HTTP/1.1" forKey:@"SERVER_PROTOCOL"];
+ [environment setObject:SERVER_SOFTWARE forKey:@"SERVER_SOFTWARE"];
+
+ // We don't do tls yet
+ [environment setObject:@"http" forKey:@"rack.url_scheme"];
+
+ // To satisfy Rack specs...
+ if ([environment objectForKey:@"QUERY_STRING"] == nil) {
+ [environment setObject:@"" forKey:@"QUERY_STRING"];
+ }
+
+ // If we've been given any part of the body, put it here
+ NSMutableString *body = [environment objectForKey:@"rack.input"];
+ if (body != nil) {
+ [body appendString:[[NSString alloc] initWithBytes:at length:length encoding:NSASCIIStringEncoding]];
+ } else {
+ NSLog(@"Hmm...you seem to have body data but no where to put it. That's probably an error.");
+ }
+
+ return;
+}
+
+@implementation CTParser
+
+@synthesize body = _body;
+
+- (id)init {
+ [super init];
+ _parser = malloc(sizeof(http_parser));
+
+ // Setup the callbacks
+ _parser->http_field = parse_HTTP_FIELD;
+ _parser->request_method = parse_REQUEST_METHOD;
+ _parser->request_uri = parse_REQUEST_URI;
+ _parser->fragment = parse_FRAGMENT;
+ _parser->request_path = parse_PATH_INFO;
+ _parser->query_string = parse_QUERY_STRING;
+ _parser->http_version = parse_HTTP_VERSION;
+ _parser->header_done = header_done;
+
+ http_parser_init(_parser);
+ return self;
+}
+
+- (void)reset
+{
+ http_parser_init(_parser);
+ return;
+}
+
+
+- (NSNumber *)parseData:(NSString *)dataBuf forEnvironment:(NSMutableDictionary *)env startingAt:(NSNumber *)startingPos
+{
+ const char *data = [dataBuf UTF8String];
+ size_t length = [dataBuf length];
+ size_t offset = [startingPos unsignedLongValue];
+ _parser->data = env;
+
+ http_parser_execute(_parser, data, length, offset);
+ if (http_parser_has_error(_parser)) {
+ [NSException raise:@"CTParserError" format:@"Invalid HTTP format, parsing failed."];
+ }
+
+ NSNumber *headerLength = [NSNumber numberWithUnsignedLong:_parser->nread];
+ VALIDATE_MAX_LENGTH([headerLength unsignedLongValue], HEADER);
+ return headerLength;
+}
+
+- (NSNumber *)parseData:(NSString *)dataBuf forEnvironment:(NSDictionary *)env
+{
+ return [self parseData:dataBuf forEnvironment:env startingAt:0];
+}
+
+- (BOOL)errorCond
+{
+ return http_parser_has_error(_parser);
+}
+
+- (BOOL)finished
+{
+ return http_parser_is_finished(_parser);
+}
+
+- (NSNumber *)nread
+{
+ return [NSNumber numberWithInt:_parser->nread];
+}
+
+- (void)finalize
+{
+ if (_parser != NULL)
+ free(_parser);
+ [super finalize];
+}
+
+@end
+
+void
+Init_CTParser(void)
+{
+ // Do nothing. This function is required by the MacRuby runtime when this
+ // file is compiled as a C extension bundle.
+}
View
3  ext/CTParser/extconf.rb
@@ -0,0 +1,3 @@
+require 'mkmf'
+$CFLAGS << ' -fobjc-gc -g '
+create_makefile("CTParser")
View
1,086 ext/CTParser/http11_parser.c
@@ -0,0 +1,1086 @@
+/**
+ * Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+#include "http11_parser.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * capitalizes all lower-case ASCII characters,
+ * converts dashes to underscores.
+ */
+static void snake_upcase_char(char *c)
+{
+ if (*c >= 'a' && *c <= 'z')
+ *c &= ~0x20;
+ else if (*c == '-')
+ *c = '_';
+}
+
+#define LEN(AT, FPC) (FPC - buffer - parser->AT)
+#define MARK(M,FPC) (parser->M = (FPC) - buffer)
+#define PTR_TO(F) (buffer + parser->F)
+
+static const int http_parser_start = 1;
+static const int http_parser_first_final = 57;
+static const int http_parser_error = 0;
+
+static const int http_parser_en_main = 1;
+
+int http_parser_init(http_parser *parser) {
+ int cs = 0;
+
+ cs = http_parser_start;
+ parser->cs = cs;
+ parser->body_start = 0;
+ parser->content_len = 0;
+ parser->mark = 0;
+ parser->nread = 0;
+ parser->field_len = 0;
+ parser->field_start = 0;
+
+ return(1);
+}
+
+
+size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
+ const char *p, *pe;
+ int cs = parser->cs;
+
+ assert(off <= len && "offset past end of buffer");
+
+ p = buffer+off;
+ pe = buffer+len;
+
+ assert(*pe == '\0' && "pointer does not end on NUL");
+ assert(pe - p == len - off && "pointers aren't same distance");
+
+
+
+ if ( p == pe )
+ goto _out;
+ switch ( cs )
+ {
+ case 1:
+ switch( (*p) ) {
+ case 36: goto tr0;
+ case 95: goto tr0;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto tr0;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto tr0;
+ } else
+ goto tr0;
+ goto st0;
+st0:
+ goto _out0;
+tr0:
+ MARK(mark, p);
+ goto st2;
+st2:
+ if ( ++p == pe )
+ goto _out2;
+ case 2:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st38;
+ case 95: goto st38;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st38;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st38;
+ } else
+ goto st38;
+ goto st0;
+tr2:
+ if(parser->request_method != NULL)
+ parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _out3;
+ case 3:
+ switch( (*p) ) {
+ case 42: goto tr4;
+ case 43: goto tr5;
+ case 47: goto tr6;
+ case 58: goto tr7;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr5;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr5;
+ } else
+ goto tr5;
+ goto st0;
+tr4:
+ MARK(mark, p);
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _out4;
+ case 4:
+ switch( (*p) ) {
+ case 32: goto tr8;
+ case 35: goto tr9;
+ }
+ goto st0;
+tr8:
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st5;
+tr30:
+ if(parser->fragment != NULL)
+ parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st5;
+tr40:
+ if(parser->request_path != NULL)
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st5;
+tr51:
+ MARK(query_start, p);
+ if(parser->query_string != NULL)
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st5;
+tr55:
+ if(parser->query_string != NULL)
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _out5;
+ case 5:
+ if ( (*p) == 72 )
+ goto tr10;
+ goto st0;
+tr10:
+ MARK(mark, p);
+ goto st6;
+st6:
+ if ( ++p == pe )
+ goto _out6;
+ case 6:
+ if ( (*p) == 84 )
+ goto st7;
+ goto st0;
+st7:
+ if ( ++p == pe )
+ goto _out7;
+ case 7:
+ if ( (*p) == 84 )
+ goto st8;
+ goto st0;
+st8:
+ if ( ++p == pe )
+ goto _out8;
+ case 8:
+ if ( (*p) == 80 )
+ goto st9;
+ goto st0;
+st9:
+ if ( ++p == pe )
+ goto _out9;
+ case 9:
+ if ( (*p) == 47 )
+ goto st10;
+ goto st0;
+st10:
+ if ( ++p == pe )
+ goto _out10;
+ case 10:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st11;
+ goto st0;
+st11:
+ if ( ++p == pe )
+ goto _out11;
+ case 11:
+ if ( (*p) == 46 )
+ goto st12;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st11;
+ goto st0;
+st12:
+ if ( ++p == pe )
+ goto _out12;
+ case 12:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st13;
+ goto st0;
+st13:
+ if ( ++p == pe )
+ goto _out13;
+ case 13:
+ if ( (*p) == 13 )
+ goto tr18;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st13;
+ goto st0;
+tr18:
+ if(parser->http_version != NULL)
+ parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st14;
+tr26:
+ if(parser->http_field != NULL) {
+ parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+ }
+ goto st14;
+st14:
+ if ( ++p == pe )
+ goto _out14;
+ case 14:
+ if ( (*p) == 10 )
+ goto st15;
+ goto st0;
+st15:
+ if ( ++p == pe )
+ goto _out15;
+ case 15:
+ switch( (*p) ) {
+ case 13: goto st16;
+ case 33: goto tr21;
+ case 124: goto tr21;
+ case 126: goto tr21;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 39 ) {
+ if ( 42 <= (*p) && (*p) <= 43 )
+ goto tr21;
+ } else if ( (*p) >= 35 )
+ goto tr21;
+ } else if ( (*p) > 46 ) {
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr21;
+ } else if ( (*p) > 90 ) {
+ if ( 94 <= (*p) && (*p) <= 122 )
+ goto tr21;
+ } else
+ goto tr21;
+ } else
+ goto tr21;
+ goto st0;
+st16:
+ if ( ++p == pe )
+ goto _out16;
+ case 16:
+ if ( (*p) == 10 )
+ goto tr22;
+ goto st0;
+tr22:
+ parser->body_start = p - buffer + 1;
+ if(parser->header_done != NULL)
+ parser->header_done(parser->data, p + 1, pe - p - 1);
+ goto _out57;
+ goto st57;
+st57:
+ if ( ++p == pe )
+ goto _out57;
+ case 57:
+ goto st0;
+tr21:
+ MARK(field_start, p);
+ snake_upcase_char((char *)p);
+ goto st17;
+tr23:
+ snake_upcase_char((char *)p);
+ goto st17;
+st17:
+ if ( ++p == pe )
+ goto _out17;
+ case 17:
+ switch( (*p) ) {
+ case 33: goto tr23;
+ case 58: goto tr24;
+ case 124: goto tr23;
+ case 126: goto tr23;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 39 ) {
+ if ( 42 <= (*p) && (*p) <= 43 )
+ goto tr23;
+ } else if ( (*p) >= 35 )
+ goto tr23;
+ } else if ( (*p) > 46 ) {
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr23;
+ } else if ( (*p) > 90 ) {
+ if ( 94 <= (*p) && (*p) <= 122 )
+ goto tr23;
+ } else
+ goto tr23;
+ } else
+ goto tr23;
+ goto st0;
+tr24:
+ parser->field_len = LEN(field_start, p);
+ goto st18;
+tr27:
+ MARK(mark, p);
+ goto st18;
+st18:
+ if ( ++p == pe )
+ goto _out18;
+ case 18:
+ switch( (*p) ) {
+ case 13: goto tr26;
+ case 32: goto tr27;
+ }
+ goto tr25;
+tr25:
+ MARK(mark, p);
+ goto st19;
+st19:
+ if ( ++p == pe )
+ goto _out19;
+ case 19:
+ if ( (*p) == 13 )
+ goto tr26;
+ goto st19;
+tr9:
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st20;
+tr41:
+ if(parser->request_path != NULL)
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st20;
+tr52:
+ MARK(query_start, p);
+ if(parser->query_string != NULL)
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st20;
+tr56:
+ if(parser->query_string != NULL)
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ if(parser->request_uri != NULL)
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ goto st20;
+st20:
+ if ( ++p == pe )
+ goto _out20;
+ case 20:
+ switch( (*p) ) {
+ case 32: goto tr30;
+ case 35: goto st0;
+ case 37: goto tr31;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto tr29;
+tr29:
+ MARK(mark, p);
+ goto st21;
+st21:
+ if ( ++p == pe )
+ goto _out21;
+ case 21:
+ switch( (*p) ) {
+ case 32: goto tr30;
+ case 35: goto st0;
+ case 37: goto st22;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st21;
+tr31:
+ MARK(mark, p);
+ goto st22;
+st22:
+ if ( ++p == pe )
+ goto _out22;
+ case 22:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st23;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st23;
+ } else
+ goto st23;
+ goto st0;
+st23:
+ if ( ++p == pe )
+ goto _out23;
+ case 23:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st21;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st21;
+ } else
+ goto st21;
+ goto st0;
+tr5:
+ MARK(mark, p);
+ goto st24;
+st24:
+ if ( ++p == pe )
+ goto _out24;
+ case 24:
+ switch( (*p) ) {
+ case 43: goto st24;
+ case 58: goto st25;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st24;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto st24;
+ } else if ( (*p) >= 65 )
+ goto st24;
+ } else
+ goto st24;
+ goto st0;
+tr7:
+ MARK(mark, p);
+ goto st25;
+st25:
+ if ( ++p == pe )
+ goto _out25;
+ case 25:
+ switch( (*p) ) {
+ case 32: goto tr8;
+ case 35: goto tr9;
+ case 37: goto st26;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st25;
+st26:
+ if ( ++p == pe )
+ goto _out26;
+ case 26:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st27;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st27;
+ } else
+ goto st27;
+ goto st0;
+st27:
+ if ( ++p == pe )
+ goto _out27;
+ case 27:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st25;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st25;
+ } else
+ goto st25;
+ goto st0;
+tr6:
+ MARK(mark, p);
+ goto st28;
+st28:
+ if ( ++p == pe )
+ goto _out28;
+ case 28:
+ switch( (*p) ) {
+ case 32: goto tr40;
+ case 35: goto tr41;
+ case 37: goto st29;
+ case 59: goto tr43;
+ case 63: goto tr44;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st28;
+st29:
+ if ( ++p == pe )
+ goto _out29;
+ case 29:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st30;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st30;
+ } else
+ goto st30;
+ goto st0;
+st30:
+ if ( ++p == pe )
+ goto _out30;
+ case 30:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st28;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st28;
+ } else
+ goto st28;
+ goto st0;
+tr43:
+ if(parser->request_path != NULL)
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ goto st31;
+st31:
+ if ( ++p == pe )
+ goto _out31;
+ case 31:
+ switch( (*p) ) {
+ case 32: goto tr8;
+ case 35: goto tr9;
+ case 37: goto st32;
+ case 63: goto st34;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st31;
+st32:
+ if ( ++p == pe )
+ goto _out32;
+ case 32:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st33;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st33;
+ } else
+ goto st33;
+ goto st0;
+st33:
+ if ( ++p == pe )
+ goto _out33;
+ case 33:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st31;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st31;
+ } else
+ goto st31;
+ goto st0;
+tr44:
+ if(parser->request_path != NULL)
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ goto st34;
+st34:
+ if ( ++p == pe )
+ goto _out34;
+ case 34:
+ switch( (*p) ) {
+ case 32: goto tr51;
+ case 35: goto tr52;
+ case 37: goto tr53;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto tr50;
+tr50:
+ MARK(query_start, p);
+ goto st35;
+st35:
+ if ( ++p == pe )
+ goto _out35;
+ case 35:
+ switch( (*p) ) {
+ case 32: goto tr55;
+ case 35: goto tr56;
+ case 37: goto st36;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st35;
+tr53:
+ MARK(query_start, p);
+ goto st36;
+st36:
+ if ( ++p == pe )
+ goto _out36;
+ case 36:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st37;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st37;
+ } else
+ goto st37;
+ goto st0;
+st37:
+ if ( ++p == pe )
+ goto _out37;
+ case 37:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st35;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st35;
+ } else
+ goto st35;
+ goto st0;
+st38:
+ if ( ++p == pe )
+ goto _out38;
+ case 38:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st39;
+ case 95: goto st39;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st39;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st39;
+ } else
+ goto st39;
+ goto st0;
+st39:
+ if ( ++p == pe )
+ goto _out39;
+ case 39:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st40;
+ case 95: goto st40;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st40;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st40;
+ } else
+ goto st40;
+ goto st0;
+st40:
+ if ( ++p == pe )
+ goto _out40;
+ case 40:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st41;
+ case 95: goto st41;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st41;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st41;
+ } else
+ goto st41;
+ goto st0;
+st41:
+ if ( ++p == pe )
+ goto _out41;
+ case 41:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st42;
+ case 95: goto st42;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st42;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st42;
+ } else
+ goto st42;
+ goto st0;
+st42:
+ if ( ++p == pe )
+ goto _out42;
+ case 42:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st43;
+ case 95: goto st43;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st43;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st43;
+ } else
+ goto st43;
+ goto st0;
+st43:
+ if ( ++p == pe )
+ goto _out43;
+ case 43:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st44;
+ case 95: goto st44;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st44;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st44;
+ } else
+ goto st44;
+ goto st0;
+st44:
+ if ( ++p == pe )
+ goto _out44;
+ case 44:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st45;
+ case 95: goto st45;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st45;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st45;
+ } else
+ goto st45;
+ goto st0;
+st45:
+ if ( ++p == pe )
+ goto _out45;
+ case 45:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st46;
+ case 95: goto st46;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st46;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st46;
+ } else
+ goto st46;
+ goto st0;
+st46:
+ if ( ++p == pe )
+ goto _out46;
+ case 46:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st47;
+ case 95: goto st47;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st47;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st47;
+ } else
+ goto st47;
+ goto st0;
+st47:
+ if ( ++p == pe )
+ goto _out47;
+ case 47:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st48;
+ case 95: goto st48;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st48;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st48;
+ } else
+ goto st48;
+ goto st0;
+st48:
+ if ( ++p == pe )
+ goto _out48;
+ case 48:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st49;
+ case 95: goto st49;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st49;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st49;
+ } else
+ goto st49;
+ goto st0;
+st49:
+ if ( ++p == pe )
+ goto _out49;
+ case 49:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st50;
+ case 95: goto st50;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st50;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st50;
+ } else
+ goto st50;
+ goto st0;
+st50:
+ if ( ++p == pe )
+ goto _out50;
+ case 50:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st51;
+ case 95: goto st51;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st51;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st51;
+ } else
+ goto st51;
+ goto st0;
+st51:
+ if ( ++p == pe )
+ goto _out51;
+ case 51:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st52;
+ case 95: goto st52;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st52;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st52;
+ } else
+ goto st52;
+ goto st0;
+st52:
+ if ( ++p == pe )
+ goto _out52;
+ case 52:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st53;
+ case 95: goto st53;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st53;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st53;
+ } else
+ goto st53;
+ goto st0;
+st53:
+ if ( ++p == pe )
+ goto _out53;
+ case 53:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st54;
+ case 95: goto st54;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st54;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st54;
+ } else
+ goto st54;
+ goto st0;
+st54:
+ if ( ++p == pe )
+ goto _out54;
+ case 54:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st55;
+ case 95: goto st55;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st55;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st55;
+ } else
+ goto st55;
+ goto st0;
+st55:
+ if ( ++p == pe )
+ goto _out55;
+ case 55:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st56;
+ case 95: goto st56;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st56;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st56;
+ } else
+ goto st56;
+ goto st0;
+st56:
+ if ( ++p == pe )
+ goto _out56;
+ case 56:
+ if ( (*p) == 32 )
+ goto tr2;
+ goto st0;
+ }
+_out0: cs = 0; goto _out;
+_out2: cs = 2; goto _out;
+_out3: cs = 3; goto _out;
+_out4: cs = 4; goto _out;
+_out5: cs = 5; goto _out;
+_out6: cs = 6; goto _out;
+_out7: cs = 7; goto _out;
+_out8: cs = 8; goto _out;
+_out9: cs = 9; goto _out;
+_out10: cs = 10; goto _out;
+_out11: cs = 11; goto _out;
+_out12: cs = 12; goto _out;
+_out13: cs = 13; goto _out;
+_out14: cs = 14; goto _out;
+_out15: cs = 15; goto _out;
+_out16: cs = 16; goto _out;
+_out57: cs = 57; goto _out;
+_out17: cs = 17; goto _out;
+_out18: cs = 18; goto _out;
+_out19: cs = 19; goto _out;
+_out20: cs = 20; goto _out;
+_out21: cs = 21; goto _out;
+_out22: cs = 22; goto _out;
+_out23: cs = 23; goto _out;
+_out24: cs = 24; goto _out;
+_out25: cs = 25; goto _out;
+_out26: cs = 26; goto _out;
+_out27: cs = 27; goto _out;
+_out28: cs = 28; goto _out;
+_out29: cs = 29; goto _out;
+_out30: cs = 30; goto _out;
+_out31: cs = 31; goto _out;
+_out32: cs = 32; goto _out;
+_out33: cs = 33; goto _out;
+_out34: cs = 34; goto _out;
+_out35: cs = 35; goto _out;
+_out36: cs = 36; goto _out;
+_out37: cs = 37; goto _out;
+_out38: cs = 38; goto _out;
+_out39: cs = 39; goto _out;
+_out40: cs = 40; goto _out;
+_out41: cs = 41; goto _out;
+_out42: cs = 42; goto _out;
+_out43: cs = 43; goto _out;
+_out44: cs = 44; goto _out;
+_out45: cs = 45; goto _out;
+_out46: cs = 46; goto _out;
+_out47: cs = 47; goto _out;
+_out48: cs = 48; goto _out;
+_out49: cs = 49; goto _out;
+_out50: cs = 50; goto _out;
+_out51: cs = 51; goto _out;
+_out52: cs = 52; goto _out;
+_out53: cs = 53; goto _out;
+_out54: cs = 54; goto _out;
+_out55: cs = 55; goto _out;
+_out56: cs = 56; goto _out;
+
+_out:
+ parser->cs = cs;
+ parser->nread += p - (buffer + off);
+
+ assert(p <= pe && "buffer overflow after parsing execute");
+ assert(parser->nread <= len && "nread longer than length");
+ assert(parser->body_start <= len && "body starts after buffer end");
+ assert(parser->mark < len && "mark is after buffer end");
+ assert(parser->field_len <= len && "field has length longer than whole buffer");
+ assert(parser->field_start < len && "field starts after buffer end");
+
+ if(parser->body_start) {
+ /* final \r\n combo encountered so stop right here */
+
+ parser->nread++;
+ }
+
+ return(parser->nread);
+}
+
+int http_parser_finish(http_parser *parser)
+{
+ int cs = parser->cs;
+
+ parser->cs = cs;
+
+ if (http_parser_has_error(parser) ) {
+ return -1;
+ } else if (http_parser_is_finished(parser) ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int http_parser_has_error(http_parser *parser) {
+ return parser->cs == http_parser_error;
+}
+
+int http_parser_is_finished(http_parser *parser) {
+ return parser->cs == http_parser_first_final;
+}
View
49 ext/CTParser/http11_parser.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+
+#ifndef http11_parser_h
+#define http11_parser_h
+
+#include <sys/types.h>
+
+#if defined(_WIN32)
+#include <stddef.h>
+#endif
+
+typedef void (*element_cb)(void *data, const char *at, size_t length);
+typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
+
+typedef struct http_parser {
+ int cs;
+ size_t body_start;
+ int content_len;
+ size_t nread;
+ size_t mark;
+ size_t field_start;
+ size_t field_len;
+ size_t query_start;
+
+ void *data;
+
+ field_cb http_field;
+ element_cb request_method;
+ element_cb request_uri;
+ element_cb fragment;
+ element_cb request_path;
+ element_cb query_string;
+ element_cb http_version;
+ element_cb header_done;
+
+} http_parser;
+
+int http_parser_init(http_parser *parser);
+int http_parser_finish(http_parser *parser);
+size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
+int http_parser_has_error(http_parser *parser);
+int http_parser_is_finished(http_parser *parser);
+
+#define http_parser_nread(parser) (parser)->nread
+
+#endif
View
11 lib/control_tower.rb
@@ -0,0 +1,11 @@
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+require 'rubygems'
+require 'rack'
+require 'socket'
+require 'tempfile'
+require File.join(File.dirname(__FILE__), 'control_tower', 'rack_socket')
+require File.join(File.dirname(__FILE__), 'control_tower', 'server')
+
+
View
102 lib/control_tower/rack_socket.rb
@@ -0,0 +1,102 @@
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+require 'CTParser'
+
+module ControlTower
+ class RackSocket
+ READ_SIZE = 16 * 1024
+ RACK_VERSION = 'rack.version'.freeze
+ VERSION = [1,0].freeze
+
+ 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
+ # @request_queue = Dispatch::Queue.concurrent
+ #else
+ # @env['rack.multithread'] = false
+ # @request_queue = Dispatch::Queue.new('com.apple.ControlTower.rack_socket_queue')
+ #end
+ #@request_group = Dispatch::Group.new
+ end
+
+ def open
+ @status = :open
+ while (@status == :open)
+ connection = @socket.accept
+
+ # TODO -- Concurrency doesn't quite work yet...
+ #@request_group.dispatch(@request_queue) do
+ req_data = parse!(connection, prepare_environment)
+ data = @server.handle_request(req_data)
+ data.each do |chunk|
+ connection.write chunk
+ end
+ connection.close
+ #end
+ end
+ end
+
+ def close
+ @status = :close
+
+ # You get 30 seconds to empty the request queue and get outa here!
+ Dispatch::Source.timer(30, 0, 1, Dispatch::Queue.concurrent) do
+ puts "Timed out waiting for connections to close"
+ exit 1
+ end
+ #@request_group.wait
+ @socket.close
+ end
+
+
+ private
+
+ def prepare_environment
+ { 'rack.errors' => $stderr,
+ 'rack.input' => '',
+ 'rack.multiprocess' => false, # No multiprocess, yet...probably never
+ 'rack.run_once' => false,
+ RACK_VERSION => VERSION }
+ end
+
+ def parse!(connection, env)
+ parser = ::CTParser.new
+ data = ""
+ headers_done = false
+ content_length = 0
+
+ while (!headers_done || env['rack.input'].bytesize < content_length) do
+ select([connection], nil, nil, 1)
+ if headers_done
+ begin
+ data = connection.readpartial(READ_SIZE)
+ env['rack.input'] << data
+ rescue EOFError
+ break
+ end
+ else
+ data << connection.readpartial(READ_SIZE)
+ nread = parser.parseData(data, forEnvironment: env, startingAt: nread)
+ if parser.finished
+ headers_done = true
+ content_length = env['CONTENT_LENGTH'].to_i
+ end
+ end
+ end
+
+ # Rack says "Make that a StringIO!"
+ body = Tempfile.new('control-tower-request-body-')
+ body << env['rack.input']
+ body.rewind
+ env['rack.input'] = body
+ # Returning what we've got...
+ return env
+ end
+ end
+end
View
69 lib/control_tower/server.rb
@@ -0,0 +1,69 @@
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+module ControlTower
+ class Server
+ def initialize(app, options)
+ @app = app
+ parse_options(options)
+ @socket = RackSocket.new(@host, @port, self, @concurrent)
+ end
+
+ def start
+ trap 'INT' do
+ @socket.close
+ exit
+ end
+
+ # Ok, let the server do it's thing
+ @socket.open
+ end
+
+ def handle_request(env)
+ wrap_output(*@app.call(env))
+ end
+
+ private
+
+ def parse_options(opt)
+ @port = (opt[:port] || 8080).to_i
+ @host = opt[:host] || `hostname`.chomp
+ @concurrent = opt[:concurrent]
+ end
+
+ def wrap_output(status, headers, body)
+ # Unless somebody's already set it for us (or we don't need it), set the Content-Length
+ unless (status == -1 ||
+ (100..199).include?(status) ||
+ status == 204 ||
+ status == 304 ||
+ headers.has_key?("Content-Length"))
+ headers["Content-Length"] = if body.respond_to?(:each)
+ size = 0
+ body.each { |x| size += x.bytesize }
+ size
+ else
+ body.bytesize
+ end
+ end
+
+ # TODO -- We don't handle keep-alive connections yet
+ headers["Connection"] = 'close'
+
+ headers_out = headers.map do |header, value|
+ "#{header}: #{value}\r\n"
+ end.join
+
+ # Assemble our response...
+ chunks = ["HTTP/1.1 #{status}\r\n#{headers_out}\r\n"]
+ if body.respond_to?(:each)
+ body.each do |chunk|
+ chunks << chunk
+ end
+ else
+ chunks << body
+ end
+ chunks
+ end
+ end
+end
View
16 lib/rack/handler/control_tower.rb
@@ -0,0 +1,16 @@
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+require "control_tower"
+
+module Rack
+ module Handler
+ class ControlTower
+ def self.run(app, options={})
+ server = ::ControlTower::Server.new(app, options)
+ yield server if block_given?
+ server.start
+ end
+ end
+ end
+end
View
67 spec/ctparser_spec.rb
@@ -0,0 +1,67 @@
+# This file is covered by the Ruby license. See COPYING for more details.
+# Copyright (C) 2009-2010, Apple Inc. All rights reserved.
+
+require "bacon"
+require 'stringio'
+
+framework "CTParser"
+
+describe "Instantiating a new Parser" do
+ it "should return an object of class CTParser" do
+ parser = CTParser.new
+ parser.class.should == CTParser
+ end
+end
+
+describe "Parsing a minimal header" do
+ before do
+ @env = { 'rack.input' => StringIO.new }
+ @parser = CTParser.new
+ @header = "GET / HTTP/1.1\r\n\r\n"
+ @read = @parser.parseData(@header, forEnvironment:@env)
+ end
+
+ it "should parse the full header length" do
+ @read.should == @header.length
+ end
+
+ it "should finish" do
+ @parser.finished.should == 1
+ end
+
+ it "should not have any errors" do
+ @parser.errorCond.should == 0
+ end
+
+ it "should have read as many bytes as it read" do
+ @parser.nread.should == @read
+ end
+
+ it "should populate SERVER_PROTOCOL" do
+ @env['SERVER_PROTOCOL'].should == 'HTTP/1.1'
+ end
+
+ it "should populate PATH_INFO" do
+ @env['PATH_INFO'].should == "/"
+ end
+
+ it "should populate HTTP_VERSION" do
+ @env['HTTP_VERSION'].should == 'HTTP/1.1'
+ end
+
+ it "should populate GATEWAY_INTERFACE" do
+ @env['GATEWAY_INTERFACE'].should == 'CGI/1.2'
+ end
+
+ it "should populate REQUEST_METHOD" do
+ @env['REQUEST_METHOD'].should == 'GET'
+ end
+
+ it "should not have generated any fragments" do
+ @env['FRAGMENT'].should.be.nil
+ end
+
+ it "should not populate QUERY_STRING" do
+ @env['QUERY_STRING'].should.be.empty
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.