Emiller's Guide To Nginx Module Development
By Evan Miller
First published: April 28, 2007 (Last edit: January 16, 2013 – changes)
Lucius Fox: The Tumbler? Oh… you wouldn't be interested in that.
To fully appreciate Nginx, the web server, it helps to understand Batman, the comic book character.
Batman is fast. Nginx is fast. Batman fights crime. Nginx fights wasted CPU cycles and memory leaks. Batman performs well under pressure. Nginx, for its part, excels under heavy server loads.
But Batman would be almost nothing without the Batman utility belt.
Figure 1: The Batman utility belt, gripping Christian Bale's love handles.
At any given time, Batman's utility belt might contain a lock pick, several batarangs, bat-cuffs, a bat-tracer, bat-darts, night vision goggles, thermite grenades, smoke pellets, a flashlight, a kryptonite ring, an acetylene torch, or an Apple iPhone. When Batman needs to tranquilize, blind, deafen, stun, track, stop, smoke out, or text-message the enemy, you better believe he's reaching down for his bat-belt. The belt is so crucial to Batman's operations that if Batman had to choose between wearing pants and wearing the utility belt, he would definitely choose the belt. In fact, he *did* choose the utility belt, and that's why Batman wears rubber tights instead of pants (Fig. 1).
Instead of a utility belt, Nginx has a module chain. When Nginx needs to gzip or chunk-encode a response, it whips out a module to do the work. When Nginx blocks access to a resource based on IP address or HTTP auth credentials, a module does the deflecting. When Nginx communicates with Memcache or FastCGI servers, a module is the walkie-talkie.
Batman's utility belt holds a lot of doo-hickeys, but occasionally Batman needs a new tool. Maybe there's a new enemy against whom bat-cuffs and batarangs are ineffectual. Or Batman needs a new ability, like being able to breathe underwater. That's when Batman rings up Lucius Fox to engineer the appropriate bat-gadget.
Figure 2: Bruce Wayne (née Batman) consults with his engineer, Lucius Fox
The purpose of this guide is to teach you the details of Nginx's module chain, so that you may be like Lucius Fox. When you're done with the guide, you'll be able to design and produce high-quality modules that enable Nginx to do things it couldn't do before. Nginx's module system has a lot of nuance and nitty-gritty, so you'll probably want to refer back to this document often. I have tried to make the concepts as clear as possible, but I'll be blunt, writing Nginx modules can still be hard work.
But whoever said making bat-tools would be easy?
Table of Contents
- Prerequisites
- High-Level Overview of Nginx's Module Delegation
- Components of an Nginx Module
- Module Configuration Struct(s)
- Module Directives
- The Module Context
- The Module Definition
- Module Installation
- Handlers
- Anatomy of a Handler (Non-proxying)
- Anatomy of an Upstream (a.k.a. Proxy) Handler
- Handler Installation
- Filters
- Load-Balancers
- The enabling directive
- The registration function
- The upstream initialization function
- The peer initialization function
- The load-balancing function
- The peer release function
- Writing and Compiling a New Nginx Module
- Advanced Topics
- Code References
0. Prerequisites
You should be comfortable with C. Not just "C-syntax"; you should know your way around a struct and not be scared off by pointers and function references, and be cognizant of the preprocessor. If you need to brush up, nothing beats K&R.
Basic understanding of HTTP is useful. You'll be working on a web server, after all.
You should also be familiar with Nginx's configuration file. If you're not, here's the gist of it: there are four contexts (called main, server, upstream, and location) which can contain directives with one or more arguments. Directives in the main context apply to everything; directives in the server context apply to a particular host/port; directives in the upstream context refer to a set of backend servers; and directives in a location context apply only to matching web locations (e.g., "/", "/images", etc.) A location context inherits from the surrounding server context, and a server context inherits from the main context. The upstream context neither inherits nor imparts its properties; it has its own special directives that don't really apply elsewhere. I'll refer to these four contexts quite a bit, so… don't forget them.
Let's get started.
1. High-Level Overview of Nginx's Module Delegation
Nginx modules have three roles we'll cover:
- handlers process a request and produce output
- filters manipulate the output produced by a handler
- load-balancers choose a backend server to send a request to, when more than one backend server is eligible
Modules do all of the "real work" that you might associate with a web server: whenever Nginx serves a file or proxies a request to another server, there's a handler module doing the work; when Nginx gzips the output or executes a server-side include, it's using filter modules. The "core" of Nginx simply takes care of all the network and application protocols and sets up the sequence of modules that are eligible to process a request. The de-centralized architecture makes it possible for *you* to make a nice self-contained unit that does something you want.
Note: Unlike modules in Apache, Nginx modules are not dynamically linked. (In other words, they're compiled right into the Nginx binary.)
How does a module get invoked? Typically, at server startup, each handler gets a chance to attach itself to particular locations defined in the configuration; if more than one handler attaches to a particular location, only one will "win" (but a good config writer won't let a conflict happen). Handlers can return in three ways: all is good, there was an error, or it can decline to process the request and defer to the default handler (typically something that serves static files).
If the handler happens to be a reverse proxy to some set of backend servers, there is room for another type of module: the load-balancer. A load-balancer takes a request and a set of backend servers and decides which server will get the request. Nginx ships with two load-balancing modules: round-robin, which deals out requests like cards at the start of a poker game, and the "IP hash" method, which ensures that a particular client will hit the same backend server across multiple requests.
If the handler does not produce an error, the filters are called. Multiple filters can hook into each location, so that (for example) a response can be compressed and then chunked. The order of their execution is determined at compile-time. Filters have the classic "CHAIN OF RESPONSIBILITY" design pattern: one filter is called, does its work, and then calls the next filter, until the final filter is called, and Nginx finishes up the response.
The really cool part about the filter chain is that each filter doesn't wait for the previous filter to finish; it can process the previous filter's output as it's being produced, sort of like the Unix pipeline. Filters operate on buffers, which are usually the size of a page (4K), although you can change this in your nginx.conf. This means, for example, a module can start compressing the response from a backend server and stream it to the client before the module has received the entire response from the backend. Nice!
So to wrap up the conceptual overview, the typical processing cycle goes:
I say "typically" because Nginx's module invocation is extremely customizable. It places a big burden on module writers to define exactly how and when the module should run (I happen to think too big a burden). Invocation is actually performed through a series of callbacks, and there are a lot of them. Namely, you can provide a function to be executed:
- Just before the server reads the config file
- For every configuration directive for the location and server for which it appears;
- When Nginx initializes the main configuration
- When Nginx initializes the server (i.e., host/port) configuration
- When Nginx merges the server configuration with the main configuration
- When Nginx initializes the location configuration
- When Nginx merges the location configuration with its parent server configuration
- When Nginx's master process starts
- When a new worker process starts
- When a worker process exits
- When the master exits
- Handling a request
- Filtering response headers
- Filtering the response body
- Picking a backend server
- Initiating a request to a backend server
- Re-initiating a request to a backend server
- Processing the response from a backend server
- Finishing an interaction with a backend server
Holy mackerel! It's a bit overwhelming. You've got a lot of power at your disposal, but you can still do something useful using only a couple of these hooks and a couple of corresponding functions. Time to dive into some modules.
2. Components of an Nginx Module
As I said, you have a lot of flexibility when it comes to making an Nginx module. This section will describe the parts that are almost always present. It's intended as a guide for understanding a module, and a reference for when you think you're ready to start writing a module.
2.1. Module Configuration Struct(s)
Modules can define up to three configuration structs, one for the main, server, and location contexts. Most modules just need a location configuration. The naming convention for these is ngx_http_<module name>_(main|srv|loc)_conf_t
. Here's an example, taken from the dav module:
typedef struct {
ngx_uint_t methods;
ngx_flag_t create_full_put_path;
ngx_uint_t access;
} ngx_http_dav_loc_conf_t;
Notice that Nginx has special data types (ngx_uint_t
and ngx_flag_t
); these are just aliases for the primitive data types you know and love (cf. core/ngx_config.h if you're curious).
The elements in the configuration structs are populated by module directives.
2.2. Module Directives
A module's directives appear in a static array of ngx_command_t
s. Here's an example of how they're declared, taken from a small module I wrote:
static ngx_command_t ngx_http_circle_gif_commands[] = {
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("circle_gif_min_radius"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),
NULL },
...
ngx_null_command
};
And here is the declaration of ngx_command_t
(the struct we're declaring), found in core/ngx_conf_file.h:
struct ngx_command_t {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
It seems like a bit much, but each element has a purpose.
The name
is the directive string, no spaces. The data type is an ngx_str_t
, which is usually instantiated with just (e.g.) ngx_str("proxy_pass")
. Note: an ngx_str_t
is a struct with a data
element, which is a string, and a len
element, which is the length of that string. Nginx uses this data structure most places you'd expect a string.
type
is a set of flags that indicate where the directive is legal and how many arguments the directive takes. Applicable flags, which are bitwise-OR'd, are:
NGX_HTTP_MAIN_CONF
: directive is valid in the main configNGX_HTTP_SRV_CONF
: directive is valid in the server (host) configNGX_HTTP_LOC_CONF
: directive is valid in a location configNGX_HTTP_UPS_CONF
: directive is valid in an upstream config
NGX_CONF_NOARGS
: directive can take 0 argumentsNGX_CONF_TAKE1
: directive can take exactly 1 argumentNGX_CONF_TAKE2
: directive can take exactly 2 arguments- …
NGX_CONF_TAKE7
: directive can take exactly 7 arguments
NGX_CONF_FLAG
: directive takes a boolean ("on" or "off")NGX_CONF_1MORE
: directive must be passed at least one argumentNGX_CONF_2MORE
: directive must be passed at least two arguments
There are a few other options, too, see core/ngx_conf_file.h.
The set
struct element is a pointer to a function for setting up part of the module's configuration; typically this function will translate the arguments passed to this directive and save an appropriate value in its configuration struct. This setup function will take three arguments:
- a pointer to an
ngx_conf_t
struct, which contains the arguments passed to the directive - a pointer to the current
ngx_command_t
struct - a pointer to the module's custom configuration struct
This setup function will be called when the directive is encountered. Nginx provides a number of functions for setting particular types of values in the custom configuration struct. These functions include:
ngx_conf_set_flag_slot
: translates "on" or "off" to 1 or 0ngx_conf_set_str_slot
: saves a string as anngx_str_t
ngx_conf_set_num_slot
: parses a number and saves it to anint
ngx_conf_set_size_slot
: parses a data size ("8k", "1m", etc.) and saves it to asize_t
There are several others, and they're quite handy (see core/ngx_conf_file.h). Modules can also put a reference to their own function here, if the built-ins aren't quite good enough.
How do these built-in functions know where to save the data? That's where the next two elements of ngx_command_t
come in, conf
and offset
. conf
tells Nginx whether this value will get saved to the module's main configuration, server configuration, or location configuration (with NGX_HTTP_MAIN_CONF_OFFSET
, NGX_HTTP_SRV_CONF_OFFSET
, or NGX_HTTP_LOC_CONF_OFFSET
). offset
then specifies which part of this configuration struct to write to.
 It's often NULL
.
The commands array is terminated with ngx_null_command
as the last element.
2.3. The Module Context
This is a static ngx_http_module_t
struct, which just has a bunch of function references for creating the three configurations and merging them together. Its name is ngx_http_<module name>_module_ctx
. In order, the function references are:
- preconfiguration
- postconfiguration
- creating the main conf (i.e., do a malloc and set defaults)
- initializing the main conf (i.e., override the defaults with what's in nginx.conf)
- creating the server conf
- merging it with the main conf
- creating the location conf
- merging it with the server conf
These take different arguments depending on what they're doing. Here's the struct definition, taken from http/ngx_http_config.h, so you can see the different function signatures of the callbacks:
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
You can set functions you don't need to NULL
, and Nginx will figure it out.
Most handlers just use the last two: a function to allocate memory for location-specific configuration (called ngx_http_<module name>_create_loc_conf
), and a function to set defaults and merge this configuration with any inherited configuration (called ngx_http_<module name >_merge_loc_conf
). The merge function is also responsible for producing an error if the configuration is invalid; these errors halt server startup.
Here's an example module context struct:
static ngx_http_module_t ngx_http_circle_gif_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_circle_gif_create_loc_conf, /* create location configuration */
ngx_http_circle_gif_merge_loc_conf /* merge location configuration */
};
Time to dig in deep a little bit. These configuration callbacks look quite similar across all modules and use the same parts of the Nginx API, so they're worth knowing about.
2.3.1. create_loc_conf
Here's what a bare-bones create_loc_conf function looks like, taken from the circle_gif module I wrote (see the the source). It takes a directive struct (ngx_conf_t
) and returns a newly created module configuration struct (in this case ngx_http_circle_gif_loc_conf_t
).
static void *
ngx_http_circle_gif_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_circle_gif_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_circle_gif_loc_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
conf->min_radius = NGX_CONF_UNSET_UINT;
conf->max_radius = NGX_CONF_UNSET_UINT;
return conf;
}
First thing to notice is Nginx's memory allocation; it takes care of the free
'ing as long as the module uses ngx_palloc
(a malloc
wrapper) or ngx_pcalloc
(a calloc
wrapper).
The possible UNSET constants are NGX_CONF_UNSET_UINT
, NGX_CONF_UNSET_PTR
, NGX_CONF_UNSET_SIZE
, NGX_CONF_UNSET_MSEC
, and the catch-all NGX_CONF_UNSET
. UNSET tell the merging function that the value should be overridden.
2.3.2. merge_loc_conf
Here's the merging function used in the circle_gif module:
static char *
ngx_http_circle_gif_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_circle_gif_loc_conf_t *prev = parent;
ngx_http_circle_gif_loc_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->min_radius, prev->min_radius, 10);
ngx_conf_merge_uint_value(conf->max_radius, prev->max_radius, 20);
if (conf->min_radius < 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"min_radius must be equal or more than 1");
return NGX_CONF_ERROR;
}
if (conf->max_radius < conf->min_radius) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"max_radius must be equal or more than min_radius");
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
Notice first that Nginx provides nice merging functions for different data types (ngx_conf_merge_<data type>_value
); the arguments are
- this location's value
- the value to inherit if #1 is not set
- the default if neither #1 nor #2 is set
The result is then stored in the first argument. Available merge functions include ngx_conf_merge_size_value
, ngx_conf_merge_msec_value
, and others. See core/ngx_conf_file.h for a full list.
Trivia question: How do these functions write to the first argument, since the first argument is passed in by value?
Answer: these functions are defined by the preprocessor (so they expand to a few "if" statements and assignments before reaching the compiler).
Notice also how errors are produced; the function writes something to the log file, and returns NGX_CONF_ERROR
. That return code halts server startup. (Since the message is logged at level NGX_LOG_EMERG
, the message will also go to standard out; FYI, core/ngx_log.h has a list of log levels.)
2.4. The Module Definition
Next we add one more layer of indirection, the ngx_module_t
struct. The variable is called ngx_http_<module name>_module
. This is where references to the context and directives go, as well as the remaining callbacks (exit thread, exit process, etc.). The module definition is sometimes used as a key to look up data associated with a particular module. The module definition usually looks like this:
ngx_module_t ngx_http_<module name>_module = {
NGX_MODULE_V1,
&ngx_http_<module name>_module_ctx, /* module context */
ngx_http_<module name>_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
…substituting <module name> appropriately. Modules can add callbacks for process/thread creation and death, but most modules keep things simple. (For the arguments passed to each callback, see core/ngx_conf_file.h.)
2.5. Module Installation
The proper way to install a module depends on whether the module is a handler, filter, or load-balancer; so the details are reserved for those respective sections.
3. Handlers
Now we'll put some trivial modules under the microscope to see how they work.
3.1. Anatomy of a Handler (Non-proxying)
Handlers typically do four things: get the location configuration, generate an appropriate response, send the header, and send the body. A handler has one argument, the request struct. A request struct has a lot of useful information about the client request, such as the request method, URI, and headers. We'll go over these steps one by one.
3.1.1. Getting the location configuration
This part's easy. All you need to do is call ngx_http_get_module_loc_conf
and pass in the current request struct and the module definition. Here's the relevant part of my circle gif handler:
static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *circle_gif_config;
circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
...
Now I've got access to all the variables that I set up in my merge function.
3.1.2. Generating a response
This is the interesting part where modules actually do work.
The request struct will be helpful here, particularly these elements:
typedef struct {
...
/* the memory pool, used in the ngx_palloc functions */
ngx_pool_t *pool;
ngx_str_t uri;
ngx_str_t args;
ngx_http_headers_in_t headers_in;
...
} ngx_http_request_t;
uri
is the path of the request, e.g. "/query.cgi".
args
is the part of the request after the question mark (e.g. "name=john").
headers_in
has a lot of useful stuff, such as cookies and browser information, but many modules don't need anything from it. See http/ngx_http_request.h if you're interested.
This should be enough information to produce some useful output. The full ngx_http_request_t
struct can be found in http/ngx_http_request.h.
3.1.3. Sending the header
The response headers live in a struct called headers_out
referenced by the request struct. The handler sets the ones it wants and then calls ngx_http_send_header(r)
. Some useful parts of headers_out
include:
typedef stuct {
...
ngx_uint_t status;
size_t content_type_len;
ngx_str_t content_type;
ngx_table_elt_t *content_encoding;
off_t content_length_n;
time_t date_time;
time_t last_modified_time;
..
} ngx_http_headers_out_t;
(The rest can be found in http/ngx_http_request.h.)
So for example, if a module were to set the Content-Type to "image/gif", Content-Length to 100, and return a 200 OK response, this code would do the trick:
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = 100;
r->headers_out.content_type.len = sizeof("image/gif") - 1;
r->headers_out.content_type.data = (u_char *) "image/gif";
ngx_http_send_header(r);
Most legal HTTP headers are available (somewhere) for your setting pleasure. However, some headers are a bit trickier to set than the ones you see above; for example, content_encoding
has type (ngx_table_elt_t*)
, so the module must allocate memory for it. This is done with a function called ngx_list_push
, which takes in an ngx_list_t
(similar to an array) and returns a reference to a newly created member of the list (of type ngx_table_elt_t
). The following code sets the Content-Encoding to "deflate" and sends the header:
r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.content_encoding == NULL) {
return NGX_ERROR;
}
r->headers_out.content_encoding->hash = 1;
r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
r->headers_out.content_encoding->value.len = sizeof("deflate") - 1;
r->headers_out.content_encoding->value.data = (u_char *) "deflate";
ngx_http_send_header(r);
This mechanism is usually used when a header can have multiple values simultaneously; it (theoretically) makes it easier for filter modules to add and delete certain values while preserving others, because they don't have to resort to string manipulation.
3.1.4. Sending the body
Now that the module has generated a response and put it in memory, it needs to assign the response to a special buffer, and then assign the buffer to a chain link, and then call the "send body" function on the chain link.
What are the chain links for? Nginx lets handler modules generate (and filter modules process) responses one buffer at a time; each chain link keeps a pointer to the next link in the chain, or NULL
if it's the last one. We'll keep it simple and assume there is just one buffer.
First, a module will declare the buffer and the chain link:
ngx_buf_t *b;
ngx_chain_t out;
The next step is to allocate the buffer and point our response data to it:
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"Failed to allocate response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = some_bytes; /* first position in memory of the data */
b->last = some_bytes + some_bytes_length; /* last position */
b->memory = 1; /* content is in read-only memory */
/* (i.e., filters should copy it rather than rewrite in place) */
b->last_buf = 1; /* there will be no more buffers in the request */
Now the module attaches it to the chain link:
out.buf = b;
out.next = NULL;
FINALLY, we send the body, and return the status code of the output filter chain all in one go:
return ngx_http_output_filter(r, &out);
Buffer chains are a critical part of Nginx's IO model, so you should be comfortable with how they work.
Trivia question: Why does the buffer have the last_buf
variable, when we can tell we're at the end of a chain by checking "next" for NULL
?
Answer: A chain might be incomplete, i.e., have multiple buffers, but not all the buffers in this request or response. So some buffers are at the end of the chain but not the end of a request. This brings us to…
3.2. Anatomy of an Upstream (a.k.a Proxy) Handler
I waved my hands a bit about having your handler generate a response. Sometimes you'll be able to get that response just with a chunk of C code, but often you'll want to talk to another server (for example, if you're writing a module to implement another network protocol). You could do all of the network programming yourself, but what happens if you receive a partial response? You don't want to block the primary event loop with your own event loop while you're waiting for the rest of the response.  Fortunately, Nginx lets you hook right into its own mechanisms for dealing with back-end servers (called "upstreams"), so your module can talk to another server without getting in the way of other requests. This section describes how a module talks to an upstream, such as Memcached, FastCGI, or another HTTP server.
3.2.1. Summary of upstream callbacks
Unlike the handler function for other modules, the handler function of an upstream module does little "real work". It does not call ngx_http_output_filter
. It merely sets callbacks that will be invoked when the upstream server is ready to be written to and read from. There are actually 6 available hooks:
create_request
crafts a request buffer (or chain of them) to be sent to the upstream
reinit_request
is called if the connection to the back-end is reset (just before create_request
is called for the second time)
process_header
processes the first bit of the upstream's response, and usually saves a pointer to the upstream's "payload"
abort_request
is called if the client aborts the request
finalize_request
is called when Nginx is finished reading from the upstream
input_filter
is a body filter that can be called on the response body (e.g., to remove a trailer)
How do these get attached? An example is in order. Here's a simplified version of the proxy module's handler:
static ngx_int_t
ngx_http_proxy_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_proxy_loc_conf_t *plcf;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
/* set up our upstream struct */
u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
if (u == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->peer.log = r->connection->log;
u->peer.log_error = NGX_ERROR_ERR;
u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
u->conf = &plcf->upstream;
/* attach the callback functions */
u->create_request = ngx_http_proxy_create_request;
u->reinit_request = ngx_http_proxy_reinit_request;
u->process_header = ngx_http_proxy_process_status_line;
u->abort_request = ngx_http_proxy_abort_request;
u->finalize_request = ngx_http_proxy_finalize_request;
r->upstream = u;
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
It does a bit of housekeeping, but the important parts are the callbacks. Also notice the bit about ngx_http_read_client_request_body
. That's setting another callback for when Nginx has finished reading from the client.
What will each of these callbacks do? Usually, reinit_request
, abort_request
, and finalize_request
will set or reset some sort of internal state and are only a few lines long. The real workhorses are create_request
and process_header
.
3.2.2. The create_request callback
For the sake of simplicity, let's suppose I have an upstream server that reads in one character and prints out two characters. What would my functions look like?
The create_request
needs to allocate a buffer for the single-character request, allocate a chain link for that buffer, and then point the upstream struct to that chain link. It would look like this:
static ngx_int_t
ngx_http_character_server_create_request(ngx_http_request_t *r)
{
/* make a buffer and chain */
ngx_buf_t *b;
ngx_chain_t *cl;
b = ngx_create_temp_buf(r->pool, sizeof("a") - 1);
if (b == NULL)
return NGX_ERROR;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL)
return NGX_ERROR;
/* hook the buffer to the chain */
cl->buf = b;
/* chain to the upstream */
r->upstream->request_bufs = cl;
/* now write to the buffer */
b->pos = "a";
b->last = b->pos + sizeof("a") - 1;
return NGX_OK;
}
That wasn't so bad, was it? Of course, in reality you'll probably want to use the request URI in some meaningful way. It's available as an ngx_str_t
in r->uri
, and the GET paramaters are in r->args
, and don't forget you also have access to the request headers and cookies.
3.2.3. The process_header callback
Now it's time for the process_header
. Just as create_request
added a pointer to the request body, process_header
shifts the response pointer to the part that the client will receive. It also reads in the header from the upstream and sets the client response headers accordingly.
Here's a bare-minimum example, reading in that two-character response. Let's suppose the first character is the "status" character. If it's a question mark, we want to return a 404 File Not Found to the client and disregard the other character. If it's a space, then we want to return the other character to the client along with a 200 OK response. All right, it's not the most useful protocol, but it's a good demonstration. How would we write this process_header
function?
static ngx_int_t
ngx_http_character_server_process_header(ngx_http_request_t *r)
{
ngx_http_upstream_t *u;
u = r->upstream;
/* read the first character */
switch(u->buffer.pos[0]) {
case '?':
r->header_only; /* suppress this buffer from the client */
u->headers_in.status_n = 404;
break;
case ' ':
u->buffer.pos++; /* move the buffer to point to the next character */
u->headers_in.status_n = 200;
break;
}
return NGX_OK;
}
That's it. Manipulate the header, change the pointer, it's done. Notice that headers_in
is actually a response header struct like we've seen before (cf. http/ngx_http_request.h), but it can be populated with the headers from the upstream. A real proxying module will do a lot more header processing, not to mention error handling, but you get the main idea.
But.. what if we don't have the whole header from the upstream in one buffer?
3.2.4. Keeping state
Well, remember how I said that abort_request
, reinit_request
, and finalize_request
could be used for resetting internal state? That's because many upstream modules have internal state. The module will need to define a custom context struct to keep track of what it has read so far from an upstream. This is NOT the same as the "Module Context" referred to above. That's of a pre-defined type, whereas the custom context can have whatever elements and data you need (it's your struct). This context struct should be instantiated inside the create_request
function, perhaps like this:
ngx_http_character_server_ctx_t *p; /* my custom context struct */
p = ngx_pcalloc(r->pool, sizeof(ngx_http_character_server_ctx_t));
if (p == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, p, ngx_http_character_server_module);
That last line essentially registers the custom context struct with a particular request and module name for easy retrieval later. Whenever you need this context struct (probably in all the other callbacks), just do:
ngx_http_proxy_ctx_t *p;
p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
And p
will have the current state. Set it, reset it, increment, decrement, shove arbitrary data in there, whatever you want. This is a great way to use a persistent state machine when reading from an upstream that returns data in chunks, again without blocking the primary event loop. Nice!
3.3. Handler Installation
Handlers are installed by adding code to the callback of the directive that enables the module. For example, my circle gif ngx_command_t
looks like this:
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
0,
0,
NULL }
The callback is the third element, in this case ngx_http_circle_gif
. Recall that the arguments to this callback are the directive struct (ngx_conf_t
, which holds the user's arguments), the relevant ngx_command_t
struct, and a pointer to the module's custom configuration struct. For my circle gif module, the function looks like:
static char *
ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_circle_gif_handler;
return NGX_CONF_OK;
}
There are two steps here: first, get the "core" struct for this location, then assign a handler to it. Pretty simple, eh?
I've said all I know about handler modules. It's time to move onto filter modules, the components in the output filter chain.
4. Filters
Filters manipulate responses generated by handlers. Header filters manipulate the HTTP headers, and body filters manipulate the response content.
4.1. Anatomy of a Header Filter
A header filter consists of three basic steps:
- Decide whether to operate on this response
- Operate on the response
- Call the next filter
To take an example, here's a simplified version of the "not modified" header filter, which sets the status to 304 Not Modified if the client's If-Modified-Since header matches the response's Last-Modified header. Note that header filters take in the ngx_http_request_t
struct as the only argument, which gets us access to both the client headers and soon-to-be-sent response headers.
static
ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
time_t if_modified_since;
if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
r->headers_in.if_modified_since->value.len);
/* step 1: decide whether to operate */
if (if_modified_since != NGX_ERROR &&
if_modified_since == r->headers_out.last_modified_time) {
/* step 2: operate on the header */
r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
r->headers_out.content_type.len = 0;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
}
/* step 3: call the next filter */
return ngx_http_next_header_filter(r);
}
The headers_out
structure is just the same as we saw in the section about handlers (cf. http/ngx_http_request.h), and can be manipulated to no end.
4.2. Anatomy of a Body Filter
The buffer chain makes it a little tricky to write a body filter, because the body filter can only operate on one buffer (chain link) at a time. The module must decide whether to overwrite the input buffer, replace the buffer with a newly allocated buffer, or insert a new buffer before or after the buffer in question. To complicate things, sometimes a module will receive several buffers so that it has an incomplete buffer chain that it must operate on. Unfortunately, Nginx does not provide a high-level API for manipulating the buffer chain, so body filters can be difficult to understand (and to write). But, here are some operations you might see in action.
A body filter's prototype might look like this (example taken from the "chunked" filter in the Nginx source):
static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
The first argument is our old friend the request struct. The second argument is a pointer to the head of the current partial chain (which could contain 0, 1, or more buffers).
Let's take a simple example. Suppose we want to insert the text "<l!-- Served by Nginx -->" to the end of every request. First, we need to figure out if the response's final buffer is included in the buffer chain we were given. Like I said, there's not a fancy API, so we'll be rolling our own for loop:
ngx_chain_t *chain_link;
int chain_contains_last_buffer = 0;
chain_link = in;
for ( ; ; ) {
if (chain_link->buf->last_buf)
chain_contains_last_buffer = 1;
if (chain_link->next == NULL)
break;
chain_link = chain_link->next;
}
Now let's bail out if we don't have that last buffer:
if (!chain_contains_last_buffer)
return ngx_http_next_body_filter(r, in);
Super, now the last buffer is stored in chain_link. Now we allocate a new buffer:
ngx_buf_t *b;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
And put some data in it:
b->pos = (u_char *) "<!-- Served by Nginx -->";
b->last = b->pos + sizeof("<!-- Served by Nginx -->") - 1;
And hook the buffer into a new chain link:
ngx_chain_t *added_link;
added_link = ngx_alloc_chain_link(r->pool);
if (added_link == NULL)
return NGX_ERROR;
added_link->buf = b;
added_link->next = NULL;
Finally, hook the new chain link to the final chain link we found before:
chain_link->next = added_link;
And reset the "last_buf" variables to reflect reality:
chain_link->buf->last_buf = 0;
added_link->buf->last_buf = 1;
And pass along the modified chain to the next output filter:
return ngx_http_next_body_filter(r, in);
The resulting function takes much more effort than what you'd do with, say, mod_perl ($response->body =~ s/$/<!-- Served by mod_perl -->/
), but the buffer chain is a very powerful construct, allowing programmers to process data incrementally so that the client gets something as soon as possible. However, in my opinion, the buffer chain desperately needs a cleaner interface so that programmers can't leave the chain in an inconsistent state. For now, manipulate it at your own risk.
4.3. Filter Installation
Filters are installed in the post-configuration step. We install both header filters and body filters in the same place.
Let's take a look at the chunked filter module for a simple example. Its module context looks like this:
static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_chunked_filter_init, /* postconfiguration */
...
};
Here's what happens in ngx_http_chunked_filter_init
:
static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_chunked_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_chunked_body_filter;
return NGX_OK;
}
What's going on here? Well, if you remember, filters are set up with a CHAIN OF RESPONSIBILITY. When a handler generates a response, it calls two functions: ngx_http_output_filter
, which calls the global function reference ngx_http_top_body_filter
; and ngx_http_send_header
, which calls the global function reference ngx_http_top_header_filter
.
ngx_http_top_body_filter
and ngx_http_top_header_filter
are the respective "heads" of the body and header filter chains. Each "link" on the chain keeps a function reference to the next link in the chain (the references are called ngx_http_next_body_filter
and ngx_http_next_header_filter
). When a filter is finished executing, it just calls the next filter, until a specially defined "write" filter is called, which wraps up the HTTP response. What you see in this filter_init function is the module adding itself to the filter chains; it keeps a reference to the old "top" filters in its own "next" variables and declares its functions to be the new "top" filters. (Thus, the last filter to be installed is the first to be executed.)
Side note: how does this work exactly?
Each filter either returns an error code or uses this as the return statement:
return ngx_http_next_body_filter();
Thus, if the filter chain reaches the (specially-defined) end of the chain, an "OK" response is returned, but if there's an error along the way, the chain is cut short and Nginx serves up the appropriate error message. It's a singly-linked list with fast failures implemented solely with function references. Brilliant.
5. Load-Balancers
A load-balancer is just a way to decide which backend server will receive a particular request; implementations exist for distributing requests in round-robin fashion or hashing some information about the request. This section will describe both a load-balancer's installation and its invocation, using the upstream_hash module (full source) as an example. upstream_hash chooses a backend by hashing a variable specified in nginx.conf.
A load-balancing module has six pieces:
- The enabling configuration directive (e.g,
hash;
) will call a registration function - The registration function will define the legal
server
options (e.g.,weight=
) and register an upstream initialization function - The upstream initialization function is called just after the configuration is validated, and it:
- resolves the
server
names to particular IP addresses - allocates space for sockets
- sets a callback to the peer initialization function
- resolves the
- the peer initialization function, called once per request, sets up data structures that the load-balancing function will access and manipulate;
- the load-balancing function decides where to route requests; it is called at least once per client request (more, if a backend request fails). This is where the interesting stuff happens.
- and finally, the peer release function can update statistics after communication with a particular backend server has finished (whether successfully or not)
It's a lot, but I'll break it down into pieces.
5.1. The enabling directive
Directive declarations, recall, specify both where they're valid and a function to call when they're encountered. A directive for a load-balancer should have the NGX_HTTP_UPS_CONF
flag set, so that Nginx knows this directive is only valid inside an upstream
block. It should provide a pointer to a registration function. Here's the directive declaration from the upstream_hash module:
{ ngx_string("hash"),
NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
ngx_http_upstream_hash,
0,
0,
NULL },
Nothing new there.
5.2. The registration function
The callback ngx_http_upstream_hash
above is the registration function, so named (by me) because it registers an upstream initialization function with the surrounding upstream
configuration. In addition, the registration function defines which options to the server
directive are legal inside this particular upstream
block (e.g., weight=
, fail_timeout=
). Here's the registration function of the upstream_hash module:
ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_upstream_srv_conf_t *uscf;
ngx_http_script_compile_t sc;
ngx_str_t *value;
ngx_array_t *vars_lengths, *vars_values;
value = cf->args->elts;
/* the following is necessary to evaluate the argument to "hash" as a $variable */
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
vars_lengths = NULL;
vars_values = NULL;
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &vars_lengths;
sc.values = &vars_values;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* end of $variable stuff */
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
/* the upstream initialization function */
uscf->peer.init_upstream = ngx_http_upstream_init_hash;
uscf->flags = NGX_HTTP_UPSTREAM_CREATE;
/* OK, more $variable stuff */
uscf->values = vars_values->elts;
uscf->lengths = vars_lengths->elts;
/* set a default value for "hash_method" */
if (uscf->hash_function == NULL) {
uscf->hash_function = ngx_hash_key;
}
return NGX_CONF_OK;
}
Aside from jumping through hoops so we can evaluation $variable
later, it's pretty straightforward; assign a callback, set some flags. What flags are available?
NGX_HTTP_UPSTREAM_CREATE
: let there beserver
directives in this upstream block. I can't think of a situation where you wouldn't use this.NGX_HTTP_UPSTREAM_WEIGHT
: let theserver
directives take aweight=
optionNGX_HTTP_UPSTREAM_MAX_FAILS
: allow themax_fails=
optionNGX_HTTP_UPSTREAM_FAIL_TIMEOUT
: allow thefail_timeout=
optionNGX_HTTP_UPSTREAM_DOWN
: allow thedown
optionNGX_HTTP_UPSTREAM_BACKUP
: allow thebackup
option
Each module will have access to these configuration values. It's up to the module to decide what to do with them. That is, max_fails
will not be automatically enforced; all the failure logic is up to the module author. More on that later. For now, we still haven't finished followed the trail of callbacks. Next up, we have the upstream initialization function (the init_upstream
callback in the previous function).
5.3. The upstream initialization function
The purpose of the upstream initialization function is to resolve the host names, allocate space for sockets, and assign (yet another) callback. Here's how upstream_hash does it:
ngx_int_t
ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
ngx_uint_t i, j, n;
ngx_http_upstream_server_t *server;
ngx_http_upstream_hash_peers_t *peers;
/* set the callback */
us->peer.init = ngx_http_upstream_init_upstream_hash_peer;
if (!us->servers) {
return NGX_ERROR;
}
server = us->servers->elts;
/* figure out how many IP addresses are in this upstream block. */
/* remember a domain name can resolve to multiple IP addresses. */
for (n = 0, i = 0; i < us->servers->nelts; i++) {
n += server[i].naddrs;
}
/* allocate space for sockets, etc */
peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_peers_t)
+ sizeof(ngx_peer_addr_t) * (n - 1));
if (peers == NULL) {
return NGX_ERROR;
}
peers->number = n;
/* one port/IP address per peer */
for (n = 0, i = 0; i < us->servers->nelts; i++) {
for (j = 0; j < server[i].naddrs; j++, n++) {
peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
peers->peer[n].socklen = server[i].addrs[j].socklen;
peers->peer[n].name = server[i].addrs[j].name;
}
}
/* save a pointer to our peers for later */
us->peer.data = peers;
return NGX_OK;
}
This function is a bit more involved than one might hope. Most of the work seems like it should be abstracted, but it's not, so that's what we live with. One strategy for simplifying things is to call the upstream initialization function of another module, have it do all the dirty work (peer allocation, etc), and then override the us->peer.init
callback afterwards. For an example, see http/modules/ngx_http_upstream_ip_hash_module.c.
The important bit from our point of view is setting a pointer to the peer initialization function, in this case ngx_http_upstream_init_upstream_hash_peer
.
5.4. The peer initialization function
The peer initialization function is called once per request. It sets up a data structure that the module will use as it tries to find an appropriate backend server to service that request; this structure is persistent across backend re-tries, so it's a convenient place to keep track of the number of connection failures, or a computed hash value. By convention, this struct is called ngx_http_upstream_<module name>_peer_data_t
.
In addition, the peer initalization function sets up two callbacks:
get
: the load-balancing functionfree
: the peer release function (usually just updates some statistics when a connection finishes)
As if that weren't enough, it also initalizes a variable called tries
. As long as tries
is positive, nginx will keep retrying this load-balancer. When tries
is zero, nginx will give up. It's up to the get
and free
functions to set tries
appropriately.
Here's a peer initialization function from the upstream_hash module:
static ngx_int_t
ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
{
ngx_http_upstream_hash_peer_data_t *uhpd;
ngx_str_t val;
/* evaluate the argument to "hash" */
if (ngx_http_script_run(r, &val, us->lengths, 0, us->values) == NULL) {
return NGX_ERROR;
}
/* data persistent through the request */
uhpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t)
+ sizeof(uintptr_t)
* ((ngx_http_upstream_hash_peers_t *)us->peer.data)->number
/ (8 * sizeof(uintptr_t)));
if (uhpd == NULL) {
return NGX_ERROR;
}
/* save our struct for later */
r->upstream->peer.data = uhpd;
uhpd->peers = us->peer.data;
/* set the callbacks and initialize "tries" to "hash_again" + 1*/
r->upstream->peer.free = ngx_http_upstream_free_hash_peer;
r->upstream->peer.get = ngx_http_upstream_get_hash_peer;
r->upstream->peer.tries = us->retries + 1;
/* do the hash and save the result */
uhpd->hash = us->hash_function(val.data, val.len);
return NGX_OK;
}
That wasn't so bad. Now we're ready to pick an upstream server.
5.5. The load-balancing function
It's time for the main course. The real meat and potatoes. This is where the module picks an upstream. The load-balancing function's prototype looks like:
static ngx_int_t
ngx_http_upstream_get_<module_name>_peer(ngx_peer_connection_t *pc, void *data);
data
is our struct of useful information concerning this client connection. pc
will have information about the server we're going to connect to. The job of the load-balancing function is to fill in values for pc->sockaddr
, pc->socklen
, and pc->name
. If you know some network programming, then those variable names might be familiar; but they're actually not very important to the task at hand. We don't care what they stand for; we just want to know where to find appropriate values to fill them.
This function must find a list of available servers, choose one, and assign its values to pc
. Let's look at how upstream_hash does it.
upstream_hash previously stashed the server list into the ngx_http_upstream_hash_peer_data_t
struct back in the call to ngx_http_upstream_init_hash
(above). This struct is now available as data
:
ngx_http_upstream_hash_peer_data_t *uhpd = data;
The list of peers is now stored in uhpd->peers->peer
. Let's pick a peer from this array by dividing the computed hash value by the number of servers:
ngx_peer_addr_t *peer = &uhpd->peers->peer[uhpd->hash % uhpd->peers->number];
Now for the grand finale:
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
return NGX_OK;
That's all! If the load-balancer returns NGX_OK
, it means, "go ahead and try this server". If it returns NGX_BUSY
, it means all the backend hosts are unavailable, and Nginx should try again.
But… how do we keep track of what's unavailable? And what if we don't want it to try again?
5.6. The peer release function
The peer release function operates after an upstream connection takes place; its purpose is to track failures. Here is its function prototype:
void
ngx_http_upstream_free_<module name>_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state);
The first two parameters are just the same as we saw in the load-balancer function. The third parameter is a state
variable, which indicates whether the connection was successful. It may contain two values bitwise OR'd together: NGX_PEER_FAILED
(the connection failed) and NGX_PEER_NEXT
(either the connection failed, or it succeeded but the application returned an error). Zero means the connection succeeded.
It's up to the module author to decide what to do about these failure events. If they are to be used at all, the results should be stored in data
, a pointer to the custom per-request data struct.
But the crucial purpose of the peer release function is to set pc->tries
to zero if you don't want Nginx to keep trying this load-balancer during this request. The simplest peer release function would look like this:
pc->tries = 0;
That would ensure that if there's ever an error reaching a backend server, a 502 Bad Proxy error will be returned to the client.
Here's a more complicated example, taken from the upstream_hash module. If a backend connection fails, it marks it as failed in a bit-vector (called tried
, an array of type uintptr_t
), then keeps choosing a new backend until it finds one that has not failed.
#define ngx_bitvector_index(index) index / (8 * sizeof(uintptr_t))
#define ngx_bitvector_bit(index) (uintptr_t) 1 << index % (8 * sizeof(uintptr_t))
static void
ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
ngx_http_upstream_hash_peer_data_t *uhpd = data;
ngx_uint_t current;
if (state & NGX_PEER_FAILED
&& --pc->tries)
{
/* the backend that failed */
current = uhpd->hash % uhpd->peers->number;
/* mark it in the bit-vector */
uhpd->tried[ngx_bitvector_index(current)] |= ngx_bitvector_bit(current);
do { /* rehash until we're out of retries or we find one that hasn't been tried */
uhpd->hash = ngx_hash_key((u_char *)&uhpd->hash, sizeof(ngx_uint_t));
current = uhpd->hash % uhpd->peers->number;
} while ((uhpd->tried[ngx_bitvector_index(current)] & ngx_bitvector_bit(current)) && --pc->tries);
}
}
This works because the load-balancing function will just look at the new value of uhpd->hash
.
Many applications won't need retry or high-availability logic, but it's possible to provide it with just a few lines of code like you see here.
6. Writing and Compiling a New Nginx Module
So by now, you should be prepared to look at an Nginx module and try to understand what's going on (and you'll know where to look for help). Take a look in src/http/modules/ to see the available modules. Pick a module that's similar to what you're trying to accomplish and look through it. Stuff look familiar? It should. Refer between this guide and the module source to get an understanding about what's going on.
But Emiller didn't write a Balls-In Guide to Reading Nginx Modules. Hell no. This is a Balls-Out Guide. We're not reading. We're writing. Creating. Sharing with the world.
First thing, you're going to need a place to work on your module. Make a folder for your module anywhere on your hard drive, but separate from the Nginx source (and make sure you have the latest copy from nginx.net). Your new folder should contain two files to start with:
- "config"
- "ngx_http_<your module>_module.c"
The "config" file will be included by ./configure
, and its contents will depend on the type of module.
"config" for filter modules:
ngx_addon_name=ngx_http_<your module>_module
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_<your module>_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c"
"config" for other modules:
ngx_addon_name=ngx_http_<your module>_module
HTTP_MODULES="$HTTP_MODULES ngx_http_<your module>_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c"
Now for your C file. I recommend copying an existing module that does something similar to what you want, but rename it "ngx_http_<your module>_module.c". Let this be your model as you change the behavior to suit your needs, and refer to this guide as you understand and refashion the different pieces.
When you're ready to compile, just go into the Nginx directory and type
./configure --add-module=path/to/your/new/module/directory
and then make
and make install
like you normally would. If all goes well, your module will be compiled right in. Nice, huh? No need to muck with the Nginx source, and adding your module to new versions of Nginx is a snap, just use that same ./configure
command. By the way, if your module needs any dynamically linked libraries, you can add this to your "config" file:
CORE_LIBS="$CORE_LIBS -lfoo"
Where foo
is the library you need. If you make a cool or useful module, be sure to send a note to the Nginx mailing list and share your work.
7. Advanced Topics
This guide covers the basics of Nginx module development. For tips on writing more sophisticated modules, be sure to check out Emiller's Advanced Topics In Nginx Module Development.
Appendix A: Code References
Nginx source tree (cross-referenced)
Nginx module directory (cross-referenced)
Appendix B: Changelog
- January 16, 2013: Corrected code sample in 5.5.
- December 20, 2011: Corrected code sample in 4.2 (one more time).
- March 14, 2011: Corrected code sample in 4.2 (again).
- November 11, 2009: Corrected code sample in 4.2.
- August 13, 2009: Reorganized, and moved Advanced Topics to a separate article.
- July 23, 2009: Corrected code sample in 3.5.3.
- December 24, 2008: Corrected code sample in 3.4.
- July 14, 2008: Added information about subrequests; slight reorganization
- July 12, 2008: Added Grzegorz Nosek's guide to shared memory
- July 2, 2008: Corrected "config" file for filter modules; rewrote introduction; added TODO section
- May 28, 2007: Changed the load-balancing example to the simpler upstream_hash module
- May 19, 2007: Corrected bug in body filter example
- May 4, 2007: Added information about load-balancers
- April 28, 2007: Initial draft
Back to Evan Miller's home page – Follow on Twitter – Subscribe to RSS


Methods
combineBlackResults(b1, b2) → {*}
Combine two sets of permissions generated from blacklists
Permissions revoked from either trump permissions granted in the other
If one blacklist has tables the other doesn't, its permissions are obeyed for those tables
Parameters:
| Name | Type | Description |
|---|---|---|
b1 |
the output from getGroupPerms for the first blacklist | |
b2 |
the output from getGroupPerms for the second blacklist |
- Source:
Returns:
- Type
- *
combinePermissionSets(tableList1, tableList2, compareFunc) → {*}
"private" function used by the functions that combine results
"private" in the sense that it's not a function that is used outside of this file
Parameters:
| Name | Type | Description |
|---|---|---|
tableList1 |
the output from getGroupPerms | |
tableList2 |
the output from getGroupPerms | |
compareFunc |
- Source:
Returns:
- Type
- *
combineWhiteAndBlackResults(white, black) → {*}
Combine two sets of permissions generated from a whitelist and blacklist
Permissions revoked from either trump permissions granted in the other
if blacklist has tables whitelist doesn't, permissions on those tables are revoked
if whitelist has tables blacklist doesn't, permissions on those tables obey whitelist permissions
Parameters:
| Name | Type | Description |
|---|---|---|
white |
the output from getGroupPerms for the whitelist | |
black |
the output from getGroupPerms for the blacklist |
- Source:
Returns:
- Type
- *
combineWhiteResults(w1, w2)
Combine two sets of permissions generated from whitelists
Permissions granted from either trump permissions revoked in the other
If one whitelist has tables the other doesn't, its permissions are obeyed for those tables
Parameters:
| Name | Type | Description |
|---|---|---|
w1 |
the output from getGroupPerms for the first whitelist | |
w2 |
the output from getGroupPerms for the second whitelist |
- Source:
getGroupPerms(groups, permSpecs, isBlackList) → {*}
Get the permissions of a given user based on the groups he/she belongs to
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
groups |
an array of the groups the user belongs to | ||
permSpecs |
a json object specifying which permissions to grant for which groups on which resources (see test files for examples) | ||
isBlackList |
false | whether or not the permSpecs are a blacklist, defaults to false (a whitelist) |
- Source:
Returns:
an object with tables for keys and CRUD permissions for values (e.g. {c:false,r:true,u:false,d:false} == read only)
- Type
- *
hasPerm(table, groups, action, permSpecs) → {boolean}
check to see if the groups that the user is in grants him/her permission to do the given action on the given resource
Parameters:
| Name | Type | Description |
|---|---|---|
table |
the table/resource upon which the action would be performed | |
groups |
and array of group names | |
action |
String can be 'c', 'r', 'u', or 'd' | |
permSpecs |
the permission specifications |
- Source:
Returns:
- Type
- boolean
invertResults(userPerms)
invert the given permissions
Parameters:
| Name | Type | Description |
|---|---|---|
userPerms |
this is the output from getGroupPerms |
- Source:


1 write to Command
System.Management.Automation (1)
engine\remoting\client\JobSourceAdapter.cs (1)
140
Command
= command;
1 reference to Command
System.Management.Automation (1)
engine\remoting\client\JobSourceAdapter.cs (1)
207
return _command ?? _definition.
Command
;


Coin
问题描述 :
Moon has many coins, but only contains two value types which is 5 cents and 7 cents, Some day he find that he can get any value which greater than 23 cents using some of his coins. For instance, he can get 24 cents using two 5 cents coins and two 7 cents coins, he can get 25 cents using five 5 cents coins, he can get 26 cents using one 5 cents coins and three 7 cents coins and so on.
Now, give you many coins which just contains two value types just like Moon, and the two value types identified by two different prime number i and j. Can you caculate the integer n that any value greater than n can be created by some of the given coins.
输入:
The first line contains an integer T, indicates the number of test cases.
For each test case, there are two different prime i and j separated by a single space.(2<=i<=1000000, 2<=j<=1000000)
For each test case, there are two different prime i and j separated by a single space.(2<=i<=1000000, 2<=j<=1000000)
输出:
The first line contains an integer T, indicates the number of test cases.
For each test case, there are two different prime i and j separated by a single space.(2<=i<=1000000, 2<=j<=1000000)
For each test case, there are two different prime i and j separated by a single space.(2<=i<=1000000, 2<=j<=1000000)
样例输入:
1 5 7
样例输出:
23
#include <iostream> using namespace std; typedef unsigned long long int longint; int main() { int t; cin >> t; while (t--) { longint n, m; cin >> n >> m; longint ans; ans = (n * m) - n - m; cout << ans << endl; } return 0; } // end // ism
解题转自：http://www.cnblogs.com/izumu/archive/2012/07/20/2600867.html
(adsbygoogle = window.adsbygoogle || []).push({});
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
三年血赚死刑不亏？
递归实现的代码部分
14句和18句的 wt[n - 1]是不是应该改为 wt[ n ] ?
学习了。找到这题
跟书上的解法不一样，不知道哪个效率要高些
為甚麼fln*20+0.5f
有一点问题。。后面动态规划的程序中
int dp[n+1][W+1];
会报错 提示表达式必须含有常量值。该怎么修改呢。。
第一个真的要比较两次吗，换成else if 不可以吗
Thanks i enjoy your write-up about Why We Keep track of | J Squared Consulting
scanner改用StreamTokenizer就不超时了
正确性是关键，冲突是都会有的，主要是那种算法冲突的概率低
图像处理那题解题报告在哪儿呢？
这个也是算法导论的习题
5.1处，反了；“上一个操作符的优先级比操作符ch的优先级大，或栈是空的就入栈。”如代码所述，应为“上一个操作符的优先级比操作符ch的优先级小，或栈是空的就入栈。”
第一种方法怎么会是O(log(m+n))呢？没有二分的话直接遍历应该就是O(m+n)吧？
失误失误，转载错了。已经改正，抱歉。


Forest Service waives recreation fees in honor of veterans
WINCHESTER, Ky. (AP) - The U.S. Forest Service is waiving recreation fees at some locations around the Daniel Boone National Forest in honor of Veterans Day.
An agency statement says forest visitors can enjoy designated locations free of charge on Nov. 11.
Locations include: the Redbird Crest Trail in Clay and Leslie counties, the White Sulphur ATV Trail and Clear Creek Shooting Range in Bath County, Natural Arch Scenic Area and Appletree Shooting Range in McCreary County, the Keno Shooting Range in Pulaski County, Laurel River Lake boat ramps in Laurel and Whitley counties and Cave Run Lake boat ramps in Bath, Rowan and Menifee counties.
Overnight camping fees are also waived at Red River Gorge, Koomer Ridge Campground, Barren Fork Horse Camp, White Sulphur Horse Camp, Clear Creek Campground and Claylick Boat-in Campground.
Sign up for Daily Newsletters
Copyright © 2019 The Washington Times, LLC.
Please read our comment policy before commenting.


As one of the most famous people in the world, Beyoncé is used to living her life in the spotlight. But that also means she's regularly subjected to criticism and unfair speculation regarding her body and appearance.
The superstar took part in an "Ask Me Anything" with ELLE, during which she got candid about many personal topics posed by fan questions.
A fan on Instagram asked the singer, "Everybody's weight fluctuates.  Beyoncé had a completely relatable answer. She explained, "If someone told me 15 years ago that my body would go through so many changes and fluctuations, and that I would feel more womanly and secure with my curves, I would not have believed them."
Ivy Park's owner also revealed how her perspective about her body has changed over the years. She told ELLE, "But children and maturity have taught me to value myself beyond my physical appearance and really understand that I am more than enough no matter what stage I'm at in life.  Also knowing true beauty is something you cannot see. I wish more people focused on discovering the beauty within themselves rather than critiquing other folks' grills."
The singer also revealed the question she hates being asked the most, saying, "Are you pregnant?
I think most people will agree that we should all adopt Beyoncé's "zero fucks" attitude when it comes to our own bodies.


TRACE is a web service that combines the sns service with the travel management service.
Find Out MoreThe advantage of Trace is that you can communicate with your travel companions in real time and create your itinerary. Also, You can manage your travel expenses and reservations while you are traveling, and share the course or experience you were preparing for your trip.
Get Started!You can create a travel schedule.
You can talk with your travel companions in real time.
You can share your itinerary with others.
You can save photos taken during your trip to the online Repository.
한현우
010-9347-1926
<EMAIL_ADDRESS>박현경
010-5579-6529
<EMAIL_ADDRESS>장태익
010-7137-4817
4rchive7@naver.com


Users
Settings
Log
ESP RFID
Warning!
Saving settings will cause the device to reboot.
Please wait while WebSocket is connecting...
Wi-Fi Client Settings
Type your Wi-Fi Network's SSID or Scan for nerby Wireless Networks to join.
SSID
Scan
Password
Access Point Settings
Type your Wi-Fi Network's SSID or Scan for nerby Wireless Networks to join.
SSID
Password
General Settings
Type your access password and device name.
Admin Password
Device Name
Hardware Settings
Select your desired strength of the RFID antenna and the activation time of the relay.
Antenna Gain
Max (48 db)
43 db
38 db
Avg (33 db)
23 db
Min (18 db)
Activate Time
Test Relay
Please reboot before testing
Save & Reboot
Clear Log
Warning!
This will delete all logs.
Clear Log
Update Firmware
Upload
Update SPIFFS
Warning!
This will delete all data (including UIDs, Users, Settings).
Upload


|
Bitstream Interpretation Library (BIL)
0.1
|
Contains declaration of DeviceCfgDb class. More...
Go to the source code of this file.
Classes | |
| class | bil::DeviceCfgDb |
| Holds a device's configuration bit mapping. More... |
Namespaces | |
| namespace | bil |
Macros | |
| #define | BIL_DEVICECFGDB_HPP |
Contains declaration of DeviceCfgDb class.
Definition in file DeviceCfgDb.hpp.
| #define BIL_DEVICECFGDB_HPP |
Definition at line 8 of file DeviceCfgDb.hpp.


Equivalent to clicking the Overlay command on the Format menu in Microsoft Excel version 4.0. Formats the overlay chart according to the arguments you specify.
Syntax
FORMAT.OVERLAY(type_num, view, overlap, gap_width, vary, drop, hilo, angle, series_dist, series_num, up_down, series_line, labels)
FORMAT.OVERLAY?(type_num, view, overlap, gap_width, vary, drop, hilo, angle, series_dist, series_num, up_down, series_line, labels)
Type_num is a number specifying the type of chart.
| Type_num | Chart |
| 1 | Area |
| 2 | Bar |
| 3 | Column |
| 4 | Line |
| 5 | Pie |
| 6 | XY (Scatter) |
| 11 | Radar |
| 14 | Doughnut |
View is a number specifying one of the views in the Data View box in the Overlay dialog box. The view varies depending on the type of chart.
Overlap is a number from -100 to 100 specifying how you want bars or columns to be positioned. It corresponds to the Overlap box in the Overlay dialog box. Overlap is ignored if type_num is not 2 or 3 (bar or column chart).
If overlap is positive, it specifies the percentage of overlap you want for bars or columns. For example, 50 would cause one-half of a bar or column to be covered by an adjacent bar or column.
If overlap is negative, then bars or columns are separated by the specified percentage of the maximum available distance between any two bars or columns.
If overlap is omitted, it is assumed to be 0 (bars or columns do not overlap), or it is unchanged if a value was previously set.
Gap_width is a number from 0 to 500 specifying the space between bar or column clusters as a percentage of the width of a bar or column.
Gap_width is ignored if type_num is not 2 or 3 (bar or column chart).
If gap_width is omitted, it is assumed to be 50, or it is unchanged if a value was previously set.
Several of the following arguments are logical values corresponding to check boxes in the Overlay dialog box. If an argument is TRUE, Microsoft Excel selects the corresponding check box; if FALSE, Microsoft Excel clears the check box. If an argument is omitted, the setting is unchanged.
Vary corresponds to the Vary By Categories check box. Vary is not available for area charts.
Drop corresponds to the Drop Lines check box. Drop is available only for area and line charts.
Hilo corresponds to the Hi-Lo Lines check box. Hilo is available only for line charts.
Angle is a number from 0 to 360 specifying the angle of the first pie slice (in degrees) if the chart is a pie chart. If angle is omitted, it is assumed to be 0, or it is unchanged if a value was previously set.
Series_dist is the number 1 or 2 and specifies automatic or manual series distribution.
If series_dist is 1 or omitted, Microsoft Excel uses automatic series distribution.
If series_dist is 2, Microsoft Excel uses manual series distribution, and you must specify which series is first in the distribution by using the series_num argument.
Series_num is the number of the first series in the overlay chart and corresponds to the First Overlay Series box in the Overlay dialog box. If series_dist is 1 (automatic series distribution), this argument is ignored.
Up_down corresponds to the Up/Down Bars check box. Up_down is available only for line charts.
Series_line corresponds to the Series Lines check box. Series_line is available only for stacked bar and column charts.
Labels corresponds to the Radar Axis Labels check box. Labels is available only for radar charts.
Related Functions
DELETE.OVERLAY Deletes the overlay on a chart
FORMAT.CHART Formats a chart
Return to index


This page contains information about the course groups as well as information on mandatory assignments and exam project.
Place and time
The course runs from August 2, 2021 to August 24. See timeplan for teaching schedule.
The teaching format will be a mix of online and teaching on campus.
- All lectures will be online and consist of an on-demand part and live part where questions are answered.
- Lab/exercise classes will run both online and on-campus. Students must choose which part they want to attend.
- All lectures and lab/exercise classes on campus will take place in CSS 35-01-05.
- Zoom link for online lectures and lab/exercise classes can be found here.
Groups
You have two options of being assigned to a group. The first is that you report the group to us and where all members consent. The second option is that if we have not heard from you then you will be assigned to a group by us. We will communicate the groups to you on the first day we will assign you to a group. Details about the two options for assignment is found below.
- In order to pre-register a group only one member should write an email to Jonas Skjold Raaschou-Pedersen expressing this. The deadline for sending the information is Friday July 30 at 12.00. The email should only say that all members consented and whether or not you want to participate in lab classes on campus or online. In order to be valid all group members must be recipients of the email using their KU email. Groups of only two people have the option of joining another group of two. new people as this course has a broad group of students. The groups will be used for handing in Assignments 1 and 2 and the final exam projects, see below. If you are too few people in the group we will reassign you to another group.
- The group forming by us will be based on two factors. First, whether or not you prefer online or on-campus lab classes, we only match people who attend the same kind of lab class. Second, given that the first criteria we randomly assign you to other students who performed similar to you in Assignment 0, see below.
Mandatory assignments
There are three mandatory assignments. Two of three must be passed in order to be eligible for participating in the exam. The first must be handed-in individually. The two other must be handed-in as groups where each group hands in the same copy.
- Assignment 0: basic Python, packages and data processing
- Assignment 1: collecting data and structuring data.
- Assignment 2: machine learning and text data.
The assignments will be available in Absalon and at the assignment page
Exam project
At the end of the course your group must hand in a independent exam project.
The content of the exam project is something that you choose. You and your group must find a subject, data, choose methods etc.
Grading
The grade for this course is exclusively determined by the project handed in. The project will be judged on a number of dimensions, these include:
- how the data was obtained (setting up new data collection);
- how the data was processed;
- how machine learning methods are applied and which methods are used;
- how results are explained (writing, figures, tables with model output etc.);
- the research question and its originality as well as how it is answered.
Some advice about the grading. It is essential that spend time on motivating your project and conveying your results. In addition, it is important that you spend time on calibrating and validating the models you work with rather than using as many models as possible. We emphasize that using machine learning is NOT necessary to make a great project, many of the best projects gain insights from the data without modelling.
Requirements for project
The exam projects have a number of requirements that must be met, these are: requirement
- Research question (you should discuss with TAs)
- Groups with two to four members
- Project formalia
- Project must consist of a report (.pdf file) and a documentation as Jupyter Notebook (.ipynb file).
- The report should be written like a brief research article (short literature review, references to methods, results etc.). The report is limited to the following maximum number of pages (normalsider).
- 2 members, 12 pages;
- 3 members, 15 pages;
- 4 members, 18 pages.
- Note that 1 page (normalside) corresponds 2,400 and does not count figures, abstract, list of reference, frontpage, appendix.
- The report should contain your exam numbers and possibly your names (optional). The exam numbers (or names) MUST show who contributed with writing which parts of the report. At most 20 pct. of the report can be written shared. If you fail to provide this the submission of your project may get rejected!
- Grading will be based on the report but process but data collection, computations etc. should be well documented in the supporting Jupyter Notebook.
Possible data sources
Students in previous years of Social Data Science have used a large variety of data sources including:
- news on DR (Danish Broadcasting Company) and the Danish newspaper Information
- price of cars for sales on bilbasen
- analyzing linguistic content on Twitter
- Airbnb pricing in Copenhagen
- Prediction of bitcoin prices from Reddit data.
If you are interested in working with one or more of these datasets or see the projects by the students who made them please contact us and we will put you in touch.


#UnravellingTech #Explorer
Commitment to ensuring the team succeeds with all tasks, duties, and projects.
Our life is what our thoughts make it.
Team player... Nothing without my team.
I am web/android developer who loves to work to making the internet a better place
I am a Tech and Vision Enthusiast with an undying Curiosity and Observant by nature.
#Explorer #Ambivert
Lot more to debug
Python developer with proficiency on cloud technology and MVC architecture. JavaScript the favourite pastime🙂.
Creating my own sunshine
A techie who understands the dynamics of technology and consistently strives to innovate.
A Tech Enthusiast | Web Developer | Graphic Designer
Turning ideas into reality.


James Clavell's Noble House by Gary Nelson full cast and crew Pierce Brosnan - Ian Dunross; Deborah Raffin - Casey Tcholok; Tia Carrere - Venus Poon
Amazon co uk - Buy James Clavell's Noble House (Pierce Brosnan) 2Disc Set Australian Import , Region Free PAL DVD at a low price; free delivery on Review: The latest critic and user reviews, photos and cast info for Noble House. Pierce Brosnan. There are no critic reviews yet for Noble House Actor: Pierce Brosnan (actor) and John Rhys-Davies (actor), Title: James Clavell's. Noble House (DVD), Distributor: Lions Gate Home Entertainment, Category: Jun 13, 2008 Well, you get Pierce Brosnan back in his Remington Steel days as tai pan Ian Dunross of House Struan, or the titular Noble House (Jardines in Never miss James Clavell's. Noble House! Conclusion of the four-part drama, starring Pierce Brosnan as a business tycoon who is plunged into a world of Jan 28, 2008 The Emmy-nominated mini-series Noble House is based on the novel by James Clavell (Shogun) and stars Pierce Brosnan ("007" films,
Mar 20, 2013 Noble House One of the best While the book is epic, the 1988 TV series (with . Pierce Brosnan as Ian Struan) is no less brilliant Here's. the Feb 21, 2012 Pierce Brosnan and his 11-year-old son Paris bumped into Tia Carrerre starring in the 1988 TV mini-series Noble. House that was based on a Feb 21, 1988 ``Noble House,`` his latest mini-series for NBC, is no exception But it is Pierce Brosnan, known to television. viewers as Remington Steele, Ian Dunross, Besitzer des Noble House in Hongkong, hat finanzielle Probleme Alastair Struan (Denholm Elliott) hat Ian Dunross (Pierce Brosnan) zum
Pierce Brosnan filmography including movies from. released projects, in theatres, in production. Noble House (1988) - TV aka James Clavell's Noble House. Pierce Brendan Brosnan was an only child, and had a difficult, challenging. ' The 4th Protocol', and then another mini-series, 'James Clavell's Noble House' Pierce Brosnan, James Clavell's Noble House (3), James Clavell's Noble House (4), James Clavell's Noble House (1), James Clavell's Noble House (2)
Sep 13, 2013 Noble House (Mini series) - Pierce Brosnan (1989) - Tin Hau Temple, Repulse Bay One of the main tourist sites (other than the beach itself of


FROM THE DESK OF RAJI RAJI BILL AND EXCHANGE MANAGER, AFRICAN DEVELOPMENT BANK (ADB) OUAGADOUGOU, BURKINA FASO. Dear Friend, How are you,first i will explain my self little to youbefore we continue. I am the manager of bill and exchange at the foreign remittance department ofAFRICAN DEVELOPMENT BANK (ADB)I am writing to seekyour interest over a transaction. In my department we discovered an abandoned sum of$15m US dollars (FIFTEEN MILLIONS US DOLLARS) . In anaccount that belongs to one of our foreign customer who died along with his entire family in November 2002 in a plane crash. Since we got information about his death, we have been expecting his next of kin to come over and claim his money because we cannot release it unless somebody applies for it as next of kin or relation to the deceased as indicated in our banking guidelines but unfortunately we learnt that all his supposed next of kin or relation died alongside with him at the plane crash leaving nobody behind for the claim. It is therefore upon this discovery that I and one orther official in my department have decided to make this business proposal to you and release the money to you as the next of kin or relation to the deceased for safety and subsequent disbursement since nobody is coming for it and we don�t want this money to go into the Bank's treasury as unclaimed Bill. The Banking law and guideline here stipulates that if such money remained unclaimed after five years, the money will be transferred into the Bank's treasury as unclaimed fund. The request of foreigner as next of kin in this business is occasioned by the fact that the customer was a foreigner and a Burkinabe cannotstand as next of kin to a foreigner. We agree that 40 % of this money will be for you as foreign partner, in respect to the provision of a foreign account, 10 % will be set aside for expenses incurred during the business and 50 % would be for me and my colleague. There after I will visit your country for disbursement of the fund according to the percentages indicated in this proposal. Therefore to enable the immediate transfer of this fund to you as arranged, you must apply first to the bank as relations or next of kin of the deceased indicating your bank name, your bank account number, your private telephone and fax number for easy and effective communication and location where in the money will beremitted. Upon receipt of your reply, I will send to you by email the text of the application. I will not fail to bring to your notice that this transaction is hitch free and that you should not entertain any atom of fear as all required arrangements have been made for the transfer . You should contact me immediately as soon as you receive this letter. Trusting to hear from you immediately. Your�s faithfully RAJI RAJI Bill and Exchange Manager, Africa Development Bank (A.D.B) _________________________________________________________________ Gagnez des écrans plats avec Live.com http://www.image-addict.fr/


{% extends "base.html" %} {% load static %}{% load i18n %} {% block title %} Home {% endblock %} {% block description %} Quran, All translations in serveral languages, Browsing the full Quran in the Original Text and its translations in various languages by various renowned scholars around the globe. A large collection of translations from various authors have been included in this site to compare the meanings and understand. Having discussion by adding Comments, Questions, Translations, Explanations, History of Verses in order to better help in understanding the message of Quran. Users may register to contribute to any of the verses with the help of references from available materials. Ability to search within the verses of Quran, its translations as well as comments in many languages. All texts, translations and comments of users can be searched efficiently to provide relevant results.{% endblock %} {% block keywords %} Quran, translations, several languages {% endblock %} {% block content %}
| Chapter No. | Name | Arabic | English Translation of Name | Verse Count |
| Chapter No. | Name | Arabic | English Translation of Name | Verse Count |
| {{m.id}} | {{m.transliteration}} | {{m.arabic_name}} | {{m.english_name}} | {{m.total_verses}} |


{% extends "account/base.html" %} {% load i18n %} {% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} {% block content %}
{% trans "We are currently in an early beta phase, hence sign up is currently limited to a small group of researchers. Please sign up to our newsletter to stay updated about our public beta launch." %}
{% endblock %}


snarf & barf
`Prev `__
S
`Next `__
--------------
**snarf & barf**: /snarf´n·barf\`/, n.
Under a `*WIMP environment* <../W/WIMP-environment.html>`__, the act of
grabbing a region of text and then stuffing the contents of that region
into another region (or the same one) to avoid retyping a command line.
In the late 1960s, this was a mainstream expression for an ‘eat now,
regret it later’ cheap-restaurant expedition.
--------------
+--------------------------+----------------------------+-------------------------------+
| `Prev `__ | `Up <../S.html>`__ | `Next `__ |
+--------------------------+----------------------------+-------------------------------+
| snarf | `Home <../index.html>`__ | snarf down |
+--------------------------+----------------------------+-------------------------------+


This book offers a comprehensive introduction to workflow management, the management of business processes with information technology. By defining, analyzing, and redesigning an organization’s resources and operations, workflow management systems ensure that the right information reaches the right person or computer application at the right time. The book provides a basic overview of workflow terminology and organization, as well as detailed coverage of workflow modeling with Petri nets. Because Petri nets make definitions easier to understand for nonexperts, they facilitate communication between designers and users. The book includes a chapter of case studies, review exercises, and a glossary.
This book is available in Portuguese at uc.pt/imprensa


Ryan Giggs doing a ‘great job’ with Wales – Sir Alex Ferguson
The record-breaking Manchester United boss has hailed his former player.
Sir Alex Ferguson has praised Wales boss Ryan Giggs and revealed his pride at the former Manchester United winger enjoying managerial success.
A 2-0 win over Hungary on Tuesday sealed a second successive appearance for Wales at the European Championship, but when Giggs was appointed in January 2018, it was not a universally popular choice.
He came in for heavy criticism over selection and tactics during the summer setbacks in Croatia and Hungary, defeats which left Euro 2020 qualification hopes hanging in the balance.
But a run of eight points from their final four qualifiers sent Wales through to only a third ever major tournament and helped the one-time Red Devils caretaker boss silence his critics.
Giggs’ old manager Ferguson, speaking at a fundraiser for international football charity Grassroot Soccer in London, told the PA news agency: “Ryan was always going to be a top man.
“He is intelligent, he has great drive about him and is doing a great job with Wales. And giving young players a chance too. It is fantastic.”
Earlier this month, Giggs revealed Ferguson sends him a good luck message before every match and the pair will be reunited again soon.
Ferguson is set to manage the team facing the likes of Giggs plus his old team-mates.
Yet the record-breaking manager warned that his former players still remain as sharp as ever, highlighting the 5-0 win they achieved over Bayern Munich in a 1999 Treble reunion game at Old Trafford in May as proof.
He is intelligent, he has great drive about him and is doing a great job with Wales.Sir Alex Ferguson on Ryan Giggs
“They are all fit.  “They played in a charity game against Bayern Munich for the anniversary of 99 and they were amazing.
“Every one of them was fantastic. Whoever wins the prize is going to have to be good.”
Glaswegian Ferguson hailed the work of Grassroot Soccer and reminisced about his childhood in Scotland.
The 77-year-old started his playing career as an amateur with Queen’s Park before representing the likes of Dunfermline and Rangers.
He would go on to achieve unprecedented success with first Aberdeen and United, who he managed for 26 years.
“Grassroot Soccer is providing a platform and giving kids the ambition to be a footballer because football is one of these great attractions for any kid,” Ferguson said.
“You see a ball rolling and a kid will run after it and I grew up in the same vein and being given the opportunity to play football and also for myself to be a professional, that started from a humble background.
“When you are that age you don’t think you are going to do it, but given the opportunity you seize it and that is what these kids in Africa will be realising with all the support they are getting from Grassroot Soccer.”


{% extends "base.html" %} {% block title %}Upload Failed | {% endblock %} {% block content %} There was an error with the input. Please check your file again. To go back to the front page, click here {% endblock %}


{% extends 'catalog/base.html' %} {% load static %} {% block title %}Polygenic Score Data Description{% endblock %} {% block desc %} {% endblock %} {% block content %}
This page contains information regarding the contents of the PGS Catalog, describing the curated data fields and tables extracted from PGS publications. The descriptions are based on the major data structures in the Catalog:
Each publication in the database is given a Polygenic Publication (PGP) ID so that scores and evaluations link to the same source object. When browsing by publications the Number of PGS Developed refers to the number of newly developed PGS in the paper, and the Number of PGS Evaluated refers to the number of PGS (new and existing) that have performance metrics derived in the study. For each publication the following information is extracted:
| PubMed ID (PMID) | PubMed Identification number. |
| Digital Object Identifier (doi) | The doi of each publication is curated in addition to the PMID to allow unpublished work (e.g. pre-prints) to be added to the catalog. |
| Title | Title of the publication. |
| Author(s) | List of publication authors, the first author is also extracted for a shorter display. |
| Journal | The name of the publication source. |
| Publication Date | Date of publication (with respect to the PMID or doi upon upload to the Catalog). |
Each PGS in the database is given a unique Polygenic Score (PGS) ID to identify it. The following information is extracted, and associated with each PGS in the catalog:
| Reported Trait | The author-reported trait (e.g. body mass index [BMI], or coronary artery disease) that the PGS has been developed to predict. |
| Mapped Trait(s) | The Reported Trait is mapped to Experimental Factor Ontology (EFO) terms and their respective identifiers by PGS Catalog curators. For more information about the ontology traits see the Trait section. |
| PGS Name | This may be the name that the authors use to refer to the PGS, or a name that a curator has assigned to identify the score during the curation process (before a PGS ID has been given). |
| Original Genome Build | The version of the genome that the variants present in the PGS are associated with. Listed as NR (Not Reported) if unknown. |
| Number of Variants | Number of variants used to calculate the PGS. In the future this will include a more detailed description of the types of variants present. |
| Number of Variant Interaction Terms | Number of higher-order variant interactions included in the PGS. |
| PGS Development Method | The name or description of the method or computational algorithm used to develop the PGS. |
| PGS Development Details/Relevant Parameters | A description of the relevant inputs and parameters relevant to the PGS development method/process. |
| PGS Catalog Publication ID (PGP ID) | A PGP ID links the PGS to the publication in which it was described. |
| Citation | External link to the original publication source. |
| Weight Type | Whether the author supplied Variant Weight is a: beta (effect size), or something like an OR/HR (odds/hazard ratio). |
| Terms and Licenses | The PGS Catalog distributes its data according to EBI’s standard Terms of use. Some PGS have specific terms, licenses, or restrictions (e.g. non-commercial use) that we highlight in this field, if known. |
The following information about the PGS are captured in tables and described in subsequent sections:
| Development Samples |
Information about the samples used for the development of the PGS. Relevant column descriptions are in the Sample Description section.
| ||||
| Performance Metrics | A record of the performance metrics that have been reported for the PGS. Relevant column descriptions are in the Performance Metrics section | ||||
| Evaluated Samples | Information about the samples used in PGS performance evaluation. These samples have an PGS Catalog Sample Set ID (PSS ID) to link them to their associated performance metrics (and across different PGS). Relevant column descriptions are in the Sample Description section. |
Traits in the Catalog are displayed/grouped according to the Mapped Traits rather than the author Reported Traits to facilitate comparability, similar to the NHGRI-EBI GWAS Catalog. For a complete description of why the trait ontology is employed please refer to related documentation from the NHGRI-EBI GWAS Catalog. The Experimental Factor Ontology is hosted and described here: Experimental Factor Ontology (EFO). The information for each EFO trait ID that is stored in the PGS catalog is:
| Trait | The trait label from the ontology. |
| Identifier | The Experimental Factor Ontology ID (EFO_ID) identifier to consistently refer to traits using the EFO, and to other resources like the NHGRI-EBI GWAS Catalog. |
| Description | Detailed description of the trait from EFO. |
| Synonyms | Other names for the trait. |
| Mapped Term(s) | Includes references to terms in other databases and ontologies (e.g. ICD9/ICD10, MONDO, SNOMEDCT, etc.). |
A consistent set of fields are used to describe the samples used to develop and evaluate each PGS:
| PGS Catalog Sample Set ID (PSS ID) | PSS IDs are assigned to describe samples used in PGS evaluations (e.g. Performance Metrics). PSS IDs are not uniquely associated with a single PGS, multiple PGS can be evaluated on the sample sets (PSS ID). | ||||||||||
| Phenotype Definitions and Methods | A description of how the phenotype was measured or defined (e.g. ICD codes used to identify cases/phenotypes in EHR data). | ||||||||||
| Participant Follow-up Time | A summary of the follow-up time (mean/median, range/confidence intervals) for participants that are part of a prospective cohort/study design (used to measure disease incidence). | ||||||||||
| Study Identifiers | Identifiers used to link the samples to their initial descriptions (e.g. using PubMed IDs) and if they were used for variant associations to their associated GWAS studies (using NHGRI-EBI GWAS Catalog GCST IDs). | ||||||||||
| Sample Numbers | This field describes the number of individuals included in the sample, along with the number of cases and controls (if the trait is dichotomous), and the percent of participants that are male (if available). In cases where the study does not provide the exact sample size we display the value as NR (not reported) and as missing in the API/metadata downloads. | ||||||||||
| Age of Study Participants | A summary (mean/median, range/confidence intervals) of study participants ages. | ||||||||||
| Ancestry fields: | Fields describing sample ancestry are curated according to the framework used to record ancestry data from the NHGRI-EBI GWAS Catalog. See our Ancestry Documentation page for a complete description of how ancestry is represented and described in the PGS Catalog.
|
||||||||||
| Cohort(s) | A list of cohorts that collected the samples. The initial list of common cohorts used in genetics studies that seeded these annotations is from Mills & Rahal. Communications Biology (2019). |
||||||||||
| Additional Sample/Cohort Information | Any additional description about the samples and what they were used for that is not captured by the structured categories (e.g. sub-cohort information). |
Each evaluation of a PGS is given a PGS Performance Metric (PPM) ID that links it to a description of the results:
| PGS Catalog Sample Set ID (PSS ID) | ID that links to the samples the displayed PGS evaluated. | ||||||
| Performance Source | ID that links to the publication where the performance metrics were reported. | ||||||
| Trait | This field displays both the Reported and Mapped Traits. The reported trait often corresponds to the test set names reported in the publication, or more specific aspects of the phenotype being tested (e.g. if the disease cases are incident vs. recurrent events). | ||||||
| Reported values: |
The reported values of the performance metrics are all reported similarly (e.g. the estimate is recorded along with the 95% confidence interval (if supplied)) and grouped according to the type of statistic they represent:
|
||||||
| Covariates Included in PGS Model | A comma-separated list of covariates used in the prediction model to evaluate the PGS. Examples include: age, sex, smoking habits, etc. | ||||||
| PGS Performance: Other Relevant Information | Any other information relevant to the understanding of the performance metrics. |


--- layout: documentation title: BuildConfigurationType ---
The BuildConfigurationType describes how the build utility was configured for this build of this application.
| Field Name | Type | Description |
|---|---|---|
| Configuration_Setting_Description0..1 | string |
This field contains the description of the configuration settings for this build of this application instance. |
| Configuration_Settings1..1 | ConfigurationSettingsType |
This field contains the configuration settings for this build of this application instance. |


Top250.80
Min250.80
Top250.80
Min250.80
Top250.80
Min250.80
235 Week Max
102 Week Avg
5 Week Min
Security not checked
Normal functionality
Full load
Collecting the transactions
When you think not to lose all the sources and transactions, remember there are no worries if we are nearby. We’ve got you covered.
Choose my planCollecting the transactions
When you think not to lose all the sources and transactions, remember there are no worries if we are nearby. We’ve got you covered.
Choose my planCollecting the transactions
When you think not to lose all the sources and transactions, remember there are no worries if we are nearby. We’ve got you covered.
Choose my planMorgan NM
Thanks for taking all my transactions and making a stable system. Collaboration and customer orientation above all!
Pricing
Startup
Popular
- Responsive
- 3-4 consultations
- Regular events
- Long-term contracts
15.95/mo
Choose Plan


Username
Password
Login
Create Record
Name:
Content:
Category:
Category:
imgurl:
View Records
Update Record
TID:
Name:
Content:
Category:
Category:
imgurl:
Delete Record
TID:
Name: