1 #include <uwsgi.h>
2 #include <mono/jit/jit.h>
3 #include <mono/metadata/mono-config.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/threads.h>
6 #include <mono/metadata/debug-helpers.h>
7 #include <mono/metadata/mono-gc.h>
8 #include <mono/metadata/exception.h>
9
10 /*
11
12 Mono ASP.NET plugin
13
14 there are various mode of operation (based on security needs)
15
16 (the --mono-key maps to DOCUMENT_ROOT by default)
17
18 1) static application
19
20 --mono-app <directory>
21
22 will create an ApplicationHost on the specified <directory>
23
24 the --mono-key will be searched for that directory
25
26 the application runs on the main domain
27
28 2) dynamic applications
29
30 the app is created on demand using the specified key as the physicalDirectory
31
32
33 TODO:
34 allows mounting apps under subpaths (currently all is mapped to "/")
35
36 Thanks to:
37 Robert Jordan for helping me understanding the ApplicationHost internals
38
39 */
40
41 extern struct uwsgi_server uwsgi;
42 struct uwsgi_plugin mono_plugin;
43
44 struct uwsgi_mono {
45
46 char *config;
47 char *version;
48 char *assembly_name ;
49
50 struct uwsgi_string_list *key;
51 struct uwsgi_string_list *index;
52
53 // GC frequency
54 uint64_t gc_freq;
55
56 // a lock for dynamic apps
57 pthread_mutex_t lock_loader;
58
59 MonoDomain *main_domain;
60 MonoMethod *create_application_host;
61
62 MonoClass *application_class;
63 MonoClass *api_class;
64
65 MonoClass *byte_class;
66
67 MonoClassField *filepath;
68
69 // thunk
70 void (*process_request)(MonoObject *, MonoException **);
71
72 struct uwsgi_string_list *app;
73 struct uwsgi_string_list *exec;
74
75 } umono;
76
77 struct uwsgi_option uwsgi_mono_options[] = {
78
79 {"mono-app", required_argument, 0, "load a Mono asp.net app from the specified directory", uwsgi_opt_add_string_list, &umono.app, 0},
80 {"mono-gc-freq", required_argument, 0, "run the Mono GC every <n> requests (default: run after every request)", uwsgi_opt_set_64bit, &umono.gc_freq, 0},
81 {"mono-key", required_argument, 0, "select the ApplicationHost based on the specified CGI var", uwsgi_opt_add_string_list, &umono.key, 0},
82 {"mono-version", required_argument, 0, "set the Mono jit version", uwsgi_opt_set_str, &umono.version, 0},
83 {"mono-config", required_argument, 0, "set the Mono config file", uwsgi_opt_set_str, &umono.config, 0},
84 {"mono-assembly", required_argument, 0, "load the specified main assembly (default: uwsgi.dll)", uwsgi_opt_set_str, &umono.assembly_name, 0},
85 {"mono-exec", required_argument, 0, "exec the specified assembly just before app loading", uwsgi_opt_add_string_list, &umono.exec, 0},
86 {"mono-index", required_argument, 0, "add an asp.net index file", uwsgi_opt_add_string_list, &umono.index, 0},
87 {0, 0, 0, 0, 0, 0, 0},
88 };
89
uwsgi_mono_method_GetFilePath(MonoObject * this)90 static MonoString *uwsgi_mono_method_GetFilePath(MonoObject *this) {
91 MonoString *ret = NULL;
92 // cache it !!!
93 MonoObject *filepath = mono_field_get_value_object(mono_domain_get(), umono.filepath, this);
94 if (filepath) {
95 return (MonoString *) filepath;
96 }
97 struct wsgi_request *wsgi_req = current_wsgi_req();
98 struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
99 char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, wsgi_req->path_info, wsgi_req->path_info_len);
100 size_t path_len = strlen(app->interpreter) + 1 + wsgi_req->path_info_len;
101
102 if (!uwsgi_file_exists(path)) {
103 free(path);
104 goto simple;
105 }
106
107 if (uwsgi_is_dir(path)) {
108 struct uwsgi_string_list *usl = umono.index;
109 while(usl) {
110 char *index = uwsgi_concat3n(path, path_len, "/", 1 , usl->value, usl->len);
111 if (uwsgi_file_exists(index)) {
112 ret = mono_string_new(mono_domain_get(), index + strlen(app->interpreter));
113 free(path);
114 free(index);
115 mono_field_set_value(this, umono.filepath, ret);
116 return ret;
117 }
118 free(index);
119 usl = usl->next;
120 }
121 }
122 free(path);
123 simple:
124 ret = mono_string_new_len(mono_domain_get(), wsgi_req->path_info, wsgi_req->path_info_len);
125 mono_field_set_value(this, umono.filepath, ret);
126 return ret;
127 }
128
uwsgi_mono_method_GetUriPath(MonoObject * this)129 static MonoString *uwsgi_mono_method_GetUriPath(MonoObject *this) {
130 return uwsgi_mono_method_GetFilePath(this);
131 }
132
uwsgi_mono_method_MapPath(MonoObject * this,MonoString * virtualPath)133 static MonoString *uwsgi_mono_method_MapPath(MonoObject *this, MonoString *virtualPath) {
134 struct wsgi_request *wsgi_req = current_wsgi_req();
135 struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
136 char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, mono_string_to_utf8(virtualPath), mono_string_length(virtualPath));
137 MonoString *ret = mono_string_new_len(mono_domain_get(), path, strlen(path));
138 free(path);
139 return ret;
140 }
141
uwsgi_mono_method_GetQueryString(MonoObject * this)142 static MonoString *uwsgi_mono_method_GetQueryString(MonoObject *this) {
143 struct wsgi_request *wsgi_req = current_wsgi_req();
144 return mono_string_new_len(mono_domain_get(), wsgi_req->query_string, wsgi_req->query_string_len);
145 }
146
uwsgi_mono_method_GetHttpVerbName(MonoObject * this)147 static MonoString *uwsgi_mono_method_GetHttpVerbName(MonoObject *this) {
148 struct wsgi_request *wsgi_req = current_wsgi_req();
149 return mono_string_new_len(mono_domain_get(), wsgi_req->method, wsgi_req->method_len);
150 }
151
uwsgi_mono_method_GetRawUrl(MonoObject * this)152 static MonoString *uwsgi_mono_method_GetRawUrl(MonoObject *this) {
153 struct wsgi_request *wsgi_req = current_wsgi_req();
154 return mono_string_new_len(mono_domain_get(), wsgi_req->uri, wsgi_req->uri_len);
155 }
156
uwsgi_mono_method_GetHttpVersion(MonoObject * this)157 static MonoString *uwsgi_mono_method_GetHttpVersion(MonoObject *this) {
158 struct wsgi_request *wsgi_req = current_wsgi_req();
159 return mono_string_new_len(mono_domain_get(), wsgi_req->protocol, wsgi_req->protocol_len);
160 }
161
uwsgi_mono_method_GetRemoteAddress(MonoObject * this)162 static MonoString *uwsgi_mono_method_GetRemoteAddress(MonoObject *this) {
163 struct wsgi_request *wsgi_req = current_wsgi_req();
164 return mono_string_new_len(mono_domain_get(), wsgi_req->remote_addr, wsgi_req->remote_addr_len);
165 }
166
uwsgi_mono_method_SendStatus(MonoObject * this,int code,MonoString * msg)167 static void uwsgi_mono_method_SendStatus(MonoObject *this, int code, MonoString *msg) {
168 struct wsgi_request *wsgi_req = current_wsgi_req();
169 char status_code[4];
170 uwsgi_num2str2n(code, status_code, 4);
171 char *status_line = uwsgi_concat3n(status_code, 3, " ", 1, mono_string_to_utf8(msg), mono_string_length(msg));
172 uwsgi_response_prepare_headers(wsgi_req, status_line, 4 + mono_string_length(msg));
173 free(status_line);
174 }
175
uwsgi_mono_method_SendUnknownResponseHeader(MonoObject * this,MonoString * key,MonoString * value)176 static void uwsgi_mono_method_SendUnknownResponseHeader(MonoObject *this, MonoString *key, MonoString *value) {
177 struct wsgi_request *wsgi_req = current_wsgi_req();
178 uwsgi_response_add_header(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), mono_string_to_utf8(value), mono_string_length(value));
179 }
180
uwsgi_mono_method_SendResponseFromMemory(MonoObject * this,MonoArray * byteArray,int len)181 static void uwsgi_mono_method_SendResponseFromMemory(MonoObject *this, MonoArray *byteArray, int len) {
182 struct wsgi_request *wsgi_req = current_wsgi_req();
183 uwsgi_response_write_body_do(wsgi_req, mono_array_addr(byteArray, char, 0), len);
184 }
185
uwsgi_mono_method_FlushResponse(MonoObject * this,int is_final)186 static void uwsgi_mono_method_FlushResponse(MonoObject *this, int is_final) {
187 struct wsgi_request *wsgi_req = current_wsgi_req();
188 uwsgi_response_write_body_do(wsgi_req, "", 0);
189 }
190
uwsgi_mono_method_SendResponseFromFd(MonoObject * this,int fd,long offset,long len)191 static void uwsgi_mono_method_SendResponseFromFd(MonoObject *this, int fd, long offset, long len) {
192 struct wsgi_request *wsgi_req = current_wsgi_req();
193 wsgi_req->sendfile_fd = fd;
194 if (fd >= 0) {
195 uwsgi_response_sendfile_do(wsgi_req, fd, offset, len);
196 }
197 wsgi_req->sendfile_fd = -1;
198 }
199
uwsgi_mono_method_SendResponseFromFile(MonoObject * this,MonoString * filename,long offset,long len)200 static void uwsgi_mono_method_SendResponseFromFile(MonoObject *this, MonoString *filename, long offset, long len) {
201 struct wsgi_request *wsgi_req = current_wsgi_req();
202 int fd = open(mono_string_to_utf8(filename), O_RDONLY);
203 if (fd >= 0) {
204 uwsgi_response_sendfile_do(wsgi_req, fd, offset, len);
205 }
206 }
207
uwsgi_mono_method_GetHeaderByName(MonoObject * this,MonoString * key)208 static MonoString *uwsgi_mono_method_GetHeaderByName(MonoObject *this, MonoString *key) {
209 struct wsgi_request *wsgi_req = current_wsgi_req();
210 uint16_t rlen = 0;
211 char *value = uwsgi_get_header(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen);
212 if (value) {
213 return mono_string_new_len(mono_domain_get(), value, rlen);
214 }
215 return mono_string_new(mono_domain_get(), "");
216 }
217
uwsgi_mono_method_GetServerVariable(MonoObject * this,MonoString * key)218 static MonoString *uwsgi_mono_method_GetServerVariable(MonoObject *this, MonoString *key) {
219 struct wsgi_request *wsgi_req = current_wsgi_req();
220 uint16_t rlen = 0;
221 char *value = uwsgi_get_var(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen);
222 if (value) {
223 return mono_string_new_len(mono_domain_get(), value, rlen);
224 }
225 return mono_string_new(mono_domain_get(), "");
226 }
227
uwsgi_mono_method_ReadEntityBody(MonoObject * this,MonoArray * byteArray,int len)228 static int uwsgi_mono_method_ReadEntityBody(MonoObject *this, MonoArray *byteArray, int len) {
229 struct wsgi_request *wsgi_req = current_wsgi_req();
230 char *buf = mono_array_addr(byteArray, char, 0);
231 ssize_t rlen = 0;
232 char *chunk = uwsgi_request_body_read(wsgi_req, len, &rlen);
233 if (chunk == uwsgi.empty) {
234 return 0;
235 }
236 if (chunk) {
237 memcpy(buf, chunk, rlen);
238 return rlen;
239 }
240 return -1;
241 }
242
uwsgi_mono_method_GetTotalEntityBodyLength(MonoObject * this)243 static int uwsgi_mono_method_GetTotalEntityBodyLength(MonoObject *this) {
244 struct wsgi_request *wsgi_req = current_wsgi_req();
245 return wsgi_req->post_cl;
246 }
247
uwsgi_mono_method_api_RegisterSignal(int signum,MonoString * target,MonoObject * func)248 static void uwsgi_mono_method_api_RegisterSignal(int signum, MonoString *target, MonoObject *func) {
249 mono_gchandle_new(func, 1);
250 if (uwsgi_register_signal(signum, mono_string_to_utf8(target), func, mono_plugin.modifier1)) {
251 mono_raise_exception(mono_get_exception_invalid_operation("unable to register signal handler"));
252 }
253 }
254
uwsgi_mono_method_api_Signal(int signum)255 static void uwsgi_mono_method_api_Signal(int signum) {
256 uwsgi_signal_send(uwsgi.signal_socket, signum);
257 }
258
uwsgi_mono_method_api_WorkerId()259 static int uwsgi_mono_method_api_WorkerId() {
260 return uwsgi.mywid;
261 }
262
uwsgi_mono_method_api_CacheGet(MonoString * key,MonoString * cache)263 static MonoArray *uwsgi_mono_method_api_CacheGet(MonoString *key, MonoString *cache) {
264 char *c_key = mono_string_to_utf8(key);
265 uint16_t c_keylen = mono_string_length(key);
266 char *c_cache = NULL;
267 if (cache) {
268 c_cache = mono_string_to_utf8(cache);
269 }
270 uint64_t vallen = 0 ;
271 char *value = uwsgi_cache_magic_get(c_key, c_keylen, &vallen, NULL, c_cache);
272 if (value) {
273 MonoArray *ret = mono_array_new(mono_domain_get(), umono.byte_class, vallen);
274 char *buf = mono_array_addr(ret, char, 0);
275 memcpy(buf, value, vallen);
276 free(value);
277 return ret;
278 }
279
280 return NULL;
281 }
282
uwsgi_mono_add_internal_calls()283 static void uwsgi_mono_add_internal_calls() {
284 // uWSGIRequest
285 mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromMemory", uwsgi_mono_method_SendResponseFromMemory);
286 mono_add_internal_call("uwsgi.uWSGIRequest::SendStatus", uwsgi_mono_method_SendStatus);
287 mono_add_internal_call("uwsgi.uWSGIRequest::SendUnknownResponseHeader", uwsgi_mono_method_SendUnknownResponseHeader);
288 mono_add_internal_call("uwsgi.uWSGIRequest::FlushResponse", uwsgi_mono_method_FlushResponse);
289 mono_add_internal_call("uwsgi.uWSGIRequest::GetQueryString", uwsgi_mono_method_GetQueryString);
290 mono_add_internal_call("uwsgi.uWSGIRequest::MapPath", uwsgi_mono_method_MapPath);
291 mono_add_internal_call("uwsgi.uWSGIRequest::GetHttpVerbName", uwsgi_mono_method_GetHttpVerbName);
292 mono_add_internal_call("uwsgi.uWSGIRequest::GetRawUrl", uwsgi_mono_method_GetRawUrl);
293 mono_add_internal_call("uwsgi.uWSGIRequest::GetFilePath", uwsgi_mono_method_GetFilePath);
294 mono_add_internal_call("uwsgi.uWSGIRequest::GetUriPath", uwsgi_mono_method_GetUriPath);
295 mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromFile", uwsgi_mono_method_SendResponseFromFile);
296 mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromFd", uwsgi_mono_method_SendResponseFromFd);
297 mono_add_internal_call("uwsgi.uWSGIRequest::GetHeaderByName", uwsgi_mono_method_GetHeaderByName);
298 mono_add_internal_call("uwsgi.uWSGIRequest::ReadEntityBody", uwsgi_mono_method_ReadEntityBody);
299 mono_add_internal_call("uwsgi.uWSGIRequest::GetTotalEntityBodyLength", uwsgi_mono_method_GetTotalEntityBodyLength);
300 mono_add_internal_call("uwsgi.uWSGIRequest::GetHttpVersion", uwsgi_mono_method_GetHttpVersion);
301 mono_add_internal_call("uwsgi.uWSGIRequest::GetServerVariable", uwsgi_mono_method_GetServerVariable);
302 mono_add_internal_call("uwsgi.uWSGIRequest::GetRemoteAddress", uwsgi_mono_method_GetRemoteAddress);
303
304 // api
305 mono_add_internal_call("uwsgi.api::Signal", uwsgi_mono_method_api_Signal);
306 mono_add_internal_call("uwsgi.api::WorkerId", uwsgi_mono_method_api_WorkerId);
307 mono_add_internal_call("uwsgi.api::RegisterSignal", uwsgi_mono_method_api_RegisterSignal);
308 mono_add_internal_call("uwsgi.api::CacheGet", uwsgi_mono_method_api_CacheGet);
309 }
310
uwsgi_mono_init()311 static int uwsgi_mono_init() {
312
313 if (!umono.version) {
314 umono.version = "v4.0.30319";
315 }
316
317 if (!umono.assembly_name) {
318 umono.assembly_name = "uwsgi.dll";
319 }
320
321 if (!umono.gc_freq) {
322 umono.gc_freq = 1;
323 }
324
325 return 0;
326 }
327
328
uwsgi_mono_create_jit()329 static void uwsgi_mono_create_jit() {
330
331
332 mono_config_parse(umono.config);
333
334 umono.main_domain = mono_jit_init_version("uwsgi", umono.version);
335 if (!umono.main_domain) {
336 uwsgi_log("unable to initialize Mono JIT\n");
337 exit(1);
338 }
339
340 uwsgi_log("Mono JIT initialized on worker %d with version %s\n", uwsgi.mywid, umono.version);
341
342 MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, umono.assembly_name);
343 if (!assembly) {
344 uwsgi_log("%s not found trying in global gac...\n", umono.assembly_name);
345 assembly = mono_assembly_load_with_partial_name(umono.assembly_name, NULL);
346 if (!assembly) {
347 if (!strcmp("uwsgi.dll", umono.assembly_name)) {
348 assembly = mono_assembly_load_with_partial_name("uwsgi", NULL);
349 }
350 }
351 }
352
353 if (!assembly) {
354 uwsgi_log("unable to load \"%s\" in the Mono domain\n", umono.assembly_name);
355 exit(1);
356 }
357
358 uwsgi_mono_add_internal_calls();
359
360 MonoImage *image = mono_assembly_get_image(assembly);
361 if (!image) {
362 uwsgi_log("unable to get assembly image\n");
363 exit(1);
364 }
365 umono.application_class = mono_class_from_name(image, "uwsgi", "uWSGIApplication");
366 if (!umono.application_class) {
367 uwsgi_log("unable to get reference to class uwsgi.uWSGIApplication\n");
368 exit(1);
369 }
370
371 umono.byte_class = mono_class_from_name(mono_get_corlib(), "System", "Byte");
372 if (!umono.byte_class) {
373 uwsgi_log("unable to get reference to class System.Byte\n");
374 exit(1);
375 }
376
377 MonoClass *urequest = mono_class_from_name(image, "uwsgi", "uWSGIRequest");
378 if (!urequest) {
379 uwsgi_log("unable to get reference to class uwsgi.uWSGIRequest\n");
380 exit(1);
381 }
382
383 umono.filepath = mono_class_get_field_from_name(urequest, "filepath");
384 if (!umono.filepath) {
385 uwsgi_log("unable to get reference to field uwsgi.uWSGIRequest.filepath\n");
386 }
387
388 umono.api_class = mono_class_from_name(image, "uwsgi", "api");
389 if (!umono.api_class) {
390 uwsgi_log("unable to get reference to class uwsgi.api\n");
391 exit(1);
392 }
393
394 MonoMethodDesc *desc = mono_method_desc_new("uwsgi.uWSGIApplication:.ctor(string,string)", 1);
395 if (!desc) {
396 uwsgi_log("unable to create description for uwsgi.uWSGIApplication:.ctor(string,string)\n");
397 exit(1);
398 }
399 umono.create_application_host = mono_method_desc_search_in_class(desc, umono.application_class);
400 if (!umono.create_application_host) {
401 uwsgi_log("unable to find constructor in uWSGIApplication class\n");
402 exit(1);
403 }
404 mono_method_desc_free(desc);
405
406 desc = mono_method_desc_new("uwsgi.uWSGIApplication:Request()", 1);
407 if (!desc) {
408 uwsgi_log("unable to create description for uwsgi.uWSGIApplication:Request()\n");
409 exit(1);
410 }
411 MonoMethod *process_request = mono_method_desc_search_in_class(desc, umono.application_class);
412 if (!process_request) {
413 uwsgi_log("unable to find ProcessRequest method in uwsgi_host class\n");
414 exit(1);
415 }
416 mono_method_desc_free(desc);
417
418 umono.process_request = mono_method_get_unmanaged_thunk(process_request);
419
420 struct uwsgi_string_list *usl = umono.exec;
421 while(usl) {
422 char *assembly_name = usl->value;
423 char *argv = "";
424 char *colon = strchr(usl->value, ':');
425 if (colon) {
426 argv = colon+1;
427 assembly_name = uwsgi_concat2n(usl->value, colon-usl->value, "", 0);
428 }
429
430 MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, assembly_name);
431 if (!assembly) {
432 uwsgi_log("unable to load assembly \"%s\"\n", assembly_name);
433 exit(1);
434 }
435 mono_jit_exec(umono.main_domain, assembly, 1, &argv);
436 if (assembly_name != usl->value) {
437 free(assembly_name);
438 }
439 usl = usl->next;
440 }
441
442 }
443
uwsgi_mono_create_app(char * key,uint16_t key_len,char * physicalDir,uint16_t physicalDir_len,int new_domain)444 static int uwsgi_mono_create_app(char *key, uint16_t key_len, char *physicalDir, uint16_t physicalDir_len, int new_domain) {
445 void *params[3];
446 params[2] = NULL;
447
448 params[0] = mono_string_new(mono_domain_get(), "/");
449 params[1] = mono_string_new_len(mono_domain_get(), physicalDir, physicalDir_len);
450
451 int id = uwsgi_apps_cnt;
452 time_t now = uwsgi_now();
453
454 MonoObject *appHost = mono_object_new(mono_domain_get(), umono.application_class);
455 if (!appHost) {
456 uwsgi_log("unable to initialize asp.net ApplicationHost\n");
457 return -1;
458 }
459
460 MonoObject *exc = NULL;
461 mono_runtime_invoke(umono.create_application_host, appHost, params, &exc);
462 if (exc) {
463 mono_print_unhandled_exception(exc);
464 return -1;
465 }
466
467 struct uwsgi_app *app = uwsgi_add_app(id, mono_plugin.modifier1, key, key_len, uwsgi_concat2n(physicalDir, physicalDir_len, "", 0), appHost);
468 app->started_at = now;
469 app->startup_time = uwsgi_now() - now;
470 // get a handlet to appHost
471 mono_gchandle_new(app->callable, 1);
472 uwsgi_log("Mono asp.net app %d (%.*s) loaded in %d seconds at %p (worker %d)\n", id, key_len, key, (int) app->startup_time, appHost, uwsgi.mywid);
473
474 // set it as default app if needed
475 if (uwsgi.default_app == -1) {
476 uwsgi.default_app = id;
477 }
478
479 return id;
480 }
481
uwsgi_mono_init_apps()482 static void uwsgi_mono_init_apps() {
483
484 if (!umono.main_domain) {
485 uwsgi_mono_create_jit();
486 }
487
488 struct uwsgi_string_list *usl = umono.app;
489
490 while(usl) {
491
492 char *mountpoint = usl->value;
493 uint8_t mountpoint_len = usl->len;
494 char *physicalDir = mountpoint;
495 uint8_t physicalDir_len = mountpoint_len;
496
497 char *equal = strchr(mountpoint, '=');
498 if (equal) {
499 physicalDir = equal+1;
500 physicalDir_len = strlen(physicalDir);
501 // ensure NULL char is at end (just for being backward compatible)
502 mountpoint = uwsgi_concat2n(mountpoint, equal - mountpoint, "", 0);
503 mountpoint_len = strlen(mountpoint);
504 }
505
506 int id = uwsgi_mono_create_app(mountpoint, mountpoint_len, physicalDir, physicalDir_len, 0);
507 if (id == -1) {
508 exit(1);
509 }
510 uwsgi_emulate_cow_for_apps(id);
511
512 usl = usl->next;
513 }
514 }
515
uwsgi_mono_request(struct wsgi_request * wsgi_req)516 static int uwsgi_mono_request(struct wsgi_request *wsgi_req) {
517
518 /* Standard ASP.NET request */
519 if (!wsgi_req->uh->pktsize) {
520 uwsgi_log("Empty Mono/ASP.NET request. skip.\n");
521 return -1;
522 }
523
524 if (uwsgi_parse_vars(wsgi_req)) {
525 return -1;
526 }
527
528 char *key = wsgi_req->document_root;
529 uint16_t key_len = wsgi_req->document_root_len;
530
531 struct uwsgi_string_list *usl = umono.key;
532 while(usl) {
533 key = uwsgi_get_var(wsgi_req, usl->value, usl->len, &key_len);
534 if (key) break;
535 usl = usl->next;
536 }
537
538 // avoid unexpected values
539 if (key == NULL) {
540 key = "";
541 key_len = 0;
542 }
543
544 wsgi_req->app_id = uwsgi_get_app_id(NULL, key, key_len, mono_plugin.modifier1);
545 // if it is -1, try to load a dynamic app
546 if (wsgi_req->app_id == -1 && key_len > 0) {
547 if (uwsgi.threads > 1) {
548 pthread_mutex_lock(&umono.lock_loader);
549 }
550
551 // check if in the mean time, something changed
552 wsgi_req->app_id = uwsgi_get_app_id(NULL, key, key_len, mono_plugin.modifier1);
553
554 if (wsgi_req->app_id == -1) {
555 wsgi_req->app_id = uwsgi_mono_create_app(key, key_len, key, key_len, 0);
556 }
557
558 if (uwsgi.threads > 1) {
559 pthread_mutex_unlock(&umono.lock_loader);
560 }
561 }
562
563
564 if (wsgi_req->app_id == -1) {
565 if (!uwsgi.no_default_app && uwsgi.default_app > -1 && uwsgi_apps[uwsgi.default_app].modifier1 == mono_plugin.modifier1) {
566 wsgi_req->app_id = uwsgi.default_app;
567 }
568 else {
569 uwsgi_500(wsgi_req);
570 uwsgi_log("--- unable to find Mono/ASP.NET application ---\n");
571 // nothing to clear/free
572 return UWSGI_OK;
573 }
574 }
575
576 struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
577 app->requests++;
578
579 // check for directory without slash
580 char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, wsgi_req->path_info, wsgi_req->path_info_len);
581 size_t path_len = strlen(app->interpreter) + 1 + wsgi_req->path_info_len;
582
583 if (uwsgi_is_dir(path) && path[path_len-1] != '/') {
584 free(path);
585 uwsgi_redirect_to_slash(wsgi_req);
586 return UWSGI_OK;
587 }
588 free(path);
589
590 MonoException *exc = NULL;
591
592 umono.process_request(app->callable, &exc);
593
594 if (exc) {
595 mono_print_unhandled_exception((MonoObject *)exc);
596 }
597
598 if ( uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests % umono.gc_freq == 0) {
599 mono_gc_collect (mono_gc_max_generation());
600 }
601
602 return UWSGI_OK;
603 }
604
uwsgi_mono_after_request(struct wsgi_request * wsgi_req)605 static void uwsgi_mono_after_request(struct wsgi_request *wsgi_req) {
606 log_request(wsgi_req);
607 }
608
uwsgi_mono_init_thread(int core_id)609 static void uwsgi_mono_init_thread(int core_id) {
610 mono_thread_attach(umono.main_domain);
611 // SIGPWR, SIGXCPU: these are used internally by the GC and pthreads.
612 sigset_t smask;
613 sigemptyset(&smask);
614 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
615 sigaddset(&smask, SIGXFSZ);
616 #else
617 sigaddset(&smask, SIGPWR);
618 #endif
619 if (sigprocmask(SIG_UNBLOCK, &smask, NULL)) {
620 uwsgi_error("uwsgi_mono_init_thread()/sigprocmask()");
621 }
622 }
623
uwsgi_mono_pthread_prepare(void)624 static void uwsgi_mono_pthread_prepare(void) {
625 pthread_mutex_lock(&umono.lock_loader);
626 }
627
uwsgi_mono_pthread_parent(void)628 static void uwsgi_mono_pthread_parent(void) {
629 pthread_mutex_unlock(&umono.lock_loader);
630 }
631
uwsgi_mono_pthread_child(void)632 static void uwsgi_mono_pthread_child(void) {
633 pthread_mutex_init(&umono.lock_loader, NULL);
634 }
635
636
uwsgi_mono_enable_threads(void)637 static void uwsgi_mono_enable_threads(void) {
638 pthread_mutex_init(&umono.lock_loader, NULL);
639 pthread_atfork(uwsgi_mono_pthread_prepare, uwsgi_mono_pthread_parent, uwsgi_mono_pthread_child);
640 }
641
uwsgi_mono_post_fork()642 static void uwsgi_mono_post_fork() {
643
644 // yes, Mono is not fork-friendly, so we initialize it in the post_fork hook
645 uwsgi_mono_init_apps();
646
647 MonoMethodDesc *desc = mono_method_desc_new("uwsgi.api:RunPostForkHook()", 1);
648 if (!desc) {
649 return;
650 }
651 MonoMethod *method = mono_method_desc_search_in_class(desc, umono.api_class);
652 mono_method_desc_free(desc);
653 if (!method) {
654 return;
655 }
656
657 MonoObject *exc = NULL;
658 mono_runtime_invoke(method, NULL, NULL, &exc);
659 if (exc) {
660 mono_print_unhandled_exception(exc);
661 }
662 }
663
uwsgi_mono_signal_handler(uint8_t sig,void * handler)664 static int uwsgi_mono_signal_handler(uint8_t sig, void *handler) {
665 void *params[2];
666 int signum = sig;
667 params[0] = &signum;
668 params[1] = NULL;
669 MonoObject *exc = NULL;
670 mono_runtime_delegate_invoke((MonoObject *) handler, params, &exc);
671 if (exc) {
672 mono_print_unhandled_exception(exc);
673 return -1;
674 }
675 return 0;
676 }
677
678 struct uwsgi_plugin mono_plugin = {
679
680 .name = "mono",
681 .modifier1 = 15,
682
683 .options = uwsgi_mono_options,
684
685 .init = uwsgi_mono_init,
686
687 .request = uwsgi_mono_request,
688 .after_request = uwsgi_mono_after_request,
689
690 .init_thread = uwsgi_mono_init_thread,
691 .enable_threads = uwsgi_mono_enable_threads,
692
693 .post_fork = uwsgi_mono_post_fork,
694
695 .signal_handler = uwsgi_mono_signal_handler,
696 };
697