[macruby-changes] [3975] ControlTower/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 29 13:51:17 PDT 2010


Revision: 3975
          http://trac.macosforge.org/projects/ruby/changeset/3975
Author:   lsansonetti at apple.com
Date:     2010-04-29 13:51:15 -0700 (Thu, 29 Apr 2010)
Log Message:
-----------
initial revision of ControlTower project

Added Paths:
-----------
    ControlTower/trunk/README
    ControlTower/trunk/Rakefile
    ControlTower/trunk/bin/
    ControlTower/trunk/bin/control_tower
    ControlTower/trunk/ext/
    ControlTower/trunk/ext/CTParser/
    ControlTower/trunk/ext/CTParser/CTParser.h
    ControlTower/trunk/ext/CTParser/CTParser.m
    ControlTower/trunk/ext/CTParser/extconf.rb
    ControlTower/trunk/ext/CTParser/http11_parser.c
    ControlTower/trunk/ext/CTParser/http11_parser.h
    ControlTower/trunk/lib/
    ControlTower/trunk/lib/control_tower/
    ControlTower/trunk/lib/control_tower/rack_socket.rb
    ControlTower/trunk/lib/control_tower/server.rb
    ControlTower/trunk/lib/control_tower.rb
    ControlTower/trunk/lib/rack/
    ControlTower/trunk/lib/rack/handler/
    ControlTower/trunk/lib/rack/handler/control_tower.rb
    ControlTower/trunk/spec/
    ControlTower/trunk/spec/ctparser_spec.rb

Added: ControlTower/trunk/README
===================================================================
--- ControlTower/trunk/README	                        (rev 0)
+++ ControlTower/trunk/README	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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.

Added: ControlTower/trunk/Rakefile
===================================================================
--- ControlTower/trunk/Rakefile	                        (rev 0)
+++ ControlTower/trunk/Rakefile	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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
+

Added: ControlTower/trunk/bin/control_tower
===================================================================
--- ControlTower/trunk/bin/control_tower	                        (rev 0)
+++ ControlTower/trunk/bin/control_tower	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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
+ at 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

Added: ControlTower/trunk/ext/CTParser/CTParser.h
===================================================================
--- ControlTower/trunk/ext/CTParser/CTParser.h	                        (rev 0)
+++ ControlTower/trunk/ext/CTParser/CTParser.h	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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"
+
+ at interface CTParser : NSObject
+{
+  http_parser *_parser;
+  NSString *_body;
+}
+
+ at 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;
+
+ at end
+
+// Describe enough of the StringIO interface to write to one
+ at interface RBStringIO : NSObject
+- (void)write:(NSString *)dataBuf;
+ at end

Added: ControlTower/trunk/ext/CTParser/CTParser.m
===================================================================
--- ControlTower/trunk/ext/CTParser/CTParser.m	                        (rev 0)
+++ ControlTower/trunk/ext/CTParser/CTParser.m	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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;
+}
+
+ at implementation CTParser
+
+ at 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];
+}
+
+ at 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.
+}

Added: ControlTower/trunk/ext/CTParser/extconf.rb
===================================================================
--- ControlTower/trunk/ext/CTParser/extconf.rb	                        (rev 0)
+++ ControlTower/trunk/ext/CTParser/extconf.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -0,0 +1,3 @@
+require 'mkmf'
+$CFLAGS << ' -fobjc-gc -g '
+create_makefile("CTParser")

Added: ControlTower/trunk/ext/CTParser/http11_parser.c
===================================================================
--- ControlTower/trunk/ext/CTParser/http11_parser.c	                        (rev 0)
+++ ControlTower/trunk/ext/CTParser/http11_parser.c	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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;
+}

Added: ControlTower/trunk/ext/CTParser/http11_parser.h
===================================================================
--- ControlTower/trunk/ext/CTParser/http11_parser.h	                        (rev 0)
+++ ControlTower/trunk/ext/CTParser/http11_parser.h	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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

Added: ControlTower/trunk/lib/control_tower/rack_socket.rb
===================================================================
--- ControlTower/trunk/lib/control_tower/rack_socket.rb	                        (rev 0)
+++ ControlTower/trunk/lib/control_tower/rack_socket.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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

Added: ControlTower/trunk/lib/control_tower/server.rb
===================================================================
--- ControlTower/trunk/lib/control_tower/server.rb	                        (rev 0)
+++ ControlTower/trunk/lib/control_tower/server.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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

Added: ControlTower/trunk/lib/control_tower.rb
===================================================================
--- ControlTower/trunk/lib/control_tower.rb	                        (rev 0)
+++ ControlTower/trunk/lib/control_tower.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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')
+
+

Added: ControlTower/trunk/lib/rack/handler/control_tower.rb
===================================================================
--- ControlTower/trunk/lib/rack/handler/control_tower.rb	                        (rev 0)
+++ ControlTower/trunk/lib/rack/handler/control_tower.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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

Added: ControlTower/trunk/spec/ctparser_spec.rb
===================================================================
--- ControlTower/trunk/spec/ctparser_spec.rb	                        (rev 0)
+++ ControlTower/trunk/spec/ctparser_spec.rb	2010-04-29 20:51:15 UTC (rev 3975)
@@ -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


Property changes on: ControlTower/trunk/spec/ctparser_spec.rb
___________________________________________________________________
Added: svn:executable
   + *
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100429/f362d93b/attachment-0001.html>


More information about the macruby-changes mailing list