diff --git a/plugins/mono/mono_plugin.c b/plugins/mono/mono_plugin.c index 9735ea7e..a51c8ce3 100644 --- a/plugins/mono/mono_plugin.c +++ b/plugins/mono/mono_plugin.c @@ -14,7 +14,7 @@ (the --mono-key maps to DOCUMENT_ROOT by default) - 1) main-domain application + 1) static application --mono-app @@ -24,19 +24,16 @@ the application runs on the main domain - 2) dedicated-domain application (can we support threads ?) + 2) dynamic applications - --mono-domain-app + the app is created on demand using the specified key as the physicalDirectory - the app is created in a new domain and --mono-key is used like in --mono-app - 3) dynamic (mono-domain) apps - - this is the default if you do not preload apps. - - The mono-key is checked for already loaded-apps. If it is not available a - new ApplicationHost is created + TODO: + allows mounting apps under subpaths (currently all is mapped to "/") + Thanks to: + Robert Jordan for helping me understanding the ApplicationHost internals */ @@ -57,7 +54,7 @@ struct uwsgi_mono { // a lock for dynamic apps pthread_mutex_t lock_loader; - MonoDomain *domain; + MonoDomain *main_domain; MonoMethod *create_application_host; MonoClass *application_class; @@ -66,7 +63,8 @@ struct uwsgi_mono { void (*process_request)(MonoObject *, MonoException **); struct uwsgi_string_list *app; - struct uwsgi_string_list *domain_app; + struct uwsgi_string_list *shared_exec; + struct uwsgi_string_list *exec; } umono; @@ -75,51 +73,56 @@ struct uwsgi_option uwsgi_mono_options[] = { {"mono-app", required_argument, 0, "load a Mono asp.net app from the specified directory", uwsgi_opt_add_string_list, &umono.app, 0}, {"mono-gc-freq", required_argument, 0, "run the Mono GC every requests (default, run after every request)", uwsgi_opt_set_64bit, &umono.gc_freq, 0}, {"mono-key", required_argument, 0, "select the ApplicationHost based on the specified CGI var", uwsgi_opt_add_string_list, &umono.key, 0}, + {"mono-version", required_argument, 0, "set the Mono jit version", uwsgi_opt_set_str, &umono.version, 0}, + {"mono-config", required_argument, 0, "set the Mono config file", uwsgi_opt_set_str, &umono.config, 0}, + {"mono-assembly", required_argument, 0, "load the specified main assembly (default: uwsgi.dll)", uwsgi_opt_set_str, &umono.assembly_name, 0}, + {"mono-shared-exec", required_argument, 0, "exec the specified assembly after the main", uwsgi_opt_add_string_list, &umono.shared_exec, 0}, + {"mono-exec", required_argument, 0, "exec the specified assembly just before app loading", uwsgi_opt_add_string_list, &umono.exec, 0}, }; static MonoString *uwsgi_mono_method_GetFilePath(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->path_info, wsgi_req->path_info_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->path_info, wsgi_req->path_info_len); } static MonoString *uwsgi_mono_method_MapPath(MonoObject *this, MonoString *virtualPath) { // first we need to get the physical path and append the virtualPath to it struct wsgi_request *wsgi_req = current_wsgi_req(); struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id]; - char *path = uwsgi_concat3n(app->responder0, strlen(app->responder0), "/", 1, mono_string_to_utf8(virtualPath), mono_string_length(virtualPath)); - MonoString *ret = mono_string_new_len(umono.domain, path, strlen(path)); + char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, mono_string_to_utf8(virtualPath), mono_string_length(virtualPath)); + MonoString *ret = mono_string_new_len(mono_domain_get(), path, strlen(path)); free(path); return ret; } static MonoString *uwsgi_mono_method_GetQueryString(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->query_string, wsgi_req->query_string_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->query_string, wsgi_req->query_string_len); } static MonoString *uwsgi_mono_method_GetHttpVerbName(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->method, wsgi_req->method_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->method, wsgi_req->method_len); } static MonoString *uwsgi_mono_method_GetRawUrl(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->uri, wsgi_req->uri_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->uri, wsgi_req->uri_len); } static MonoString *uwsgi_mono_method_GetHttpVersion(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->protocol, wsgi_req->protocol_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->protocol, wsgi_req->protocol_len); } static MonoString *uwsgi_mono_method_GetUriPath(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->path_info, wsgi_req->path_info_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->path_info, wsgi_req->path_info_len); } static MonoString *uwsgi_mono_method_GetRemoteAddress(MonoObject *this) { struct wsgi_request *wsgi_req = current_wsgi_req(); - return mono_string_new_len(umono.domain, wsgi_req->remote_addr, wsgi_req->remote_addr_len); + return mono_string_new_len(mono_domain_get(), wsgi_req->remote_addr, wsgi_req->remote_addr_len); } static void uwsgi_mono_method_SendStatus(MonoObject *this, int code, MonoString *msg) { @@ -168,9 +171,9 @@ static MonoString *uwsgi_mono_method_GetHeaderByName(MonoObject *this, MonoStrin uint16_t rlen = 0; char *value = uwsgi_get_header(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen); if (value) { - return mono_string_new_len(umono.domain, value, rlen); + return mono_string_new_len(mono_domain_get(), value, rlen); } - return mono_string_new(umono.domain, ""); + return mono_string_new(mono_domain_get(), ""); } static MonoString *uwsgi_mono_method_GetServerVariable(MonoObject *this, MonoString *key) { @@ -178,9 +181,9 @@ static MonoString *uwsgi_mono_method_GetServerVariable(MonoObject *this, MonoStr uint16_t rlen = 0; char *value = uwsgi_get_var(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen); if (value) { - return mono_string_new_len(umono.domain, value, rlen); + return mono_string_new_len(mono_domain_get(), value, rlen); } - return mono_string_new(umono.domain, ""); + return mono_string_new(mono_domain_get(), ""); } static int uwsgi_mono_method_ReadEntityBody(MonoObject *this, MonoArray *byteArray, int len) { @@ -240,15 +243,15 @@ static int uwsgi_mono_init() { mono_config_parse(umono.config); - umono.domain = mono_jit_init_version("uwsgi", umono.version); - if (!umono.domain) { + umono.main_domain = mono_jit_init_version("uwsgi", umono.version); + if (!umono.main_domain) { uwsgi_log("unable to initialize Mono JIT\n"); exit(1); } uwsgi_log("Mono JIT initialized with version %s\n", umono.version); - MonoAssembly *assembly = mono_domain_assembly_open(umono.domain, umono.assembly_name); + MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, umono.assembly_name); if (!assembly) { uwsgi_log("unable to load \"%s\" in the Mono domain\n", umono.assembly_name); exit(1); @@ -257,11 +260,21 @@ static int uwsgi_mono_init() { uwsgi_mono_add_internal_calls(); MonoImage *image = mono_assembly_get_image(assembly); - uwsgi_log("image at %p\n", image); + if (!image) { + uwsgi_log("unable to get assembly image\n"); + exit(1); + } umono.application_class = mono_class_from_name(image, "uwsgi", "uWSGIApplication"); - uwsgi_log("class at %p\n", umono.application_class); + if (!umono.application_class) { + uwsgi_log("unable to get reference to class uwsgi.uWSGIApplication\n"); + exit(1); + } + MonoMethodDesc *desc = mono_method_desc_new("uwsgi.uWSGIApplication:.ctor(string,string)", 1); - uwsgi_log("desc at %p\n", desc); + if (!desc) { + uwsgi_log("unable to create description for uwsgi.uWSGIApplication:.ctor(string,string)\n"); + exit(1); + } umono.create_application_host = mono_method_desc_search_in_class(desc, umono.application_class); if (!umono.create_application_host) { uwsgi_log("unable to find constructor in uWSGIApplication class\n"); @@ -270,6 +283,10 @@ static int uwsgi_mono_init() { mono_method_desc_free(desc); desc = mono_method_desc_new("uwsgi.uWSGIApplication:Request()", 1); + if (!desc) { + uwsgi_log("unable to create description for uwsgi.uWSGIApplication:Request()\n"); + exit(1); + } MonoMethod *process_request = mono_method_desc_search_in_class(desc, umono.application_class); if (!process_request) { uwsgi_log("unable to find ProcessRequest method in uwsgi_host class\n"); @@ -279,6 +296,28 @@ static int uwsgi_mono_init() { umono.process_request = mono_method_get_unmanaged_thunk(process_request); + struct uwsgi_string_list *usl = umono.shared_exec; + while(usl) { + char *assembly_name = usl->value; + char *argv = ""; + char *colon = strchr(usl->value, ':'); + if (colon) { + argv = colon+1; + assembly_name = uwsgi_concat2n(usl->value, colon-usl->value, "", 0); + } + + MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, assembly_name); + if (!assembly) { + uwsgi_log("unable to load assembly \"%s\"\n", assembly_name); + exit(1); + } + mono_jit_exec(umono.main_domain, assembly, 1, &argv); + if (assembly_name != usl->value) { + free(assembly_name); + } + usl = usl->next; + } + return 0; } @@ -286,13 +325,13 @@ static int uwsgi_mono_create_app(char *key, uint16_t key_len, char *physicalDir, void *params[3]; params[2] = NULL; - params[0] = mono_string_new(umono.domain, "/"); - params[1] = mono_string_new_len(umono.domain, physicalDir, physicalDir_len); + params[0] = mono_string_new(mono_domain_get(), "/"); + params[1] = mono_string_new_len(mono_domain_get(), physicalDir, physicalDir_len); int id = uwsgi_apps_cnt; time_t now = uwsgi_now(); - MonoObject *appHost = mono_object_new(umono.domain, umono.application_class); + MonoObject *appHost = mono_object_new(mono_domain_get(), umono.application_class); if (!appHost) { uwsgi_log("unable to initialize asp.net ApplicationHost\n"); return -1; @@ -305,13 +344,12 @@ static int uwsgi_mono_create_app(char *key, uint16_t key_len, char *physicalDir, return -1; } - struct uwsgi_app *app = uwsgi_add_app(id, mono_plugin.modifier1, key, key_len, umono.domain, appHost); + struct uwsgi_app *app = uwsgi_add_app(id, mono_plugin.modifier1, key, key_len, uwsgi_concat2n(physicalDir, physicalDir_len, "", 0), appHost); app->started_at = now; app->startup_time = uwsgi_now() - now; - // use a copy of responder0 for physical_path - app->responder0 = uwsgi_concat2n(physicalDir, physicalDir_len, "", 0); - mono_gchandle_new (app->callable, 1); - uwsgi_log("Mono asp.net app %d (%.*s) loaded in %d seconds at %p (domain %p)\n", id, key_len, key, (int) app->startup_time, appHost, umono.domain); + // get a handlet to appHost + mono_gchandle_new(app->callable, 1); + uwsgi_log("Mono asp.net app %d (%.*s) loaded in %d seconds at %p (domain %p)\n", id, key_len, key, (int) app->startup_time, appHost, mono_domain_get()); // set it as default app if needed if (uwsgi.default_app == -1) { @@ -429,7 +467,7 @@ static void uwsgi_mono_after_request(struct wsgi_request *wsgi_req) { } static void uwsgi_mono_init_thread(int core_id) { - mono_thread_attach(umono.domain); + mono_thread_attach(umono.main_domain); // SIGPWR, SIGXCPU: these are used internally by the GC and pthreads. sigset_t smask; sigemptyset(&smask); diff --git a/plugins/mono/uwsgi.cs b/plugins/mono/uwsgi.cs index b20c7068..956afc9d 100644 --- a/plugins/mono/uwsgi.cs +++ b/plugins/mono/uwsgi.cs @@ -8,6 +8,11 @@ using System.Runtime.CompilerServices; namespace uwsgi { + class api { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void PostFork(); + } + class uWSGIRequest: HttpWorkerRequest { public override string GetAppPath() {