HTTP API¶
The HTTP API provides structures, constants, and functions for building HTTP modules.
HTTP Status Codes¶
NGX_HTTP_OK /* 200 */
NGX_HTTP_CREATED /* 201 */
NGX_HTTP_NO_CONTENT /* 204 */
NGX_HTTP_MOVED_PERMANENTLY /* 301 */
NGX_HTTP_MOVED_TEMPORARILY /* 302 */
NGX_HTTP_NOT_MODIFIED /* 304 */
NGX_HTTP_BAD_REQUEST /* 400 */
NGX_HTTP_UNAUTHORIZED /* 401 */
NGX_HTTP_FORBIDDEN /* 403 */
NGX_HTTP_NOT_FOUND /* 404 */
NGX_HTTP_INTERNAL_SERVER_ERROR /* 500 */
NGX_HTTP_BAD_GATEWAY /* 502 */
NGX_HTTP_SERVICE_UNAVAILABLE /* 503 */
Request Structure (ngx_http_request_t)¶
The central structure for HTTP processing:
struct ngx_http_request_t {
ngx_connection_t *connection; /* Client connection */
ngx_pool_t *pool; /* Request memory pool */
ngx_http_headers_in_t headers_in; /* Incoming headers */
ngx_http_headers_out_t headers_out; /* Outgoing headers */
ngx_str_t uri; /* Request URI path */
ngx_str_t args; /* Query string */
ngx_str_t method_name; /* GET, POST, etc. */
ngx_http_request_t *main; /* Main request */
ngx_http_request_t *parent; /* Parent (for subrequests) */
unsigned header_only:1; /* Only send headers */
unsigned internal:1; /* Internal redirect */
/* ... many more fields */
};
Complete Handler Example¶
Simple "Hello World" Handler¶
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r);
/* Directive definition */
static ngx_command_t ngx_http_hello_world_commands[] = {
{
ngx_string("hello_world"),
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_hello_world,
0,
0,
NULL
},
ngx_null_command
};
/* Module context */
static ngx_http_module_t ngx_http_hello_world_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 */
};
/* Module definition */
ngx_module_t ngx_http_hello_world_module = {
NGX_MODULE_V1,
&ngx_http_hello_world_module_ctx,
ngx_http_hello_world_commands,
NGX_HTTP_MODULE,
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
};
/* Directive handler - sets up content handler */
static char *
ngx_http_hello_world(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_hello_world_handler;
return NGX_CONF_OK;
}
/* Request handler */
static ngx_int_t
ngx_http_hello_world_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
static u_char hello[] = "Hello, World!\n";
/* Only handle GET and HEAD */
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
/* Discard request body */
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
/* Set response headers */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = sizeof(hello) - 1;
r->headers_out.content_type_len = sizeof("text/plain") - 1;
ngx_str_set(&r->headers_out.content_type, "text/plain");
/* Send headers */
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
/* Create response buffer */
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = hello;
b->last = hello + sizeof(hello) - 1;
b->memory = 1; /* Content is in read-only memory */
b->last_buf = 1; /* This is the last buffer */
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
Reading Request Headers¶
Access Common Headers¶
static ngx_int_t
ngx_http_check_headers(ngx_http_request_t *r)
{
/* Host header (always present in HTTP/1.1) */
if (r->headers_in.host) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"Host: %V", &r->headers_in.host->value);
}
/* User-Agent */
if (r->headers_in.user_agent) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"User-Agent: %V", &r->headers_in.user_agent->value);
}
/* Content-Type */
if (r->headers_in.content_type) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"Content-Type: %V", &r->headers_in.content_type->value);
}
/* Content-Length as number */
if (r->headers_in.content_length_n > 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"Content-Length: %O", r->headers_in.content_length_n);
}
return NGX_OK;
}
Search for Custom Headers¶
static ngx_table_elt_t *
ngx_http_find_header(ngx_http_request_t *r, u_char *name, size_t len)
{
ngx_list_part_t *part;
ngx_table_elt_t *h;
ngx_uint_t i;
part = &r->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (len != h[i].key.len) {
continue;
}
if (ngx_strncasecmp(name, h[i].key.data, len) == 0) {
return &h[i];
}
}
return NULL;
}
/* Usage */
static ngx_int_t
ngx_http_check_auth_token(ngx_http_request_t *r)
{
ngx_table_elt_t *h;
h = ngx_http_find_header(r, (u_char *) "X-Auth-Token", 12);
if (h == NULL) {
return NGX_HTTP_UNAUTHORIZED;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"Auth token: %V", &h->value);
return NGX_OK;
}
Setting Response Headers¶
Add Custom Headers¶
static ngx_int_t
ngx_http_add_custom_headers(ngx_http_request_t *r)
{
ngx_table_elt_t *h;
/* Add X-Powered-By header */
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "X-Powered-By");
ngx_str_set(&h->value, "NGINX Module");
/* Add X-Request-ID header with dynamic value */
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "X-Request-ID");
/* Allocate value from pool */
h->value.data = ngx_pnalloc(r->pool, 32);
if (h->value.data == NULL) {
return NGX_ERROR;
}
h->value.len = ngx_sprintf(h->value.data, "%016xD",
(uint32_t) ngx_random()) - h->value.data;
return NGX_OK;
}
Subrequests¶
Execute internal requests to other locations:
static ngx_int_t ngx_http_auth_subrequest_done(ngx_http_request_t *r,
void *data, ngx_int_t rc);
static ngx_int_t
ngx_http_auth_check(ngx_http_request_t *r)
{
ngx_http_request_t *sr;
ngx_http_post_subrequest_t *ps;
ngx_str_t uri;
/* Allocate post-subrequest structure */
ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
if (ps == NULL) {
return NGX_ERROR;
}
ps->handler = ngx_http_auth_subrequest_done;
ps->data = r;
/* Create subrequest to /auth endpoint */
ngx_str_set(&uri, "/auth");
if (ngx_http_subrequest(r, &uri, NULL, &sr, ps,
NGX_HTTP_SUBREQUEST_WAITED) != NGX_OK) {
return NGX_ERROR;
}
/* Don't send output from subrequest to client */
sr->header_only = 1;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_auth_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
ngx_http_request_t *pr = data; /* Parent request */
/* Check subrequest result */
if (r->headers_out.status == NGX_HTTP_OK) {
/* Auth succeeded - continue processing parent */
pr->write_event_handler = ngx_http_core_run_phases;
return NGX_OK;
}
/* Auth failed */
return NGX_HTTP_FORBIDDEN;
}
Body Filters¶
Process response body before sending to client:
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_uppercase_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_chain_t *cl;
ngx_buf_t *b;
u_char *p;
/* Process each buffer in the chain */
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
/* Only process memory buffers */
if (!ngx_buf_in_memory(b)) {
continue;
}
/* Convert to uppercase */
for (p = b->pos; p < b->last; p++) {
*p = ngx_toupper(*p);
}
}
/* Pass to next filter */
return ngx_http_next_body_filter(r, in);
}
/* Register filter in postconfiguration */
static ngx_int_t
ngx_http_uppercase_filter_init(ngx_conf_t *cf)
{
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_uppercase_body_filter;
return NGX_OK;
}
Reading Request Body¶
static void ngx_http_mymodule_body_handler(ngx_http_request_t *r);
static ngx_int_t
ngx_http_mymodule_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
/* Start reading body */
rc = ngx_http_read_client_request_body(r, ngx_http_mymodule_body_handler);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static void
ngx_http_mymodule_body_handler(ngx_http_request_t *r)
{
ngx_chain_t *cl;
ngx_buf_t *b;
size_t len = 0;
if (r->request_body == NULL || r->request_body->bufs == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
/* Calculate total body length */
for (cl = r->request_body->bufs; cl; cl = cl->next) {
b = cl->buf;
if (b->in_file) {
len += b->file_last - b->file_pos;
} else {
len += b->last - b->pos;
}
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"received body: %uz bytes", len);
/* Process body and send response */
ngx_http_finalize_request(r, ngx_http_send_response(r));
}
Common Patterns¶
Return JSON Response¶
static ngx_int_t
ngx_http_json_response(ngx_http_request_t *r, ngx_uint_t status,
const char *json)
{
ngx_buf_t *b;
ngx_chain_t out;
size_t len;
len = ngx_strlen(json);
r->headers_out.status = status;
r->headers_out.content_length_n = len;
ngx_str_set(&r->headers_out.content_type, "application/json");
if (ngx_http_send_header(r) != NGX_OK) {
return NGX_ERROR;
}
if (r->header_only) {
return NGX_OK;
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b->pos, json, len);
b->last = b->pos + len;
b->last_buf = 1;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
/* Usage */
return ngx_http_json_response(r, NGX_HTTP_OK,
"{\"status\":\"ok\",\"message\":\"Hello\"}");
Internal Redirect¶
static ngx_int_t
ngx_http_redirect_internal(ngx_http_request_t *r, ngx_str_t *uri)
{
ngx_http_internal_redirect(r, uri, &r->args);
return NGX_DONE;
}