Some basic ideas, all subject to discussion, with some open questions...
Context
User should be able to ask for the &mut Request, &mut Response, &mut BackendRequest, and &mut BackendResponse objects.
Not sure if all should be mutable.
If possible, it should be possible for the macro to communicate which functions should be used where - e.g. if a function uses frontend objects, it should only be allowed in the fronted VCL blocks.
Allow function attribute to indicate where in VCL the function can be used (is this implemented in Varnish?)
User should not access the above objects via context object - macro can do all the logic to figure out who should get what.
Context object should be accessible as &mut only in an exclusive context, e.g. in an event or object constructor.
Filters
Filters consist:
a factory instance, created by the user during event or object creation(?) and registered with VRT_AddVFP or VRT_AddVDP. Any number of these instances can exist.
a filter instance - instantiated by Varnish via a callback to the factory, used, and destroyed the same way.
on destroy event, user should un-register all factories (?)
(?) is there a way for varnish to clean it up automatically, or is there a way to iterate over all registered factory instances to remove and destroy them?
The new "context" object would allow registration of VFP and VDP instances and require &mut self
Can a single VMOD have more than one VDP and/or VFP? If so, we may need a "registration ID" - some token that register_* returns, and unregister_* requires.
Perhaps, logging should use the log crate - which may benefit from standardized interface, standard ENV var support, etc. Note that log crate itself does not define implementation of how logs are stored, only the interface and the helper functions.
Logging could be done via the shared magical context, or if we want to split things up, as a readonly Logger instance. So if a user function wants to log something, it can declare fn user_fn(logger: &Logger).
I might be mis-designing this if logging must go via the workspace(?).
impl Logger {
pub fn log(level: LogLevel, msg: &str) {...}
}
// Also define a few format!-like macros: fail!(logger, "...", ...), error!(...), and a few others.
// User function
fn user_fn(log: &Logger) {
info!(log, "Something cool is {var1} or {var2}");
}
Workspace
Workspace is always exclusive to the call, so perhaps allow user to request it (why?) The question of course is what benefit a user can get by having a direct reference (performance?).
fn user_fn(ws: &mut Workspace) {
// TODO: use cases to justify this
}
Some basic ideas, all subject to discussion, with some open questions...
Context
&mut Request
,&mut Response
,&mut BackendRequest
, and&mut BackendResponse
objects.&mut
only in an exclusive context, e.g. in an event or object constructor.Filters
VRT_AddVFP
orVRT_AddVDP
. Any number of these instances can exist.&mut self
register_*
returns, andunregister_*
requires.Logging
log
crate itself does not define implementation of how logs are stored, only the interface and the helper functions.Logger
instance. So if a user function wants to log something, it can declarefn user_fn(logger: &Logger)
.Workspace
Workspace is always exclusive to the call, so perhaps allow user to request it (why?) The question of course is what benefit a user can get by having a direct reference (performance?).