ext/http11/http11.c in mongrel-0.2.0 vs ext/http11/http11.c in mongrel-0.2.1

- old
+ new

@@ -9,20 +9,26 @@ static VALUE mMongrel; static VALUE cHttpParser; static VALUE cURIClassifier; static int id_handler_map; +static VALUE global_http_prefix; +static VALUE global_request_method; +static VALUE global_path_info; +static VALUE global_query_string; +static VALUE global_http_version; + void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) { char *ch, *end; VALUE req = (VALUE)data; - VALUE f = rb_str_new2("HTTP_"); VALUE v = rb_str_new(value, vlen); + VALUE f = rb_str_dup(global_http_prefix); + + f = rb_str_buf_cat(f, field, flen); - rb_str_buf_cat(f, field, flen); - for(ch = RSTRING(f)->ptr, end = ch + RSTRING(f)->len; ch < end; ch++) { if(*ch == '-') { *ch = '_'; } else { *ch = toupper(*ch); @@ -34,37 +40,33 @@ void request_method(void *data, const char *at, size_t length) { VALUE req = (VALUE)data; VALUE val = rb_str_new(at, length); - VALUE id = rb_str_new2("REQUEST_METHOD"); - rb_hash_aset(req, id, val); + rb_hash_aset(req, global_request_method, val); } void path_info(void *data, const char *at, size_t length) { VALUE req = (VALUE)data; VALUE val = rb_str_new(at, length); - VALUE id = rb_str_new2("PATH_INFO"); - rb_hash_aset(req, id, val); + rb_hash_aset(req, global_path_info, val); } void query_string(void *data, const char *at, size_t length) { VALUE req = (VALUE)data; VALUE val = rb_str_new(at, length); - VALUE id = rb_str_new2("QUERY_STRING"); - rb_hash_aset(req, id, val); + rb_hash_aset(req, global_query_string, val); } void http_version(void *data, const char *at, size_t length) { VALUE req = (VALUE)data; VALUE val = rb_str_new(at, length); - VALUE id = rb_str_new2("HTTP_VERSION"); - rb_hash_aset(req, id, val); + rb_hash_aset(req, global_http_version, val); } @@ -256,35 +258,20 @@ * trie to hold all of the URIs. It uses this to do an initial search for the a URI * prefix, and then to break the URI into SCRIPT_NAME and PATH_INFO portions. It actually * will do two searches most of the time in order to find the right handler for the * registered prefix portion. * - * Here's how it all works. Let's say you register "/blog" with a BlogHandler. Great. - * Now, someone goes to "/blog/zedsucks/ass". You want SCRIPT_NAME to be "/blog" and - * PATH_INFO to be "/zedsucks/ass". URIClassifier first does a TST search and comes - * up with a failure, but knows that the failure ended at the "/blog" part. So, that's - * the SCRIPT_NAME. It then tries a second search for just "/blog". If that comes back - * good then it sets the rest ("/zedsucks/ass") to the PATH_INFO and returns the BlogHandler. - * - * The optimal approach would be to not do the search twice, but the TST lib doesn't - * really support returning prefixes. Might not be hard to add later. - * - * The key though is that it will try to match the *longest* match it can. If you - * also register "/blog/zed" then the above URI will give SCRIPT_NAME="/blog/zed", - * PATH_INFO="sucks/ass". Probably not what you want, so your handler will need to - * do the 404 thing. - * - * Take a look at the postamble of example/tepee.rb to see how this is handled for - * Camping. */ VALUE URIClassifier_init(VALUE self) { VALUE hash; // we create an internal hash to protect stuff from the GC hash = rb_hash_new(); rb_ivar_set(self, id_handler_map, hash); + + return self; } /** * call-seq: @@ -354,22 +341,32 @@ * uc.resolve("/someuri/pathinfo") -> "/someuri", "/pathinfo", handler * uc.resolve("/notfound/orhere") -> nil, nil, nil * * Attempts to resolve either the whole URI or at the longest prefix, returning * the prefix (as script_info), path (as path_info), and registered handler - * (usually an HttpHandler). + * (usually an HttpHandler). If it doesn't find a handler registered at the longest + * match then it returns nil,nil,nil. * + * Because the resolver uses a trie you are able to register a handler at *any* character + * in the URI and it will be handled as long as it's the longest prefix. So, if you + * registered handler #1 at "/something/lik", and #2 at "/something/like/that", then a + * a search for "/something/like" would give you #1. A search for "/something/like/that/too" + * would give you #2. + * + * This is very powerful since it means you can also attach handlers to parts of the ; + * (semi-colon) separated path params, any part of the path, use off chars, anything really. + * It also means that it's very efficient to do this only taking as long as the URI has + * characters. + * * It expects strings. Don't try other string-line stuff yet. */ VALUE URIClassifier_resolve(VALUE self, VALUE uri) { void *handler = NULL; int pref_len = 0; struct tst *tst = NULL; VALUE result; - VALUE script_name; - VALUE path_info; unsigned char *uri_str = NULL; unsigned char *script_name_str = NULL; DATA_GET(self, struct tst, tst); uri_str = (unsigned char *)StringValueCStr(uri); @@ -377,44 +374,40 @@ handler = tst_search(uri_str, tst, &pref_len); // setup for multiple return values result = rb_ary_new(); - - if(handler == NULL) { - script_name = rb_str_substr (uri, 0, pref_len); - script_name_str = (unsigned char *)StringValueCStr(script_name); - - handler = tst_search(script_name_str, tst, NULL); - - if(handler == NULL) { - // didn't find the script name at all - rb_ary_push(result, Qnil); - rb_ary_push(result, Qnil); - rb_ary_push(result, Qnil); - return result; - } else { - // found a handler, setup the path info and we're good - path_info = rb_str_substr(uri, pref_len, RSTRING(uri)->len); - } + if(handler) { + rb_ary_push(result, rb_str_substr (uri, 0, pref_len)); + rb_ary_push(result, rb_str_substr(uri, pref_len, RSTRING(uri)->len)); + rb_ary_push(result, (VALUE)handler); } else { - // whole thing was found, so uri is the script name, path info empty - script_name = uri; - path_info = rb_str_new2(""); + // not found so push back nothing + rb_ary_push(result, Qnil); + rb_ary_push(result, Qnil); + rb_ary_push(result, Qnil); } - rb_ary_push(result, script_name); - rb_ary_push(result, path_info); - rb_ary_push(result, (VALUE)handler); return result; } void Init_http11() { mMongrel = rb_define_module("Mongrel"); id_handler_map = rb_intern("@handler_map"); + + global_http_prefix = rb_str_new2("HTTP_"); + rb_global_variable(&global_http_prefix); + global_request_method = rb_str_new2("REQUEST_METHOD"); + rb_global_variable(&global_request_method); + global_path_info = rb_str_new2("PATH_INFO"); + rb_global_variable(&global_path_info); + global_query_string = rb_str_new2("QUERY_STRING"); + rb_global_variable(&global_query_string); + global_http_version = rb_str_new2("HTTP_VERSION"); + rb_global_variable(&global_http_version); cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject); rb_define_alloc_func(cHttpParser, HttpParser_alloc); rb_define_method(cHttpParser, "initialize", HttpParser_init,0); rb_define_method(cHttpParser, "reset", HttpParser_reset,0);