Title: | A Lightweight and Flexible Web Framework |
---|---|
Description: | A very flexible framework for building server side logic in R. The framework is unopinionated when it comes to how HTTP requests and WebSocket messages are handled and supports all levels of app complexity; from serving static content to full-blown dynamic web-apps. Fiery does not hold your hand as much as e.g. the shiny package does, but instead sets you free to create your web app the way you want. |
Authors: | Thomas Lin Pedersen [cre, aut] |
Maintainer: | Thomas Lin Pedersen <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.2.1.9000 |
Built: | 2024-11-04 04:18:54 UTC |
Source: | https://github.com/thomasp85/fiery |
The Fire generator creates a new Fire
-object, which is the class containing
all the app logic. The class is based on the R6 OO-system and
is thus reference-based with methods and data attached to each object, in
contrast to the more well known S3 and S4 systems. A fiery
server is event
driven, which means that it is build up and manipulated by adding event
handlers and triggering events. To learn more about the fiery
event model,
read the event vignette.
fiery
servers can be modified directly or by attaching plugins. As with
events, plugins has its own vignette.
A new 'Fire'-object is initialized using the new()
method on the generator:
app <- Fire$new(host = '127.0.0.1', port = 8080L)
|
As Fire
objects are using reference semantics new copies of an app cannot
be made simply be assigning it to a new variable. If a true copy of a Fire
object is desired, use the clone()
method.
host
A string giving a valid IPv4 address owned by the server, or '0.0.0.0'
to listen on all addresses. The default is '127.0.0.1'
port
An integer giving the port number the server should listen on (defaults to 8080L
)
refresh_rate
The interval in seconds between run cycles when running a blocking server (defaults to 0.001
)
refresh_rate_nb
The interval in seconds between run cycles when running a non-blocking server (defaults to 1
)
trigger_dir
A valid folder where trigger files can be put when running a blocking server (defaults to NULL
). See the The event cycle in fiery vignette for more information.
plugins
A named list of the already attached plugins. Read Only - can only be modified using the attach()
method.
root
The location of the app. Setting this will remove the root value from requests (or decline them with 400
if the request does not match the root). E.g. the path of a request will be changed from /demo/test
to /test
if root == '/demo'
access_log_format
A glue string defining how requests will be logged. For standard formats see common_log_format and combined_log_format. Defaults to the Common Log Format
new()
Create a new Fire
app
Fire$new(host = "127.0.0.1", port = 8080)
host
A string overriding the default host
port
An port number overriding the default port
A Fire
object
format()
Human readable description of the app
Fire$format(...)
...
ignored
A character vector
ignite()
Begin running the server. Will trigger the start
event
Fire$ignite(block = TRUE, showcase = FALSE, ..., silent = FALSE)
block
Should the console be blocked while running (alternative is to run in the background)
showcase
Should the default browser open up at the server address
...
Arguments passed on to the start
handler
silent
Should startup messaging by silenced
start()
Synonymous method to ignite()
Fire$start(...)
...
passed on to ignite()
reignite()
Resume a session. This is equivalent to ignite()
but will also trigger the resume
event
Fire$reignite(...)
...
passed on to ignite()
resume()
Synonymous method to reignite()
Fire$resume(...)
...
passed on to ignite()
extinguish()
Stop the server. Will trigger the end
event
Fire$extinguish()
stop()
Synonymous method to extinguish()
Fire$stop()
on()
Add a handler to an event. See the The event cycle in fiery vignette for more information.
Fire$on(event, handler, pos = NULL)
event
The name of the event that should trigger the handler
handler
The handler function that should be triggered
pos
The position in the handler stack to place it at. NULL
will place it at the end.
A unique string identifying the handler
off()
Remove an event handler from the app.
Fire$off(handlerId)
handlerId
The unique id identifying the handler
trigger()
Trigger an event in the app. This will cause any handler attached to the event to be called. See the The event cycle in fiery vignette for more information.
Fire$trigger(event, ...)
event
The name of the event
...
Arguments passed on to the handlers
A named list containing the return values of all handlers attached to the event
send()
Send a Websocket message to a client. Will trigger the send
event.
Fire$send(message, id)
message
The message to send
id
The id of the client to send to. If missing, the message will be send to all clients
close_ws_con()
Close a Websocket connection. Will trigger the websocket-closed
event
Fire$close_ws_con(id)
id
The id of the client to close the websocket connection to
attach()
Attach a plugin to the app. See the Creating and using fiery plugins vignette for more information
Fire$attach(plugin, ..., force = FALSE)
plugin
The plugin to attach
...
Arguments to pass into the plugins on_attach()
method
force
If the plugin has already been attached an error is thrown, unless force = TRUE
which tells the app to reattach it
has_plugin()
Check if the app has a plugin attached
Fire$has_plugin(name)
name
The name of the plugin
A boolean indicating if the given plugin is already attached
header()
Add a global http header that will be applied to all responses
Fire$header(name, value)
name
The name of the header
value
The value of the header. Use NULL
to remove the global header
set_data()
Add data to the global data store
Fire$set_data(name, value)
name
The name identifying the data
value
The data to add
get_data()
Retrieve data from the global data store
Fire$get_data(name)
name
The name identifying the data
The data requested. Returns NULL
if the store does not contain the requested data
remove_data()
Remove data from the global data store
Fire$remove_data(name)
name
The name identifying the data to be removed
time()
Add a timed evaluation that will be evaluated after the given number of seconds. See the Delaying code execution in Fiery vignette for more information
Fire$time(expr, then, after, loop = FALSE)
expr
The expression to evaluate when the time has passed
then
A handler to call once expr
has been evaluated
after
The time in second to wait before evaluating expr
loop
Should expr
be called repeatedly with the interval given by after
A unique id identifying the handler
remove_time()
Remove a timed evaluation
Fire$remove_time(id)
id
The unique id identifying the handler
delay()
Add a delayed evaluation to be evaluated immediately at the end of the loop cycle. See the Delaying code execution in Fiery vignette for more information
Fire$delay(expr, then)
expr
The expression to evaluate at the end of the cycle
then
A handler to call once expr
has been evaluated
A unique id identifying the handler
remove_delay()
Remove a delayed evaluation
Fire$remove_delay(id)
id
The unique id identifying the handler
async()
Add an asynchronous evaluation to be evaluated in another process without blocking the server. See the Delaying code execution in Fiery vignette for more information
Fire$async(expr, then)
expr
The expression to evaluate at the end of the cycle
then
A handler to call once expr
has been evaluated
A unique id identifying the handler
remove_async()
Remove an async evaluation
Fire$remove_async(id)
id
The unique id identifying the handler
set_client_id_converter()
Sets the function that converts an HTTP request into a specific client id
Fire$set_client_id_converter(converter)
converter
A function with the argument request
set_logger()
Sets the logging function to use
Fire$set_logger(logger)
logger
A function with the arguments event
, message
, request
, and ...
log()
Log a message with the logger attached to the app. See loggers for build in functionality
Fire$log(event, message, request = NULL, ...)
event
The event associated with the message
message
The message to log
request
The Request
object associated with the message, if any.
...
Additional arguments passed on to the logger.
is_running()
Test if an app is running
Fire$is_running()
test_request()
Send a request directly to the request logic of a non-running app. Only intended for testing the request logic
Fire$test_request(request)
request
The request to send
test_header()
Send a request directly to the header logic of a non-running app. Only intended for testing the request logic
Fire$test_header(request)
request
The request to send
test_message()
Send a message directly to the message logic of a non-running app. Only intended for testing the websocket logic
Fire$test_message(request, binary, message, withClose = TRUE)
request
The request to use to establish the connection
binary
Is the message send in binary or character format
message
The message to send. If binary = FALSE
a character vector, if binary = TRUE
a raw vector
withClose
Should the websocket connection be closed at the end by the client
test_websocket()
Send a message directly from a non-running app. Only intended for testing the websocket logic
Fire$test_websocket(request, message, close = TRUE)
request
The request to use to establish the connection
message
The message to send from the app
close
Should the websocket connection be closed at the end by the server
clone()
The objects of this class are cloneable with this method.
Fire$clone(deep = FALSE)
deep
Whether to make a deep clone.
# Create a New App app <- Fire$new(port = 4689) # Setup the data every time it starts app$on('start', function(server, ...) { server$set_data('visits', 0) server$set_data('cycles', 0) }) # Count the number of cycles app$on('cycle-start', function(server, ...) { server$set_data('cycles', server$get_data('cycles') + 1) }) # Count the number of requests app$on('before-request', function(server, ...) { server$set_data('visits', server$get_data('visits') + 1) }) # Handle requests app$on('request', function(server, ...) { list( status = 200L, headers = list('Content-Type' = 'text/html'), body = paste('This is indeed a test. You are number', server$get_data('visits')) ) }) # Show number of requests in the console app$on('after-request', function(server, ...) { message(server$get_data('visits')) flush.console() }) # Terminate the server after 300 cycles app$on('cycle-end', function(server, ...) { if (server$get_data('cycles') > 300) { message('Ending...') flush.console() server$extinguish() } }) # Be polite app$on('end', function(server) { message('Goodbye') flush.console() }) ## Not run: app$ignite(showcase = TRUE) ## End(Not run)
# Create a New App app <- Fire$new(port = 4689) # Setup the data every time it starts app$on('start', function(server, ...) { server$set_data('visits', 0) server$set_data('cycles', 0) }) # Count the number of cycles app$on('cycle-start', function(server, ...) { server$set_data('cycles', server$get_data('cycles') + 1) }) # Count the number of requests app$on('before-request', function(server, ...) { server$set_data('visits', server$get_data('visits') + 1) }) # Handle requests app$on('request', function(server, ...) { list( status = 200L, headers = list('Content-Type' = 'text/html'), body = paste('This is indeed a test. You are number', server$get_data('visits')) ) }) # Show number of requests in the console app$on('after-request', function(server, ...) { message(server$get_data('visits')) flush.console() }) # Terminate the server after 300 cycles app$on('cycle-end', function(server, ...) { if (server$get_data('cycles') > 300) { message('Ending...') flush.console() server$extinguish() } }) # Be polite app$on('end', function(server) { message('Goodbye') flush.console() }) ## Not run: app$ignite(showcase = TRUE) ## End(Not run)
fiery
has a build in logging mechanism that lets you capture event
information however you like. Every user-injested warnings and errors are
automatically captured by the logger along with most system errors as well.
fiery
tries very hard not to break due to faulty app logic. This means that
any event handler error will be converted to an error log without fiery
stopping. In the case of request handlers a 500L response will be send back
if any error is encountered.
logger_null() logger_console(format = "{time} - {event}: {message}") logger_file(file, format = "{time} - {event}: {message}") logger_switch(..., default = logger_null()) common_log_format combined_log_format
logger_null() logger_console(format = "{time} - {event}: {message}") logger_file(file, format = "{time} - {event}: {message}") logger_switch(..., default = logger_null()) common_log_format combined_log_format
format |
A glue string specifying the format of the log entry |
file |
A file or connection to write to |
... |
A named list of loggers to use for different events. The same
semantics as switch is used so it is possible to let events
fall through e.g. |
default |
A catch-all logger for use with events not defined in |
An object of class character
of length 1.
An object of class character
of length 1.
By default, fiery
uses logger_null()
which forwards warning and error
messages to stderr()
and ignores any other logging events. To change this
behavior, set a different logger using the set_logger()
method:
app$set_logger(logger)
where logger
is a function taking at least the following arguments: event
,
message
, request
, time
, and ...
.
fiery
comes with some additional loggers, which either writes all logs to a
file or to the console. A new instance of the file logger can be created with
logger_file(file)
:
app$set_logger(logger_file('fiery_log.log'))
A new instance of the console logger can be create with logger_console()
:
app$set_logger(logger_console())
Both functions takes a format
a argument that lets you customise how the
log is written. Furthermore the console logger will style the logs with
colour coding depending on the content if the console supports it.
As a last possibility it is possible to use different loggers dependent on the event by using the switch logger:
app$set_logger(logger_switch(warning =, error = logger_file('errors.log), default = logger_file('info.log')))
fiery
logs a number of different information by itself describing its
operations during run. The following events are send to the log:
Will be send when the server starts up
Will be send when the server is resumed
Will be send when the server stops
Will be send when a request has been handled. The message will contain information about how long time it took to handle the request or if it was denied.
Will be send every time a WebSocket connection is established or closed as well as when a message is received or send
Will be send every time a message is emitted by an event handler or delayed execution handler
Will be send everytime a warning is emitted by an event handler or delayed execution handler
Will be send everytime an error is signaled by an event handler or delayed execution handler. In addition some internal functions will also emit error event when exceptions are encountered
By default only message, warning and error events will be logged by
sending them to the error stream as a message()
.
Of particular interest are logs that detail requests made to the server.
These are the request
events detailed above. There are different standards
for how requests are logged. fiery
uses the Common Log Format by default,
but this can be modified by setting the access_log_format
field to a
glue expression that has access to the following variables:
start_time
The time the request was recieved
end_time
The time the response was send back
request
The Request
object
response
The Response
object
id
The client id
To change the format:
app$access_log_format <- combined_log_format
Apart from the standard logs described above it is also possible to send
messages to the log as you please, e.g. inside event handlers. This is done
through the log()
method where you at the very least specify an event and a
message. In general it is better to send messages through log()
rather than
with warning()
and stop()
even though the latters will eventually be
caught, as it gives you more control over the logging and what should happen
in the case of an exception.
An example of using log()
in a handler could be:
app$on('header', function(server, id, request) { server$log('info', paste0('request from ', id, ' received'), request) })
Which would log the timepoint the headers of a request has been recieved.