Last active
December 8, 2024 05:07
-
-
Save J-Moravec/497d71f4a4b7a204235d093b3fa69cc3 to your computer and use it in GitHub Desktop.
Implementation of a local server for static website using the R's internal httpd server.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
error404 = paste0( | |
"<!DOCTYPE html>", | |
"<html lang=\"en\">", | |
"<head>", | |
" <meta charset=\"UTF-8\">", | |
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">", | |
" <title>Resources not found</title>", | |
"</head>", | |
"<body>", | |
" <div class=\"main\">", | |
" <h1>404</h1>", | |
" <div>The page you are looking for is not found</div>", | |
" <a href=\"/\">Back to home</a>", | |
" </div>", | |
"</body>", | |
"</html>", | |
collapse = "\n" | |
) | |
#' Serve static sites | |
#' | |
#' Take the path, such as "/", "/index.html", "posts/learning_r.html" | |
#' and serve a file with an appropriate mimetype. | |
#' If file is not found, return the 404 error page. | |
#' | |
#' All links within the html pages (such as css, images) are also translated in this way. | |
#' | |
#' query and ... are ignored | |
static_server = function(path, query, ...){ | |
path = sub(pattern = "^/", replace = "", path) | |
if(path == "") path = "index.html" | |
if(file.exists(path) && file_test("-f", path)){ | |
list(file = path, "content-type" = tools:::mime_type(path)) | |
} else { | |
list(payload = error404) | |
} | |
} | |
#' Print arguments | |
#' | |
#' this is a toy server for learning how are the arguments | |
#' that are passed to the browser URL interpreted. | |
#' | |
#' For instance, the default the server is launched with: | |
#' "127.0.0.1:port", which translates to "/" path. | |
#' If we type into browser "127.0.0.1:port/foo?bar=1&baz=2", | |
#' then path will be "/foo", and query a named vector: | |
#' `c("foo" = 1, "bar" = 2)` | |
#' In all cases, the `...` contains NULL and then a raw vector with | |
#' user agent information. | |
print_server = function(path, query, ...){ | |
list( | |
payload = paste0( | |
c( | |
"path:", capture.output(str(path)), | |
"query:", capture.output(str(query)), | |
"...", capture.output(str(list(...))) | |
), | |
collapse = "<p>" | |
) | |
) | |
} | |
assign_in_namespace = function(x, f, envir){ | |
old = get(x, envir = envir) | |
unlockBinding(x, envir) | |
assign(x, f, envir = envir) | |
lockBinding(x, envir) | |
invisible(old) | |
} | |
#' Start a server and open it in browser | |
#' | |
#' This function will start server `f` in a directory `dir` | |
#' on a prespecified `port` (or random, by default) on a localhost | |
#' (loopback device 127.0.0.1) | |
#' | |
#' It will then open browser and point at `127.0.0.1:port` | |
#' and wait. Terminating this process, such as with CTRL+C | |
#' will close the server. | |
#' | |
#' The default server is the `print_server` at current directory. | |
#' You can explore how the website URL translate to different | |
#' parameters passed to the server function. | |
#' | |
#' To effectively use the `static_server`, you need to specify | |
#' the `f = static_server` and set `dir` to a path with a static site. | |
#' This site needs to be generated for local browsing | |
#' (e.g., "site_url" needs to be empty instead of something like | |
#' "[email protected]". | |
#' Feel free to clone https://github.com/j-moravec/cookingrecipes | |
#' for this purpose and run `Rscript ssg.r -s`. | |
#' Running this code (serve.r) will then replace `Rscript ssg.r -v` which | |
#' uses the package `servr` for this purpose. | |
serve = function(dir = ".", port = 0, f = print_server){ | |
f = match.fun(f) | |
dir = normalizePath(dir) | |
if(port) | |
options(help.ports = port) | |
old_f = assign_in_namespace("httpd", f, getNamespace("tools")) | |
old_wd = getwd() | |
setwd(dir) | |
on.exit({ | |
port = tools:::httpdPort() | |
if(port > 0) | |
tools:::startDynamicHelp(FALSE) | |
assign_in_namespace("httpd", old_f, getNamespace("tools")) | |
setwd(old_wd) | |
}) | |
port = tools:::httpdPort() | |
if(port > 0){ | |
# server is running, restart it | |
tools:::startDynamicHelp(FALSE) | |
} | |
port = tools:::startDynamicHelp(NA) | |
url = paste0("http://127.0.0.1:", port) | |
message("Serving directory: ", dir) | |
message(paste("Served at:", url)) | |
browser = getOption("browser") | |
browseURL(url, browser = browser) | |
Sys.sleep(Inf) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment