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