Redirect to index.html when a file is not found by web server (#15143)
* redirect to index.html when a file is not found * return index.html only when not found is likely a directory * recursively find the first path that works --------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>
Emmanuel Vasilakis committed
Jun 14, 2023 at 21:37 UTC
dfa045ae6c64b69871ea1a6c90bd07333d1118cb
1 file changed
+71
-32
web/server/web_client.c
+71
-32
@@ -324,6 +324,64 @@ static inline int access_to_file_is_not_permitted(struct web_client *w, const ch
324
325
// Work around a bug in the CMocka library by removing this function during testing.
326
#ifndef REMOVE_MYSENDFILE
327
+
328
+static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_len, struct stat *statbuf) {
329
+ // copy the filename to our src buffer
330
+ char path[FILENAME_MAX + 1];
331
+ strncpyz(path, filename, FILENAME_MAX);
332
+
333
+ bool strip = false;
334
+ while(1) {
335
+ if(*path)
336
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, path);
337
+ else
338
+ snprintfz(dst, dst_len, "%s", netdata_configured_web_dir);
339
+
340
+ // internal_error(true, "WEBFILE: trying '%s', path '%s'", dst, path);
341
+
342
+ strip = false;
343
+ if (lstat(dst, statbuf) != 0)
344
+ strip = true;
345
+
346
+ if (!strip && (statbuf->st_mode & S_IFMT) == S_IFDIR) {
347
+ // it is a directory
348
+ // let's see if it has index.html in it
349
+ if(*path)
350
+ snprintfz(dst, dst_len, "%s/%s/index.html", netdata_configured_web_dir, path);
351
+ else
352
+ snprintfz(dst, dst_len, "%s/index.html", netdata_configured_web_dir);
353
+
354
+ if (lstat(dst, statbuf) != 0 || (statbuf->st_mode & S_IFMT) == S_IFDIR)
355
+ strip = true;
356
+ }
357
+
358
+ if(!strip && (statbuf->st_mode & S_IFMT) != S_IFREG)
359
+ strip = true;
360
+
361
+ if(strip) {
362
+ char *s = path, *e = path;
363
+ while(*e) e++; // find the terminator
364
+ if(e > s) e--; // find the last character
365
+
366
+ while(e >= s && *e != '/') *e-- = '\0'; // find the previous slash
367
+ while(e >= s && *e == '/') *e-- = '\0'; // zero the slashes
368
+
369
+ if(!*s || e <= s) {
370
+ snprintfz(dst, dst_len, "%s/index.html", netdata_configured_web_dir);
371
+ if(lstat(dst, statbuf) != 0)
372
+ return false;
373
+ else
374
+ break;
375
+ }
376
+ }
377
+ else
378
+ break;
379
+ }
380
+
381
+ // internal_error(true, "WEBFILE: final '%s'", dst);
382
+ return true;
383
+}
384
+
385
int mysendfile(struct web_client *w, char *filename) {
386
debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
387
@@ -355,60 +413,41 @@ int mysendfile(struct web_client *w, char *filename) {
413
}
414
415
// find the physical file on disk
358
- char webfilename[FILENAME_MAX + 1];
359
- snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_web_dir, filename);
360
-
416
+ char web_filename[FILENAME_MAX + 1];
417
struct stat statbuf;
362
- int done = 0;
363
- while(!done) {
364
- // check if the file exists
365
- if (lstat(webfilename, &statbuf) != 0) {
366
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
367
- w->response.data->content_type = CT_TEXT_HTML;
368
- buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
369
- buffer_strcat_htmlescape(w->response.data, webfilename);
370
- return HTTP_RESP_NOT_FOUND;
371
- }
372
-
373
- if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
374
- snprintfz(webfilename, FILENAME_MAX, "%s/%s/index.html", netdata_configured_web_dir, filename);
375
- continue;
376
- }
377
-
378
- if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
379
- error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
380
- return access_to_file_is_not_permitted(w, webfilename);
381
- }
382
-
383
- done = 1;
418
+ if(!find_filename_to_serve(filename, web_filename, FILENAME_MAX, &statbuf)) {
419
+ w->response.data->content_type = CT_TEXT_HTML;
420
+ buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
421
+ buffer_strcat_htmlescape(w->response.data, web_filename);
422
+ return HTTP_RESP_NOT_FOUND;
423
}
424
425
// open the file
387
- w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
426
+ w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY);
427
if(w->ifd == -1) {
428
w->ifd = w->ofd;
429
430
if(errno == EBUSY || errno == EAGAIN) {
392
- error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
431
+ error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
432
w->response.data->content_type = CT_TEXT_HTML;
433
buffer_sprintf(w->response.header, "Location: /%s\r\n", filename);
434
buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
396
- buffer_strcat_htmlescape(w->response.data, webfilename);
435
+ buffer_strcat_htmlescape(w->response.data, web_filename);
436
return HTTP_RESP_REDIR_TEMP;
437
}
438
else {
400
- error("%llu: Cannot open file '%s'.", w->id, webfilename);
439
+ error("%llu: Cannot open file '%s'.", w->id, web_filename);
440
w->response.data->content_type = CT_TEXT_HTML;
441
buffer_strcat(w->response.data, "Cannot open file: ");
403
- buffer_strcat_htmlescape(w->response.data, webfilename);
442
+ buffer_strcat_htmlescape(w->response.data, web_filename);
443
return HTTP_RESP_NOT_FOUND;
444
}
445
}
446
447
sock_setnonblock(w->ifd);
448
410
- w->response.data->content_type = contenttype_for_filename(webfilename);
411
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, webfilename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
449
+ w->response.data->content_type = contenttype_for_filename(web_filename);
450
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
451
452
w->mode = WEB_CLIENT_MODE_FILECOPY;
453
web_client_enable_wait_receive(w);