nginx/ngx-rust

Safe accessors for the module configuration

Closed this issue · 2 comments

There's a certain unsafe pattern that is repeated in most of the examples:

let co = unsafe { request.get_module_loc_conf::<ModuleConfig>(&*addr_of!(ngx_http_curl_module)) };
let co = co.expect("module config is none");

The problems here are:

  • ngx_http_curl_module is a global mutable static, requiring unsafe for any access.
  • Nothing guarantees that the type requested matches an actual type allocated for the module config.

Something we can optimize here is to tie config type to a context type and to a module ptr, and hide most of the unsafe details from end user.

E.g.

impl HttpLocationConf for ngx_http_core_module_t {
    type ConfType = ngx_http_core_loc_conf_t;

    fn module() -> &'static ngx_module_t { unsafe { &*addr_of!(ngx_http_core_module) }  }
}

fn get_loc_conf<T: HttpLocationConf>(req) -> <T as HttpLocationConf>::ConfType;

req.get_loc_conf::<ngx_http_core_module_t>().ok_or(...)?;

or with an opposite direction of the mapping

impl HttpModuleConf for ngx_http_core_loc_conf_t {
    const CONTEXT: &HttpModuleConfType = HttpModuleConfType::Location;

    fn module() -> &'static ngx_module_t { unsafe { &*addr_of!(ngx_http_core_module) }  }
}

fn get_conf<T: HttpModuleConf>(req) -> &T;

req.get_conf::<ngx_http_core_loc_conf_t>().ok_or(...)?;

Extending the ngx::http::module::HTTPModule trait is also a viable option.

An important thing to consider is that the module configs can be obtained from ngx_cycle_t, ngx_conf_t, ngx_http_request_t or ngx_http_upstream_t objects and all of those should provide safe accessors.

Possible variation of this interface with functions bound to module itself and using request, event, etc as a parameter.

All names are just examples and may be changed as desired.

/// Trait to be implemented for request, upstream, event, and conf_t
trait HttpMainConf {
    fn get_main_conf<T>(&self) -> Option<&T>;
}

/// Trait with config types and data
/// Should be implemented for each module including standard ones
trait ModuleConf {
    type MainConf;
    /// . . .
    fn module() -> &'static ngx_module_t;
}

/// Trait for main config
/// Similar traits should be defined for server and location configs
/// Each module may implement only necessary traits
trait GetModuleConf<M> {
    fn get_main_conf(o: & impl HttpMainConf) -> Option<&M::MainConf>;
}

/// usage with request
let r: &ngx_http_request_t;

let mcf = Module::get_main_conf(r);

I do not have strong opinion regarding where to implement traits, on module vs an object.

If there are no compelling arguments for module variant. I would prefer the initial proposal (impl HttpLocationConf for ngx_http_core_module_t).