1 #include "uwsgi.h"
2 
3 extern struct uwsgi_server uwsgi;
4 
uwsgi_static_want_gzip(struct wsgi_request * wsgi_req,char * filename,size_t * filename_len,struct stat * st)5 int uwsgi_static_want_gzip(struct wsgi_request *wsgi_req, char *filename, size_t *filename_len, struct stat *st) {
6 	char can_gzip = 0, can_br = 0;
7 
8 	// check for filename size
9 	if (*filename_len + 4 > PATH_MAX) return 0;
10 	// check for supported encodings
11 	can_br = uwsgi_contains_n(wsgi_req->encoding, wsgi_req->encoding_len, "br", 2);
12 	can_gzip = uwsgi_contains_n(wsgi_req->encoding, wsgi_req->encoding_len, "gzip", 4);
13 
14 	if(!can_br && !can_gzip)
15 		return 0;
16 
17 	// check for 'all'
18 	if (uwsgi.static_gzip_all) goto gzip;
19 
20 	// check for dirs/prefix
21 	struct uwsgi_string_list *usl = uwsgi.static_gzip_dir;
22 	while(usl) {
23 		if (!uwsgi_starts_with(filename, *filename_len, usl->value, usl->len)) {
24 			goto gzip;
25 		}
26 		usl = usl->next;
27 	}
28 
29 	// check for ext/suffix
30 	usl = uwsgi.static_gzip_ext;
31 	while(usl) {
32 		if (!uwsgi_strncmp(filename + (*filename_len - usl->len), usl->len, usl->value, usl->len)) {
33 			goto gzip;
34 		}
35 		usl = usl->next;
36 	}
37 
38 #ifdef UWSGI_PCRE
39 	// check for regexp
40 	struct uwsgi_regexp_list *url = uwsgi.static_gzip;
41 	while(url) {
42 		if (uwsgi_regexp_match(url->pattern, url->pattern_extra, filename, *filename_len) >= 0) {
43 			goto gzip;
44 		}
45 		url = url->next;
46 	}
47 #endif
48 	return 0;
49 
50 gzip:
51 	if (can_br) {
52 		memcpy(filename + *filename_len, ".br\0", 4);
53 		*filename_len += 3;
54 		if (!stat(filename, st)) return 2;
55 		*filename_len -= 3;
56 		filename[*filename_len] = 0;
57 	}
58 
59 	if (can_gzip) {
60 		memcpy(filename + *filename_len, ".gz\0", 4);
61 		*filename_len += 3;
62 		if (!stat(filename, st)) return 1;
63 		*filename_len -= 3;
64 		filename[*filename_len] = 0;
65 	}
66 
67 	return 0;
68 }
69 
uwsgi_http_date(time_t t,char * dst)70 int uwsgi_http_date(time_t t, char *dst) {
71 
72         static char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
73         static char *months[] = {
74                 "Jan", "Feb", "Mar", "Apr",
75                 "May", "Jun", "Jul", "Aug",
76                 "Sep", "Oct", "Nov", "Dec"
77         };
78 
79         struct tm *hdtm = gmtime(&t);
80 
81         int ret = snprintf(dst, 31, "%s, %02d %s %4d %02d:%02d:%02d GMT",
82 		week[hdtm->tm_wday], hdtm->tm_mday, months[hdtm->tm_mon], hdtm->tm_year + 1900, hdtm->tm_hour, hdtm->tm_min, hdtm->tm_sec);
83 	if (ret <= 0 || ret > 31) {
84 		return 0;
85 	}
86 	return ret;
87 }
88 
89 // only RFC 1123 is supported
parse_http_date(char * date,uint16_t len)90 static time_t parse_http_date(char *date, uint16_t len) {
91 
92         struct tm hdtm;
93 
94         if (len != 29 && date[3] != ',')
95                 return 0;
96 
97         hdtm.tm_mday = uwsgi_str2_num(date + 5);
98 
99         switch (date[8]) {
100         case 'J':
101                 if (date[9] == 'a') {
102                         hdtm.tm_mon = 0;
103                         break;
104                 }
105 
106                 if (date[9] == 'u') {
107                         if (date[10] == 'n') {
108                                 hdtm.tm_mon = 5;
109                                 break;
110                         }
111 
112                         if (date[10] == 'l') {
113                                 hdtm.tm_mon = 6;
114                                 break;
115                         }
116 
117                         return 0;
118                 }
119 
120                 return 0;
121 
122         case 'F':
123                 hdtm.tm_mon = 1;
124                 break;
125 
126         case 'M':
127                 if (date[9] != 'a')
128                         return 0;
129 
130                 if (date[10] == 'r') {
131                         hdtm.tm_mon = 2;
132                         break;
133                 }
134 
135                 if (date[10] == 'y') {
136                         hdtm.tm_mon = 4;
137                         break;
138                 }
139 
140                 return 0;
141 
142         case 'A':
143                 if (date[10] == 'r') {
144                         hdtm.tm_mon = 3;
145                         break;
146                 }
147                 if (date[10] == 'g') {
148                         hdtm.tm_mon = 7;
149                         break;
150                 }
151                 return 0;
152 
153         case 'S':
154                 hdtm.tm_mon = 8;
155                 break;
156 
157         case 'O':
158                 hdtm.tm_mon = 9;
159                 break;
160 
161         case 'N':
162                 hdtm.tm_mon = 10;
163 		break;
164 
165         case 'D':
166                 hdtm.tm_mon = 11;
167                 break;
168         default:
169                 return 0;
170         }
171 
172         hdtm.tm_year = uwsgi_str4_num(date + 12) - 1900;
173 
174         hdtm.tm_hour = uwsgi_str2_num(date + 17);
175         hdtm.tm_min = uwsgi_str2_num(date + 20);
176         hdtm.tm_sec = uwsgi_str2_num(date + 23);
177 
178         return timegm(&hdtm);
179 
180 }
181 
182 
183 
uwsgi_add_expires_type(struct wsgi_request * wsgi_req,char * mime_type,int mime_type_len,struct stat * st)184 int uwsgi_add_expires_type(struct wsgi_request *wsgi_req, char *mime_type, int mime_type_len, struct stat *st) {
185 
186 	struct uwsgi_dyn_dict *udd = uwsgi.static_expires_type;
187 	time_t now = wsgi_req->start_of_request / 1000000;
188 	// 30+1
189 	char expires[31];
190 
191 	while (udd) {
192 		if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) {
193 			int delta = uwsgi_str_num(udd->value, udd->vallen);
194 			int size = uwsgi_http_date(now + delta, expires);
195 			if (size > 0) {
196 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
197 			}
198 			return 0;
199 		}
200 		udd = udd->next;
201 	}
202 
203 	udd = uwsgi.static_expires_type_mtime;
204 	while (udd) {
205 		if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) {
206 			int delta = uwsgi_str_num(udd->value, udd->vallen);
207 			int size = uwsgi_http_date(st->st_mtime + delta, expires);
208 			if (size > 0) {
209 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
210 			}
211 			return 0;
212 		}
213 		udd = udd->next;
214 	}
215 
216 	return 0;
217 }
218 
219 #ifdef UWSGI_PCRE
uwsgi_add_expires(struct wsgi_request * wsgi_req,char * filename,int filename_len,struct stat * st)220 int uwsgi_add_expires(struct wsgi_request *wsgi_req, char *filename, int filename_len, struct stat *st) {
221 
222 	struct uwsgi_dyn_dict *udd = uwsgi.static_expires;
223 	time_t now = wsgi_req->start_of_request / 1000000;
224 	// 30+1
225 	char expires[31];
226 
227 	while (udd) {
228 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, filename, filename_len) >= 0) {
229 			int delta = uwsgi_str_num(udd->value, udd->vallen);
230 			int size = uwsgi_http_date(now + delta, expires);
231 			if (size > 0) {
232 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
233 			}
234 			return 0;
235 		}
236 		udd = udd->next;
237 	}
238 
239 	udd = uwsgi.static_expires_mtime;
240 	while (udd) {
241 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, filename, filename_len) >= 0) {
242 			int delta = uwsgi_str_num(udd->value, udd->vallen);
243 			int size = uwsgi_http_date(st->st_mtime + delta, expires);
244 			if (size > 0) {
245 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
246 			}
247 			return 0;
248 		}
249 		udd = udd->next;
250 	}
251 
252 	return 0;
253 }
254 
uwsgi_add_expires_path_info(struct wsgi_request * wsgi_req,struct stat * st)255 int uwsgi_add_expires_path_info(struct wsgi_request *wsgi_req, struct stat *st) {
256 
257 	struct uwsgi_dyn_dict *udd = uwsgi.static_expires_path_info;
258 	time_t now = wsgi_req->start_of_request / 1000000;
259 	// 30+1
260 	char expires[31];
261 
262 	while (udd) {
263 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) {
264 			int delta = uwsgi_str_num(udd->value, udd->vallen);
265 			int size = uwsgi_http_date(now + delta, expires);
266 			if (size > 0) {
267 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
268 			}
269 			return 0;
270 		}
271 		udd = udd->next;
272 	}
273 
274 	udd = uwsgi.static_expires_path_info_mtime;
275 	while (udd) {
276 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) {
277 			int delta = uwsgi_str_num(udd->value, udd->vallen);
278 			int size = uwsgi_http_date(st->st_mtime + delta, expires);
279 			if (size > 0) {
280 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
281 			}
282 			return 0;
283 		}
284 		udd = udd->next;
285 	}
286 
287 	return 0;
288 }
289 
uwsgi_add_expires_uri(struct wsgi_request * wsgi_req,struct stat * st)290 int uwsgi_add_expires_uri(struct wsgi_request *wsgi_req, struct stat *st) {
291 
292 	struct uwsgi_dyn_dict *udd = uwsgi.static_expires_uri;
293 	time_t now = wsgi_req->start_of_request / 1000000;
294 	// 30+1
295 	char expires[31];
296 
297 	while (udd) {
298 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) {
299 			int delta = uwsgi_str_num(udd->value, udd->vallen);
300 			int size = uwsgi_http_date(now + delta, expires);
301 			if (size > 0) {
302 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
303 			}
304 			return 0;
305 		}
306 		udd = udd->next;
307 	}
308 
309 	udd = uwsgi.static_expires_uri_mtime;
310 	while (udd) {
311 		if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) {
312 			int delta = uwsgi_str_num(udd->value, udd->vallen);
313 			int size = uwsgi_http_date(st->st_mtime + delta, expires);
314 			if (size > 0) {
315 				if (uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, size)) return -1;
316 			}
317 			return 0;
318 		}
319 		udd = udd->next;
320 	}
321 
322 	return 0;
323 }
324 
325 
326 
327 #endif
328 
329 
uwsgi_get_mime_type(char * name,int namelen,size_t * size)330 char *uwsgi_get_mime_type(char *name, int namelen, size_t *size) {
331 
332 	int i;
333 	int count = 0;
334 	char *ext = NULL;
335 	for (i = namelen - 1; i >= 0; i--) {
336 		if (!isalnum((int) name[i])) {
337 			if (name[i] == '.') {
338 				ext = name + (namelen - count);
339 				break;
340 			}
341 		}
342 		count++;
343 	}
344 
345 	if (!ext)
346 		return NULL;
347 
348 
349 	if (uwsgi.threads > 1)
350                 pthread_mutex_lock(&uwsgi.lock_static);
351 
352 	struct uwsgi_dyn_dict *udd = uwsgi.mimetypes;
353 	while (udd) {
354 		if (!uwsgi_strncmp(ext, count, udd->key, udd->keylen)) {
355 			udd->hits++;
356 			// auto optimization
357 			if (udd->prev) {
358 				if (udd->hits > udd->prev->hits) {
359 					struct uwsgi_dyn_dict *udd_parent = udd->prev->prev, *udd_prev = udd->prev;
360 					if (udd_parent) {
361 						udd_parent->next = udd;
362 					}
363 
364 					if (udd->next) {
365 						udd->next->prev = udd_prev;
366 					}
367 
368 					udd_prev->prev = udd;
369 					udd_prev->next = udd->next;
370 
371 					udd->prev = udd_parent;
372 					udd->next = udd_prev;
373 
374 					if (udd->prev == NULL) {
375 						uwsgi.mimetypes = udd;
376 					}
377 				}
378 			}
379 			*size = udd->vallen;
380 			if (uwsgi.threads > 1)
381                 		pthread_mutex_unlock(&uwsgi.lock_static);
382 			return udd->value;
383 		}
384 		udd = udd->next;
385 	}
386 
387 	if (uwsgi.threads > 1)
388         	pthread_mutex_unlock(&uwsgi.lock_static);
389 
390 	return NULL;
391 }
392 
uwsgi_append_static_path(char * dir,size_t dir_len,char * file,size_t file_len)393 ssize_t uwsgi_append_static_path(char *dir, size_t dir_len, char *file, size_t file_len) {
394 
395 
396 	size_t len = dir_len;
397 
398 	if (len + 1 + file_len > PATH_MAX) {
399 		return -1;
400 	}
401 
402 	if (dir[len - 1] == '/') {
403 		memcpy(dir + len, file, file_len);
404 		dir[len + file_len] = 0;
405 		len += file_len;
406 	}
407 	else {
408 		dir[len] = '/';
409 		memcpy(dir + len + 1, file, file_len);
410 		dir[len + 1 + file_len] = 0;
411 		len += 1 + file_len;
412 	}
413 
414 	return len;
415 }
416 
uwsgi_static_stat(struct wsgi_request * wsgi_req,char * filename,size_t * filename_len,struct stat * st,struct uwsgi_string_list ** index)417 static int uwsgi_static_stat(struct wsgi_request *wsgi_req, char *filename, size_t *filename_len, struct stat *st, struct uwsgi_string_list **index) {
418 
419 	int ret = stat(filename, st);
420 	// if non-existant return -1
421 	if (ret < 0)
422 		return -1;
423 
424 	if (S_ISREG(st->st_mode))
425 		return 0;
426 
427 	// check for index
428 	if (S_ISDIR(st->st_mode)) {
429 		struct uwsgi_string_list *usl = uwsgi.static_index;
430 		while (usl) {
431 			ssize_t new_len = uwsgi_append_static_path(filename, *filename_len, usl->value, usl->len);
432 			if (new_len >= 0) {
433 #ifdef UWSGI_DEBUG
434 				uwsgi_log("checking for %s\n", filename);
435 #endif
436 				if (uwsgi_is_file2(filename, st)) {
437 					*index = usl;
438 					*filename_len = new_len;
439 					return 0;
440 				}
441 				// reset to original name
442 				filename[*filename_len] = 0;
443 			}
444 			usl = usl->next;
445 		}
446 	}
447 
448 	return -1;
449 }
450 
uwsgi_request_fix_range_for_size(struct wsgi_request * wsgi_req,int64_t size)451 void uwsgi_request_fix_range_for_size(struct wsgi_request *wsgi_req, int64_t size) {
452 	uwsgi_fix_range_for_size(&wsgi_req->range_parsed,
453 			&wsgi_req->range_from, &wsgi_req->range_to, size);
454 }
455 
uwsgi_real_file_serve(struct wsgi_request * wsgi_req,char * real_filename,size_t real_filename_len,struct stat * st)456 int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, size_t real_filename_len, struct stat *st) {
457 
458 	size_t mime_type_size = 0;
459 	char http_last_modified[49];
460 	int use_gzip = 0;
461 
462 	char *mime_type = uwsgi_get_mime_type(real_filename, real_filename_len, &mime_type_size);
463 
464 	// here we need to choose if we want the gzip variant;
465 	use_gzip = uwsgi_static_want_gzip(wsgi_req, real_filename, &real_filename_len, st);
466 
467 	if (wsgi_req->if_modified_since_len) {
468 		time_t ims = parse_http_date(wsgi_req->if_modified_since, wsgi_req->if_modified_since_len);
469 		if (st->st_mtime <= ims) {
470 			if (uwsgi_response_prepare_headers(wsgi_req, "304 Not Modified", 16))
471 				return -1;
472 			return uwsgi_response_write_headers_do(wsgi_req);
473 		}
474 	}
475 #ifdef UWSGI_DEBUG
476 	uwsgi_log("[uwsgi-fileserve] file %s found\n", real_filename);
477 #endif
478 
479 	// static file - don't update avg_rt after request
480 	wsgi_req->do_not_account_avg_rt = 1;
481 
482 	int64_t fsize = (int64_t)st->st_size;
483 	uwsgi_request_fix_range_for_size(wsgi_req, fsize);
484 	switch (wsgi_req->range_parsed) {
485 	case UWSGI_RANGE_INVALID:
486 		if (uwsgi_response_prepare_headers(wsgi_req,
487 					"416 Requested Range Not Satisfiable", 35))
488 			return -1;
489 		if (uwsgi_response_add_content_range(wsgi_req, -1, -1, st->st_size)) return -1;
490 		return 0;
491 	case UWSGI_RANGE_VALID:
492 		{
493 			time_t when = 0;
494 			if (wsgi_req->if_range != NULL) {
495 				when = parse_http_date(wsgi_req->if_range, wsgi_req->if_range_len);
496 				// an ETag will result in when == 0
497 			}
498 
499 			if (when < st->st_mtime) {
500 				fsize = wsgi_req->range_to - wsgi_req->range_from + 1;
501 				if (uwsgi_response_prepare_headers(wsgi_req, "206 Partial Content", 19)) return -1;
502 				break;
503 			}
504 		}
505 		/* fallthrough */
506 	default: /* UWSGI_RANGE_NOT_PARSED */
507 		if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
508 	}
509 
510 #ifdef UWSGI_PCRE
511 	uwsgi_add_expires(wsgi_req, real_filename, real_filename_len, st);
512 	uwsgi_add_expires_path_info(wsgi_req, st);
513 	uwsgi_add_expires_uri(wsgi_req, st);
514 #endif
515 
516 	if (use_gzip == 1) {
517 		if (uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "gzip", 4)) return -1;
518 	} else if (use_gzip == 2) {
519 	    if (uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "br", 2)) return -1;
520 	}
521 
522 	// Content-Type (if available)
523 	if (mime_type_size > 0 && mime_type) {
524 		if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_size)) return -1;
525 		// check for content-type related headers
526 		uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, st);
527 	}
528 
529 	// increase static requests counter
530 	uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].static_requests++;
531 
532 	// nginx
533 	if (uwsgi.file_serve_mode == 1) {
534 		if (uwsgi_response_add_header(wsgi_req, "X-Accel-Redirect", 16, real_filename, real_filename_len)) return -1;
535 		// this is the final header (\r\n added)
536 		int size = uwsgi_http_date(st->st_mtime, http_last_modified);
537 		if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1;
538 	}
539 	// apache
540 	else if (uwsgi.file_serve_mode == 2) {
541 		if (uwsgi_response_add_header(wsgi_req, "X-Sendfile", 10, real_filename, real_filename_len)) return -1;
542 		// this is the final header (\r\n added)
543 		int size = uwsgi_http_date(st->st_mtime, http_last_modified);
544 		if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1;
545 	}
546 	// raw
547 	else {
548 		// set Content-Length (to fsize NOT st->st_size)
549 		if (uwsgi_response_add_content_length(wsgi_req, fsize)) return -1;
550 		if (wsgi_req->range_parsed == UWSGI_RANGE_VALID) {
551 			// here use the original size !!!
552 			if (uwsgi_response_add_content_range(wsgi_req, wsgi_req->range_from, wsgi_req->range_to, st->st_size)) return -1;
553 		}
554 		int size = uwsgi_http_date(st->st_mtime, http_last_modified);
555 		if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1;
556 
557 		// if it is a HEAD request just skip transfer
558 		if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) {
559 			wsgi_req->status = 200;
560 			return 0;
561 		}
562 
563 		// Ok, the file must be transferred from uWSGI
564 		// offloading will be automatically managed
565 		int fd = open(real_filename, O_RDONLY);
566 		if (fd < 0) return -1;
567 		// fd will be closed in the following function
568 		uwsgi_response_sendfile_do(wsgi_req, fd, wsgi_req->range_from, fsize);
569 	}
570 
571 	wsgi_req->status = 200;
572 	return 0;
573 }
574 
575 
uwsgi_file_serve(struct wsgi_request * wsgi_req,char * document_root,uint16_t document_root_len,char * path_info,uint16_t path_info_len,int is_a_file)576 int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_t document_root_len, char *path_info, uint16_t path_info_len, int is_a_file) {
577 
578 	struct stat st;
579 	char real_filename[PATH_MAX + 1];
580 	size_t real_filename_len = 0;
581 	char *filename = NULL;
582 	size_t filename_len = 0;
583 
584 	struct uwsgi_string_list *index = NULL;
585 
586 	if (!is_a_file) {
587 		filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len);
588 		filename_len = document_root_len + 1 + path_info_len;
589 	}
590 	else {
591 		filename = uwsgi_concat2n(document_root, document_root_len, "", 0);
592 		filename_len = document_root_len;
593 	}
594 
595 #ifdef UWSGI_DEBUG
596 	uwsgi_log("[uwsgi-fileserve] checking for %s\n", filename);
597 #endif
598 
599 	if (uwsgi.static_cache_paths) {
600 		uwsgi_rlock(uwsgi.static_cache_paths->lock);
601 		uint64_t item_len;
602 		char *item = uwsgi_cache_get2(uwsgi.static_cache_paths, filename, filename_len, &item_len);
603 		if (item && item_len > 0 && item_len <= PATH_MAX) {
604 			memcpy(real_filename, item, item_len);
605 			real_filename_len = item_len;
606 			real_filename[real_filename_len] = 0;
607 			uwsgi_rwunlock(uwsgi.static_cache_paths->lock);
608 			goto found;
609 		}
610 		uwsgi_rwunlock(uwsgi.static_cache_paths->lock);
611 	}
612 
613 	if (!realpath(filename, real_filename)) {
614 #ifdef UWSGI_DEBUG
615 		uwsgi_log("[uwsgi-fileserve] unable to get realpath() of the static file\n");
616 #endif
617 		free(filename);
618 		return -1;
619 	}
620 	real_filename_len = strlen(real_filename);
621 
622 	if (uwsgi.static_cache_paths) {
623 		uwsgi_wlock(uwsgi.static_cache_paths->lock);
624 		uwsgi_cache_set2(uwsgi.static_cache_paths, filename, filename_len, real_filename, real_filename_len, uwsgi.use_static_cache_paths, UWSGI_CACHE_FLAG_UPDATE);
625 		uwsgi_rwunlock(uwsgi.static_cache_paths->lock);
626 	}
627 
628 found:
629 	free(filename);
630 
631 	if (uwsgi_starts_with(real_filename, real_filename_len, document_root, document_root_len)) {
632 		struct uwsgi_string_list *safe = uwsgi.static_safe;
633 		while(safe) {
634 			if (!uwsgi_starts_with(real_filename, real_filename_len, safe->value, safe->len)) {
635 				goto safe;
636 			}
637 			safe = safe->next;
638 		}
639 		uwsgi_log("[uwsgi-fileserve] security error: %s is not under %.*s or a safe path\n", real_filename, document_root_len, document_root);
640 		return -1;
641 	}
642 
643 safe:
644 
645 	if (!uwsgi_static_stat(wsgi_req, real_filename, &real_filename_len, &st, &index)) {
646 
647 		if (index) {
648 			// if we are here the PATH_INFO need to be changed
649 			if (uwsgi_req_append_path_info_with_index(wsgi_req, index->value, index->len)) {
650                         	return -1;
651                         }
652 		}
653 
654 		// skip methods other than GET and HEAD
655         	if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3) && uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) {
656 			return -1;
657         	}
658 
659 		// check for skippable ext
660 		struct uwsgi_string_list *sse = uwsgi.static_skip_ext;
661 		while (sse) {
662 			if (real_filename_len >= sse->len) {
663 				if (!uwsgi_strncmp(real_filename + (real_filename_len - sse->len), sse->len, sse->value, sse->len)) {
664 					return -1;
665 				}
666 			}
667 			sse = sse->next;
668 		}
669 
670 #ifdef UWSGI_ROUTING
671 		// before sending the file, we need to check if some rule applies
672 		if (!wsgi_req->is_routing && uwsgi_apply_routes_do(uwsgi.routes, wsgi_req, NULL, 0) == UWSGI_ROUTE_BREAK) {
673 			return 0;
674 		}
675 		wsgi_req->routes_applied = 1;
676 #endif
677 
678 		return uwsgi_real_file_serve(wsgi_req, real_filename, real_filename_len, &st);
679 	}
680 
681 	return -1;
682 
683 }
684