SlideShare a Scribd company logo
1 of 61
Download to read offline
Extending functionality in nginx, with modules!
2012-01-31
What is nginx?
•The 2nd most used HTTP-server (Apache is 1st)
•Asynchronous event-driven approach to request-handling.
•Written in C
•Readable source code!
What kind of modules do we have?
•handler modules - they process a request and produce output
•filter modules - they manipulate the output produced by a
 handler
•load-balancers - they choose a backend server to send a
 request to, when more than one backend server is eligible
What will you learn through the course of this
presentation?
•Step 0: Building Nginx (with a module)
•Step 1: Create a stub http content module, aka "Hello, World"
•Step 2: Update the module to use an external library for
 content creation
•Step 3: Give your module its own configuration directives
•Step 4: Fetching arguments from the URL
Let's get started
Step 0: Building Nginx (with a module)
Download & build
#   wget http://nginx.org/download/nginx-1.0.11.tar.gz
#   tar zxvf nginx-1.0.11.tar.gz
#   cd nginx-1.0.11.tar.gz
#   ./configure
#   make



Straight forward. No surprises there.
Building a third party module
Modules are statically linked, so adding a new module requires
a new nginx-build.
# ./configure --add-module=$HOME/git/some_cool_module_I_found_somewhere_on_github
# make



Easy. (Want more modules? --add-module can be added several
times)
This is boring, show me the code!
Step 1: Create a stub http content module, aka "Hello, World"
Creating your own module
A minimal HTTP module - let's call it "fun" - consists of a
directory with two files:
config


and a source file
ngx_http_fun_module.c


A significant amount of the source-file will be stub-code.
The config file
Is sourced as a shell-script, and should set different
environment variables to help the build-system understand
what to do.
A minimal config-file for a module without any external
dependencies could look like this:
ngx_addon_name=ngx_http_fun_module
HTTP_MODULES="$HTTP_MODULES ngx_http_fun_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fun_module.c"
The source file (1)
Include your headers.
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>


Declare a handler.
static char * ngx_http_fun(ngx_conf_t * cf, ngx_command_t * cmd, void * conf);


Create our HTTP body.
static u_char ngx_fun_string[] = "This is fun!";
How does nginx know about my configuration
directives?
Quoting the nginx source-code:
struct ngx_command_s {
    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;
};

#define ngx_null_command   { ngx_null_string, 0, NULL, 0, 0, NULL }

typedef struct ngx_command_s      ngx_command_t;


An array of ngx_command_t's, terminated by a
ngx_null_command should be defined in your module.
Declaring the configuration parameters
available by your module
Create a static array of the ngx_command_t-type, terminate it
with ngx_null_command. Populate it with the commands
available for your module.
static ngx_command_t ngx_http_fun_commands[] = {
    { // Our command is named "fun":
      ngx_string("fun"),

      // The directive may be specified in the location-level of your nginx-config.
      // The directive does not take any arguments (NGX_CONF_NOARGS)
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

      // A pointer to our handler-function.
      ngx_http_fun,

      // We're not using these two, they're related to the configuration structures.
      0, 0,

       // A pointer to a post-processing handler.   We're not using any here.
       NULL },
     ngx_null_command
};
Declaring the module context
Create a static ngx_http_module_t, populate all 8 elements with
NULL.
static ngx_http_module_t ngx_http_fun_module_ctx = {
    NULL,                          // preconfiguration
    NULL,                          // postconfiguration

     NULL,                        // create main configuration
     NULL,                        // init main configuration

     NULL,                        // create server configuration
     NULL,                        // merge server configuration

     NULL,                        // create location configuration
     NULL                         // merge location configuration
};


This is used to determine multiple levels of configuration, and
gives you control over which directives beat which in a race.
Our module does not use any configuration parameters other
than "fun;", so we don't need any handlers.
Declaring the module description structure
Create a (not static) structure of type ngx_module_t, reference
the structures we've made like this:
ngx_module_t ngx_http_fun_module = {
    NGX_MODULE_V1,
    &ngx_http_fun_module_ctx,      // module context
    ngx_http_fun_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
};
Creating the actual handler (1)
A static function that returns an ngx_int_t, and receives a
pointer to a ngx_http_request_t.
static ngx_int_t
ngx_http_fun_handler(ngx_http_request_t * r)
{
    ngx_int_t     rc;
    ngx_buf_t    * b;
    ngx_chain_t out;


We use rc to store the return value of certain function calls.
We use * b to store our buffer pointer.
out is the buffer chain.
Creating the actual handler (2)
A simple test of the kind of request we're receiving.
// we response to 'GET' and 'HEAD' requests only
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
    return NGX_HTTP_NOT_ALLOWED;
}
Creating the actual handler (3)
The request-body is useful if we're dealing with POST. We're
not.
// discard request body, since we don't need it here
rc = ngx_http_discard_request_body(r);

if (rc != NGX_OK) {
    return rc;
}


This attempts to detach the request body from the request. It's
an optimization.
Creating the actual handler (4)
Setting the content-type is important --
// set the 'Content-type' header
r->headers_out.content_type_len = sizeof("text/html") - 1;
r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char * ) "text/html";
Creating the actual handler (5)
// send the header only, if the request type is http 'HEAD'
if (r->method == NGX_HTTP_HEAD) {
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1;

    return ngx_http_send_header(r);
}
Creating the actual handler (6)
// allocate a buffer for your response body
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
Creating the actual handler (7)
// attach this buffer to the buffer chain
out.buf = b;
out.next = NULL;
Creating the actual handler (8)
// adjust the pointers of the buffer
b->pos = ngx_fun_string;
b->last = ngx_fun_string + sizeof(ngx_fun_string) - 1;
b->memory = 1;    // This buffer is in read-only memory
                  // This means that filters should copy it, and not try to rewrite in place.
b->last_buf = 1; // this is the last buffer in the buffer chain
Creating the actual handler (9)
// set the status line
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1;
Creating the actual handler (10)
    // send the headers of your response
    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    // send the buffer chain of your response
    return ngx_http_output_filter(r, &out);
}


... almost done!
Attaching the handler
static char *
ngx_http_fun(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_fun_handler; // handler to process the 'fun' directive

    return NGX_CONF_OK;
}


In this function, we can validate on the incoming
ngx_command_t, and attach different handlers.
Does this really work?
Trygve will attempt to demonstrate.
# make
# sudo make install



Update the nginx-configuration with a location handler,
something like this:
Location: /fun {
    fun;
}


Start nginx, and access http://localhost/fun/
Of course it worked! But static text isn't any fun
Step 2: Update the module to use an external library for
content creation
What are we going to do? Tell me now!
•We're going to replace the static text with an image,
 dynamically created with libcairo.
•We're going to learn about buffers and chains
The config file - dependency handling
Testing for dependencies is done with the feature tests
provided by nginx
ngx_feature="cairo"
ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs="#include <cairo.h>"
ngx_feature_path="/usr/include/cairo"
ngx_feature_libs=-lcairo
ngx_feature_test="cairo_version()"
. auto/feature

if [ $ngx_found = yes ]; then
     ngx_addon_name=ngx_http_fun_module
     HTTP_MODULES="$HTTP_MODULES ngx_http_fun_module"
     NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fun_module.c"
     CORE_LIBS="$CORE_LIBS `pkg-config cairo cairo-png --libs`"
     CFLAGS="$CFLAGS `pkg-config cairo cairo-png --cflags`"
else
     cat << END
$0: error: the fun module requires the cairo library.
END
     exit 1
fi
The Cairo part (1)
Include the header, and define M_PI
#include <cairo.h>

#define M_PI 3.14159265

struct closure {
    ngx_http_request_t * r;
    ngx_chain_t * chain;
    uint32_t length;
};


The struct will be used by a callback function we're going to
create later.
Throw away ngx_fun_string while you're at it, we're not going to
be using that.
The Cairo part (2)
In ngx_http_fun_handler(), remove the ngx_buf_t (we'll do these
things in a callback function later), and add our struct.
static ngx_int_t
ngx_http_fun_handler(ngx_http_request_t * r)
{
    ngx_int_t    rc;
    ngx_chain_t out;
    struct closure c = { r, &out, 0 };
The Cairo part (3)
Remove our header-handling from before - we won't be able to
calculate content-length before we've created the png.
// set the 'Content-type' header
r->headers_out.content_type_len = sizeof("text/html") - 1;
r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char * ) "text/html";

// send the header only, if the request type is http 'HEAD'
if (r->method == NGX_HTTP_HEAD) {
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1;
    return ngx_http_send_header(r);
}

// allocate a buffer for your response body
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
The Cairo part (4)
This is basically ripped from one of their many examples on
their web page.
cairo_surface_t * surface;
cairo_t * cr;

surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 256, 256);
cr = cairo_create (surface);

double   xc = 128.0;
double   yc = 128.0;
double   radius = 100.0;
double   angle1 = 270.0 * (M_PI/180.0);   // angles are specified
double   angle2 = 180.0 * (M_PI/180.0);   // in radians
The Cairo part (5)
cairo_set_line_width (cr, 10.0);
cairo_arc (cr, xc, yc, radius, angle1, angle2);
cairo_stroke (cr);

// draw helping lines
cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
cairo_set_line_width (cr, 6.0);

cairo_arc (cr, xc, yc, 10.0, 0, 2*M_PI);
cairo_fill (cr);

cairo_arc (cr, xc,   yc,   radius, angle1, angle1);
cairo_line_to (cr,   xc,   yc);
cairo_arc (cr, xc,   yc,   radius, angle2, angle2);
cairo_line_to (cr,   xc,   yc);
cairo_stroke (cr);
The Cairo part (6)
Make sure that our buffer chain is NULL'ed. Remove any
references to the ngx_buf_t from earlier.
out.buf = NULL;
out.next = NULL;

// Copy the png image to our buffer chain (we provide our own callback-function)
rc = cairo_surface_write_to_png_stream(surface, copy_png_to_chain, &c);

// Free cairo stuff.
cairo_destroy(cr);
cairo_surface_destroy(surface);

// If we for some reason didn't manage to copy the png to our buffer, throw 503.
if ( rc != CAIRO_STATUS_SUCCESS )
{
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}


The cairo_surface_write_to_png_stream uses a callback-function
to copy the png-data to a buffer, in the way we want.
Remember to free resources, and check that everything went
well.
The Cairo part (7)
We're going to set our headers now. New content-type - the
length has been calculated by our callback function and is
stored in our struct.
// set the 'Content-type' header
r->headers_out.content_type_len = sizeof("image/png") - 1;
r->headers_out.content_type.len = sizeof("image/png") - 1;
r->headers_out.content_type.data = (u_char * ) "image/png";
// set the status line
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = c.length;

// send the headers of your response
rc = ngx_http_send_header(r);

//   We've added the NGX_HTTP_HEAD check here, because we're unable to set content length before
//   we've actually calculated it (which is done by generating the image).
//   This is a waste of resources, and is why caching solutions exist.
if   (rc == NGX_ERROR || rc > NGX_OK || r->header_only || r->method == NGX_HTTP_HEAD) {
      return rc;
}
Allocating memory in an nginx-module
•Nginx has its own memory management system.
•ngx_pcalloc(pool, amount) will give you a pointer to some
 memory you can do what you want with.
•'pool' attaches the memory to an owner. Some memory will
 be allocated for the request (ngx_http_request_t->pool), and
 some will be allocated for the module configuration
 (ngx_conf_t->pool).
•Nginx deals with freeing/reusing this memory when it's owner
 doesn't need it anymore.
How does buffers and chains work?
•ngx_chain_t is a linked list of ngx_buf_t's.
•You provide a pointer to the first ngx_chain_t in the chain to
 ngx_http_output_filter()
•It will walk through the chain, and pass all the buffers to the
 output filter
•You need to get comfortable with this, or attaching bits and
 pieces together will be tricky.
Forgot what a linked list is?
struct element {
    int value;
    struct element * next;
};


A linked list is typically a struct, referencing an element of its
own type. The last element is often set to NULL, but they can
also be circular, or double (having an additional reference for
the previous item in the list), etc.
Creating our callback function (1)
Cairo provides you with an interface for accessing png-images
however you'd like. What we want to do, is to attach them to
our chain. We do that by implementing our own callback
function. This function will be called an undefined number of
times.
static cairo_status_t
copy_png_to_chain(void * closure, const unsigned char * data, unsigned int length)
{
    // closure is a 'struct closure'
    struct closure * c = closure;

   // Just a helper pointer, to help us traverse the linked list.
   ngx_chain_t * ch = c->chain;

   // We track the size of the png-file in our closure struct.
   c->length += length;
Creating our callback function (2)
// The allocated memory belongs to the request-pool.
ngx_buf_t * b = ngx_pcalloc(c->r->pool, sizeof(ngx_buf_t));
unsigned char * d = ngx_pcalloc(c->r->pool, length);

// We make sure to fail if we're unable to allocate memory.
if (b == NULL || d == NULL) {
    return CAIRO_STATUS_NO_MEMORY;
}

// Copy data to our new buffer, and set the pointers.
ngx_memcpy(d, data, length);

b->pos = d;
b->last = d + length;
b->memory = 1;
b->last_buf = 1;
Creating our callback function (3)
If the first element isn't put into place, we can quit early.
// Handle the first element in our linked list.
if ( c->chain->buf == NULL )
{
    c->chain->buf = b;
    return CAIRO_STATUS_SUCCESS;
}
Creating our callback function (4)
// Skip to the end of the linked list.
while ( ch->next )
{
    ch = ch->next;
}
Creating our callback function (5)
    // Allocate a new link in our chain.
    ch->next = ngx_pcalloc(c->r->pool, sizeof(ngx_chain_t));
    if ( ch->next == NULL )
    {
        return CAIRO_STATUS_NO_MEMORY;
    }

    // Attach our buffer at the end.
    ch->next->buf = b;
    ch->next->next = NULL;

    return CAIRO_STATUS_SUCCESS;
}


Presto! That was easy :)
We're ready to test this out!
Fingers crossed.
Yeah, but what if I want to be able to configure
my module?
•Step 3: Give your module its own configuration directives
Create a datatype for config-storage
typedef struct {
    ngx_uint_t   radius;
} ngx_http_fun_loc_conf_t;
Extend the ngx_http_fun_commands[]-array
{    // New parameter: "fun_radius":
     ngx_string("fun_radius"),
     // Can be specified on the main level of the config,
     // can be specified in the server level of the config,
     // can be specified in the location level of the config,
     // the directive takes 1 argument (NGX_CONF_TAKE1)
     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
     // A builtin function for setting numeric variables
     ngx_conf_set_num_slot,
     // We tell nginx how we're storing the config.
     NGX_HTTP_LOC_CONF_OFFSET,
     offsetof(ngx_http_fun_loc_conf_t, radius),
     NULL
},
Create a function for dealing with the config
creation
static void *
ngx_http_fun_create_loc_conf(ngx_conf_t * cf)
{
    ngx_http_fun_loc_conf_t * conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fun_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf->radius = NGX_CONF_UNSET_UINT;
    return conf;
}
Create a function for merging config
static char *
ngx_http_fun_merge_loc_conf(ngx_conf_t * cf, void * parent, void * child)
{
    ngx_http_fun_loc_conf_t * prev = parent;
    ngx_http_fun_loc_conf_t * conf = child;

    ngx_conf_merge_uint_value(conf->radius, prev->radius, 100);

    if (conf->radius < 1) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "radius must be equal or more than 1");
        return NGX_CONF_ERROR;
    }
    if (conf->radius > 1000 ) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "radius must be equal or less than 1000");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}
Add pointers to our new function in the
ngx_htt_fun_module_ctx
ngx_http_fun_create_loc_conf,   // create location configuration
ngx_http_fun_merge_loc_conf     // merge location configuration
Give our handler access to the configuration
data
ngx_http_fun_loc_conf_t * cglcf;
cglcf = ngx_http_get_module_loc_conf(r, ngx_http_fun_module);
Using the config-data in our module
We override the dimensions of our image, and we override the
center-position.
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cglcf->radius*2 + 64, cglcf->radius*2 + 64);
cr = cairo_create (surface);

double xc = cglcf->radius + 32;
double yc = cglcf->radius + 32;
double radius = cglcf->radius;
It can't possibly be that easy! Show me!
Update the configuration to something like this:
Location: /funbig {
    fun;
    fun_radius 500;
}

Location: /funsmall {
    fun;
    fun_radius 50;
}


The different URLs result in different sized images.
I want to deal with user-input
•Step 4: Fetching arguments from the URL
This is as easy as parsing strings in C. cough
The request has an uri-element, with members .data and .len.
Adding something like this to your handle will give you the 3
last characters of the URI as an integer;
char * uri;
int angle = 0;

if ( r->uri.len > 3 )
{
    uri = (char * )r->uri.data + r->uri.len - 3;
    angle = strtol(uri, NULL, 10);
}
Which you then can use ...
double angle1 = 0.0;                    // angles are specified
double angle2 = angle * (M_PI/180.0);   // in radians
Let's have a look at this too!
Last demo.
References
•http://www.evanmiller.org/nginx-modules-guide.html
•The nginx source code.
•http://blog.zhuzhaoyuan.com/
•http://www.nginxguts.com/
That's it!

More Related Content

What's hot

Nginx Internals
Nginx InternalsNginx Internals
Nginx InternalsJoshua Zhu
 
5 things you didn't know nginx could do velocity
5 things you didn't know nginx could do   velocity5 things you didn't know nginx could do   velocity
5 things you didn't know nginx could do velocitysarahnovotny
 
What's New in NGINX Plus R12?
What's New in NGINX Plus R12? What's New in NGINX Plus R12?
What's New in NGINX Plus R12? NGINX, Inc.
 
Learn nginx in 90mins
Learn nginx in 90minsLearn nginx in 90mins
Learn nginx in 90minsLarry Cai
 
Nginx A High Performance Load Balancer, Web Server & Reverse Proxy
Nginx A High Performance Load Balancer, Web Server & Reverse ProxyNginx A High Performance Load Balancer, Web Server & Reverse Proxy
Nginx A High Performance Load Balancer, Web Server & Reverse ProxyAmit Aggarwal
 
Introduction to NGINX web server
Introduction to NGINX web serverIntroduction to NGINX web server
Introduction to NGINX web serverMd Waresul Islam
 
NGINX High-performance Caching
NGINX High-performance CachingNGINX High-performance Caching
NGINX High-performance CachingNGINX, Inc.
 
High Availability Content Caching with NGINX
High Availability Content Caching with NGINXHigh Availability Content Caching with NGINX
High Availability Content Caching with NGINXNGINX, Inc.
 
NGINX 101 - now with more Docker
NGINX 101 - now with more DockerNGINX 101 - now with more Docker
NGINX 101 - now with more DockerSarah Novotny
 
Maximizing PHP Performance with NGINX
Maximizing PHP Performance with NGINXMaximizing PHP Performance with NGINX
Maximizing PHP Performance with NGINXNGINX, Inc.
 
Rate Limiting with NGINX and NGINX Plus
Rate Limiting with NGINX and NGINX PlusRate Limiting with NGINX and NGINX Plus
Rate Limiting with NGINX and NGINX PlusNGINX, Inc.
 
under the covers -- chef in 20 minutes or less
under the covers -- chef in 20 minutes or lessunder the covers -- chef in 20 minutes or less
under the covers -- chef in 20 minutes or lesssarahnovotny
 
Using NGINX as an Effective and Highly Available Content Cache
Using NGINX as an Effective and Highly Available Content CacheUsing NGINX as an Effective and Highly Available Content Cache
Using NGINX as an Effective and Highly Available Content CacheKevin Jones
 
Load Balancing Applications with NGINX in a CoreOS Cluster
Load Balancing Applications with NGINX in a CoreOS ClusterLoad Balancing Applications with NGINX in a CoreOS Cluster
Load Balancing Applications with NGINX in a CoreOS ClusterKevin Jones
 
Content Caching with NGINX and NGINX Plus
Content Caching with NGINX and NGINX PlusContent Caching with NGINX and NGINX Plus
Content Caching with NGINX and NGINX PlusKevin Jones
 
Delivering High Performance Websites with NGINX
Delivering High Performance Websites with NGINXDelivering High Performance Websites with NGINX
Delivering High Performance Websites with NGINXNGINX, Inc.
 
Introduction to Nginx
Introduction to NginxIntroduction to Nginx
Introduction to NginxKnoldus Inc.
 
Lcu14 Lightning Talk- NGINX
Lcu14 Lightning Talk- NGINXLcu14 Lightning Talk- NGINX
Lcu14 Lightning Talk- NGINXLinaro
 

What's hot (20)

How to monitor NGINX
How to monitor NGINXHow to monitor NGINX
How to monitor NGINX
 
Nginx dhruba mandal
Nginx dhruba mandalNginx dhruba mandal
Nginx dhruba mandal
 
Nginx Internals
Nginx InternalsNginx Internals
Nginx Internals
 
5 things you didn't know nginx could do velocity
5 things you didn't know nginx could do   velocity5 things you didn't know nginx could do   velocity
5 things you didn't know nginx could do velocity
 
What's New in NGINX Plus R12?
What's New in NGINX Plus R12? What's New in NGINX Plus R12?
What's New in NGINX Plus R12?
 
Learn nginx in 90mins
Learn nginx in 90minsLearn nginx in 90mins
Learn nginx in 90mins
 
Nginx A High Performance Load Balancer, Web Server & Reverse Proxy
Nginx A High Performance Load Balancer, Web Server & Reverse ProxyNginx A High Performance Load Balancer, Web Server & Reverse Proxy
Nginx A High Performance Load Balancer, Web Server & Reverse Proxy
 
Introduction to NGINX web server
Introduction to NGINX web serverIntroduction to NGINX web server
Introduction to NGINX web server
 
NGINX High-performance Caching
NGINX High-performance CachingNGINX High-performance Caching
NGINX High-performance Caching
 
High Availability Content Caching with NGINX
High Availability Content Caching with NGINXHigh Availability Content Caching with NGINX
High Availability Content Caching with NGINX
 
NGINX 101 - now with more Docker
NGINX 101 - now with more DockerNGINX 101 - now with more Docker
NGINX 101 - now with more Docker
 
Maximizing PHP Performance with NGINX
Maximizing PHP Performance with NGINXMaximizing PHP Performance with NGINX
Maximizing PHP Performance with NGINX
 
Rate Limiting with NGINX and NGINX Plus
Rate Limiting with NGINX and NGINX PlusRate Limiting with NGINX and NGINX Plus
Rate Limiting with NGINX and NGINX Plus
 
under the covers -- chef in 20 minutes or less
under the covers -- chef in 20 minutes or lessunder the covers -- chef in 20 minutes or less
under the covers -- chef in 20 minutes or less
 
Using NGINX as an Effective and Highly Available Content Cache
Using NGINX as an Effective and Highly Available Content CacheUsing NGINX as an Effective and Highly Available Content Cache
Using NGINX as an Effective and Highly Available Content Cache
 
Load Balancing Applications with NGINX in a CoreOS Cluster
Load Balancing Applications with NGINX in a CoreOS ClusterLoad Balancing Applications with NGINX in a CoreOS Cluster
Load Balancing Applications with NGINX in a CoreOS Cluster
 
Content Caching with NGINX and NGINX Plus
Content Caching with NGINX and NGINX PlusContent Caching with NGINX and NGINX Plus
Content Caching with NGINX and NGINX Plus
 
Delivering High Performance Websites with NGINX
Delivering High Performance Websites with NGINXDelivering High Performance Websites with NGINX
Delivering High Performance Websites with NGINX
 
Introduction to Nginx
Introduction to NginxIntroduction to Nginx
Introduction to Nginx
 
Lcu14 Lightning Talk- NGINX
Lcu14 Lightning Talk- NGINXLcu14 Lightning Talk- NGINX
Lcu14 Lightning Talk- NGINX
 

Similar to Extending functionality in nginx, with modules!

Gr8conf EU 2018 - Bring you infrastructure under control with Infrastructor
Gr8conf EU 2018 - Bring you infrastructure under control with InfrastructorGr8conf EU 2018 - Bring you infrastructure under control with Infrastructor
Gr8conf EU 2018 - Bring you infrastructure under control with InfrastructorStanislav Tiurikov
 
Node.js basics
Node.js basicsNode.js basics
Node.js basicsBen Lin
 
nginx: writing your first module
nginx: writing your first modulenginx: writing your first module
nginx: writing your first moduleredivy
 
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB Devroom
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB DevroomMore on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB Devroom
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB DevroomValeriy Kravchuk
 
Créer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heureCréer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heureAmaury Bouchard
 
Dragoncraft Architectural Overview
Dragoncraft Architectural OverviewDragoncraft Architectural Overview
Dragoncraft Architectural Overviewjessesanford
 
Building apache modules
Building apache modulesBuilding apache modules
Building apache modulesMarian Marinov
 
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2Alfonso Martino
 
Protocol handler in Gecko
Protocol handler in GeckoProtocol handler in Gecko
Protocol handler in GeckoChih-Hsuan Kuo
 
Php 5.6 From the Inside Out
Php 5.6 From the Inside OutPhp 5.6 From the Inside Out
Php 5.6 From the Inside OutFerenc Kovács
 
Automation with Ansible and Containers
Automation with Ansible and ContainersAutomation with Ansible and Containers
Automation with Ansible and ContainersRodolfo Carvalho
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudAndrea Righi
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
#OktoCampus - Workshop : An introduction to Ansible
#OktoCampus - Workshop : An introduction to Ansible#OktoCampus - Workshop : An introduction to Ansible
#OktoCampus - Workshop : An introduction to AnsibleCédric Delgehier
 

Similar to Extending functionality in nginx, with modules! (20)

Writing Nginx Modules
Writing Nginx ModulesWriting Nginx Modules
Writing Nginx Modules
 
Catalyst MVC
Catalyst MVCCatalyst MVC
Catalyst MVC
 
Gr8conf EU 2018 - Bring you infrastructure under control with Infrastructor
Gr8conf EU 2018 - Bring you infrastructure under control with InfrastructorGr8conf EU 2018 - Bring you infrastructure under control with Infrastructor
Gr8conf EU 2018 - Bring you infrastructure under control with Infrastructor
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
 
nginx: writing your first module
nginx: writing your first modulenginx: writing your first module
nginx: writing your first module
 
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB Devroom
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB DevroomMore on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB Devroom
More on bpftrace for MariaDB DBAs and Developers - FOSDEM 2022 MariaDB Devroom
 
Créer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heureCréer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heure
 
Dragoncraft Architectural Overview
Dragoncraft Architectural OverviewDragoncraft Architectural Overview
Dragoncraft Architectural Overview
 
Building apache modules
Building apache modulesBuilding apache modules
Building apache modules
 
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2
MuleSoft Meetup Roma - Runtime Fabric Series (From Zero to Hero) - Sessione 2
 
Protocol handler in Gecko
Protocol handler in GeckoProtocol handler in Gecko
Protocol handler in Gecko
 
Php 5.6 From the Inside Out
Php 5.6 From the Inside OutPhp 5.6 From the Inside Out
Php 5.6 From the Inside Out
 
Automation with Ansible and Containers
Automation with Ansible and ContainersAutomation with Ansible and Containers
Automation with Ansible and Containers
 
Jenkins' shared libraries in action
Jenkins' shared libraries in actionJenkins' shared libraries in action
Jenkins' shared libraries in action
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloud
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
Php
PhpPhp
Php
 
Php
PhpPhp
Php
 
#OktoCampus - Workshop : An introduction to Ansible
#OktoCampus - Workshop : An introduction to Ansible#OktoCampus - Workshop : An introduction to Ansible
#OktoCampus - Workshop : An introduction to Ansible
 

Recently uploaded

The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 

Recently uploaded (20)

The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 

Extending functionality in nginx, with modules!

  • 1. Extending functionality in nginx, with modules! 2012-01-31
  • 2. What is nginx? •The 2nd most used HTTP-server (Apache is 1st) •Asynchronous event-driven approach to request-handling. •Written in C •Readable source code!
  • 3. What kind of modules do we have? •handler modules - they process a request and produce output •filter modules - they manipulate the output produced by a handler •load-balancers - they choose a backend server to send a request to, when more than one backend server is eligible
  • 4. What will you learn through the course of this presentation? •Step 0: Building Nginx (with a module) •Step 1: Create a stub http content module, aka "Hello, World" •Step 2: Update the module to use an external library for content creation •Step 3: Give your module its own configuration directives •Step 4: Fetching arguments from the URL
  • 5. Let's get started Step 0: Building Nginx (with a module)
  • 6. Download & build # wget http://nginx.org/download/nginx-1.0.11.tar.gz # tar zxvf nginx-1.0.11.tar.gz # cd nginx-1.0.11.tar.gz # ./configure # make Straight forward. No surprises there.
  • 7. Building a third party module Modules are statically linked, so adding a new module requires a new nginx-build. # ./configure --add-module=$HOME/git/some_cool_module_I_found_somewhere_on_github # make Easy. (Want more modules? --add-module can be added several times)
  • 8. This is boring, show me the code! Step 1: Create a stub http content module, aka "Hello, World"
  • 9. Creating your own module A minimal HTTP module - let's call it "fun" - consists of a directory with two files: config and a source file ngx_http_fun_module.c A significant amount of the source-file will be stub-code.
  • 10. The config file Is sourced as a shell-script, and should set different environment variables to help the build-system understand what to do. A minimal config-file for a module without any external dependencies could look like this: ngx_addon_name=ngx_http_fun_module HTTP_MODULES="$HTTP_MODULES ngx_http_fun_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fun_module.c"
  • 11. The source file (1) Include your headers. #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> Declare a handler. static char * ngx_http_fun(ngx_conf_t * cf, ngx_command_t * cmd, void * conf); Create our HTTP body. static u_char ngx_fun_string[] = "This is fun!";
  • 12. How does nginx know about my configuration directives? Quoting the nginx source-code: struct ngx_command_s { 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; }; #define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } typedef struct ngx_command_s ngx_command_t; An array of ngx_command_t's, terminated by a ngx_null_command should be defined in your module.
  • 13. Declaring the configuration parameters available by your module Create a static array of the ngx_command_t-type, terminate it with ngx_null_command. Populate it with the commands available for your module. static ngx_command_t ngx_http_fun_commands[] = { { // Our command is named "fun": ngx_string("fun"), // The directive may be specified in the location-level of your nginx-config. // The directive does not take any arguments (NGX_CONF_NOARGS) NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, // A pointer to our handler-function. ngx_http_fun, // We're not using these two, they're related to the configuration structures. 0, 0, // A pointer to a post-processing handler. We're not using any here. NULL }, ngx_null_command };
  • 14. Declaring the module context Create a static ngx_http_module_t, populate all 8 elements with NULL. static ngx_http_module_t ngx_http_fun_module_ctx = { NULL, // preconfiguration NULL, // postconfiguration NULL, // create main configuration NULL, // init main configuration NULL, // create server configuration NULL, // merge server configuration NULL, // create location configuration NULL // merge location configuration }; This is used to determine multiple levels of configuration, and gives you control over which directives beat which in a race. Our module does not use any configuration parameters other than "fun;", so we don't need any handlers.
  • 15. Declaring the module description structure Create a (not static) structure of type ngx_module_t, reference the structures we've made like this: ngx_module_t ngx_http_fun_module = { NGX_MODULE_V1, &ngx_http_fun_module_ctx, // module context ngx_http_fun_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 };
  • 16. Creating the actual handler (1) A static function that returns an ngx_int_t, and receives a pointer to a ngx_http_request_t. static ngx_int_t ngx_http_fun_handler(ngx_http_request_t * r) { ngx_int_t rc; ngx_buf_t * b; ngx_chain_t out; We use rc to store the return value of certain function calls. We use * b to store our buffer pointer. out is the buffer chain.
  • 17. Creating the actual handler (2) A simple test of the kind of request we're receiving. // we response to 'GET' and 'HEAD' requests only if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; }
  • 18. Creating the actual handler (3) The request-body is useful if we're dealing with POST. We're not. // discard request body, since we don't need it here rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } This attempts to detach the request body from the request. It's an optimization.
  • 19. Creating the actual handler (4) Setting the content-type is important -- // set the 'Content-type' header r->headers_out.content_type_len = sizeof("text/html") - 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char * ) "text/html";
  • 20. Creating the actual handler (5) // send the header only, if the request type is http 'HEAD' if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1; return ngx_http_send_header(r); }
  • 21. Creating the actual handler (6) // allocate a buffer for your response body b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
  • 22. Creating the actual handler (7) // attach this buffer to the buffer chain out.buf = b; out.next = NULL;
  • 23. Creating the actual handler (8) // adjust the pointers of the buffer b->pos = ngx_fun_string; b->last = ngx_fun_string + sizeof(ngx_fun_string) - 1; b->memory = 1; // This buffer is in read-only memory // This means that filters should copy it, and not try to rewrite in place. b->last_buf = 1; // this is the last buffer in the buffer chain
  • 24. Creating the actual handler (9) // set the status line r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1;
  • 25. Creating the actual handler (10) // send the headers of your response rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } // send the buffer chain of your response return ngx_http_output_filter(r, &out); } ... almost done!
  • 26. Attaching the handler static char * ngx_http_fun(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_fun_handler; // handler to process the 'fun' directive return NGX_CONF_OK; } In this function, we can validate on the incoming ngx_command_t, and attach different handlers.
  • 27. Does this really work? Trygve will attempt to demonstrate. # make # sudo make install Update the nginx-configuration with a location handler, something like this: Location: /fun { fun; } Start nginx, and access http://localhost/fun/
  • 28. Of course it worked! But static text isn't any fun Step 2: Update the module to use an external library for content creation
  • 29. What are we going to do? Tell me now! •We're going to replace the static text with an image, dynamically created with libcairo. •We're going to learn about buffers and chains
  • 30. The config file - dependency handling Testing for dependencies is done with the feature tests provided by nginx ngx_feature="cairo" ngx_feature_name= ngx_feature_run=no ngx_feature_incs="#include <cairo.h>" ngx_feature_path="/usr/include/cairo" ngx_feature_libs=-lcairo ngx_feature_test="cairo_version()" . auto/feature if [ $ngx_found = yes ]; then ngx_addon_name=ngx_http_fun_module HTTP_MODULES="$HTTP_MODULES ngx_http_fun_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fun_module.c" CORE_LIBS="$CORE_LIBS `pkg-config cairo cairo-png --libs`" CFLAGS="$CFLAGS `pkg-config cairo cairo-png --cflags`" else cat << END $0: error: the fun module requires the cairo library. END exit 1 fi
  • 31. The Cairo part (1) Include the header, and define M_PI #include <cairo.h> #define M_PI 3.14159265 struct closure { ngx_http_request_t * r; ngx_chain_t * chain; uint32_t length; }; The struct will be used by a callback function we're going to create later. Throw away ngx_fun_string while you're at it, we're not going to be using that.
  • 32. The Cairo part (2) In ngx_http_fun_handler(), remove the ngx_buf_t (we'll do these things in a callback function later), and add our struct. static ngx_int_t ngx_http_fun_handler(ngx_http_request_t * r) { ngx_int_t rc; ngx_chain_t out; struct closure c = { r, &out, 0 };
  • 33. The Cairo part (3) Remove our header-handling from before - we won't be able to calculate content-length before we've created the png. // set the 'Content-type' header r->headers_out.content_type_len = sizeof("text/html") - 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char * ) "text/html"; // send the header only, if the request type is http 'HEAD' if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = sizeof(ngx_fun_string) - 1; return ngx_http_send_header(r); } // allocate a buffer for your response body b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
  • 34. The Cairo part (4) This is basically ripped from one of their many examples on their web page. cairo_surface_t * surface; cairo_t * cr; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 256, 256); cr = cairo_create (surface); double xc = 128.0; double yc = 128.0; double radius = 100.0; double angle1 = 270.0 * (M_PI/180.0); // angles are specified double angle2 = 180.0 * (M_PI/180.0); // in radians
  • 35. The Cairo part (5) cairo_set_line_width (cr, 10.0); cairo_arc (cr, xc, yc, radius, angle1, angle2); cairo_stroke (cr); // draw helping lines cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6); cairo_set_line_width (cr, 6.0); cairo_arc (cr, xc, yc, 10.0, 0, 2*M_PI); cairo_fill (cr); cairo_arc (cr, xc, yc, radius, angle1, angle1); cairo_line_to (cr, xc, yc); cairo_arc (cr, xc, yc, radius, angle2, angle2); cairo_line_to (cr, xc, yc); cairo_stroke (cr);
  • 36. The Cairo part (6) Make sure that our buffer chain is NULL'ed. Remove any references to the ngx_buf_t from earlier. out.buf = NULL; out.next = NULL; // Copy the png image to our buffer chain (we provide our own callback-function) rc = cairo_surface_write_to_png_stream(surface, copy_png_to_chain, &c); // Free cairo stuff. cairo_destroy(cr); cairo_surface_destroy(surface); // If we for some reason didn't manage to copy the png to our buffer, throw 503. if ( rc != CAIRO_STATUS_SUCCESS ) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } The cairo_surface_write_to_png_stream uses a callback-function to copy the png-data to a buffer, in the way we want. Remember to free resources, and check that everything went well.
  • 37. The Cairo part (7) We're going to set our headers now. New content-type - the length has been calculated by our callback function and is stored in our struct. // set the 'Content-type' header r->headers_out.content_type_len = sizeof("image/png") - 1; r->headers_out.content_type.len = sizeof("image/png") - 1; r->headers_out.content_type.data = (u_char * ) "image/png"; // set the status line r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = c.length; // send the headers of your response rc = ngx_http_send_header(r); // We've added the NGX_HTTP_HEAD check here, because we're unable to set content length before // we've actually calculated it (which is done by generating the image). // This is a waste of resources, and is why caching solutions exist. if (rc == NGX_ERROR || rc > NGX_OK || r->header_only || r->method == NGX_HTTP_HEAD) { return rc; }
  • 38. Allocating memory in an nginx-module •Nginx has its own memory management system. •ngx_pcalloc(pool, amount) will give you a pointer to some memory you can do what you want with. •'pool' attaches the memory to an owner. Some memory will be allocated for the request (ngx_http_request_t->pool), and some will be allocated for the module configuration (ngx_conf_t->pool). •Nginx deals with freeing/reusing this memory when it's owner doesn't need it anymore.
  • 39. How does buffers and chains work? •ngx_chain_t is a linked list of ngx_buf_t's. •You provide a pointer to the first ngx_chain_t in the chain to ngx_http_output_filter() •It will walk through the chain, and pass all the buffers to the output filter •You need to get comfortable with this, or attaching bits and pieces together will be tricky.
  • 40. Forgot what a linked list is? struct element { int value; struct element * next; }; A linked list is typically a struct, referencing an element of its own type. The last element is often set to NULL, but they can also be circular, or double (having an additional reference for the previous item in the list), etc.
  • 41. Creating our callback function (1) Cairo provides you with an interface for accessing png-images however you'd like. What we want to do, is to attach them to our chain. We do that by implementing our own callback function. This function will be called an undefined number of times. static cairo_status_t copy_png_to_chain(void * closure, const unsigned char * data, unsigned int length) { // closure is a 'struct closure' struct closure * c = closure; // Just a helper pointer, to help us traverse the linked list. ngx_chain_t * ch = c->chain; // We track the size of the png-file in our closure struct. c->length += length;
  • 42. Creating our callback function (2) // The allocated memory belongs to the request-pool. ngx_buf_t * b = ngx_pcalloc(c->r->pool, sizeof(ngx_buf_t)); unsigned char * d = ngx_pcalloc(c->r->pool, length); // We make sure to fail if we're unable to allocate memory. if (b == NULL || d == NULL) { return CAIRO_STATUS_NO_MEMORY; } // Copy data to our new buffer, and set the pointers. ngx_memcpy(d, data, length); b->pos = d; b->last = d + length; b->memory = 1; b->last_buf = 1;
  • 43. Creating our callback function (3) If the first element isn't put into place, we can quit early. // Handle the first element in our linked list. if ( c->chain->buf == NULL ) { c->chain->buf = b; return CAIRO_STATUS_SUCCESS; }
  • 44. Creating our callback function (4) // Skip to the end of the linked list. while ( ch->next ) { ch = ch->next; }
  • 45. Creating our callback function (5) // Allocate a new link in our chain. ch->next = ngx_pcalloc(c->r->pool, sizeof(ngx_chain_t)); if ( ch->next == NULL ) { return CAIRO_STATUS_NO_MEMORY; } // Attach our buffer at the end. ch->next->buf = b; ch->next->next = NULL; return CAIRO_STATUS_SUCCESS; } Presto! That was easy :)
  • 46. We're ready to test this out! Fingers crossed.
  • 47. Yeah, but what if I want to be able to configure my module? •Step 3: Give your module its own configuration directives
  • 48. Create a datatype for config-storage typedef struct { ngx_uint_t radius; } ngx_http_fun_loc_conf_t;
  • 49. Extend the ngx_http_fun_commands[]-array { // New parameter: "fun_radius": ngx_string("fun_radius"), // Can be specified on the main level of the config, // can be specified in the server level of the config, // can be specified in the location level of the config, // the directive takes 1 argument (NGX_CONF_TAKE1) NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, // A builtin function for setting numeric variables ngx_conf_set_num_slot, // We tell nginx how we're storing the config. NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fun_loc_conf_t, radius), NULL },
  • 50. Create a function for dealing with the config creation static void * ngx_http_fun_create_loc_conf(ngx_conf_t * cf) { ngx_http_fun_loc_conf_t * conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fun_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } conf->radius = NGX_CONF_UNSET_UINT; return conf; }
  • 51. Create a function for merging config static char * ngx_http_fun_merge_loc_conf(ngx_conf_t * cf, void * parent, void * child) { ngx_http_fun_loc_conf_t * prev = parent; ngx_http_fun_loc_conf_t * conf = child; ngx_conf_merge_uint_value(conf->radius, prev->radius, 100); if (conf->radius < 1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "radius must be equal or more than 1"); return NGX_CONF_ERROR; } if (conf->radius > 1000 ) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "radius must be equal or less than 1000"); return NGX_CONF_ERROR; } return NGX_CONF_OK; }
  • 52. Add pointers to our new function in the ngx_htt_fun_module_ctx ngx_http_fun_create_loc_conf, // create location configuration ngx_http_fun_merge_loc_conf // merge location configuration
  • 53. Give our handler access to the configuration data ngx_http_fun_loc_conf_t * cglcf; cglcf = ngx_http_get_module_loc_conf(r, ngx_http_fun_module);
  • 54. Using the config-data in our module We override the dimensions of our image, and we override the center-position. surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cglcf->radius*2 + 64, cglcf->radius*2 + 64); cr = cairo_create (surface); double xc = cglcf->radius + 32; double yc = cglcf->radius + 32; double radius = cglcf->radius;
  • 55. It can't possibly be that easy! Show me! Update the configuration to something like this: Location: /funbig { fun; fun_radius 500; } Location: /funsmall { fun; fun_radius 50; } The different URLs result in different sized images.
  • 56. I want to deal with user-input •Step 4: Fetching arguments from the URL
  • 57. This is as easy as parsing strings in C. cough The request has an uri-element, with members .data and .len. Adding something like this to your handle will give you the 3 last characters of the URI as an integer; char * uri; int angle = 0; if ( r->uri.len > 3 ) { uri = (char * )r->uri.data + r->uri.len - 3; angle = strtol(uri, NULL, 10); }
  • 58. Which you then can use ... double angle1 = 0.0; // angles are specified double angle2 = angle * (M_PI/180.0); // in radians
  • 59. Let's have a look at this too! Last demo.
  • 60. References •http://www.evanmiller.org/nginx-modules-guide.html •The nginx source code. •http://blog.zhuzhaoyuan.com/ •http://www.nginxguts.com/