Add support for auto reloading the database if it has changed
This commit is contained in:
parent
7eaa956e61
commit
39a8abfa60
3 changed files with 204 additions and 3 deletions
13
README.md
13
README.md
|
@ -48,6 +48,7 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max
|
||||||
http {
|
http {
|
||||||
...
|
...
|
||||||
geoip2 /etc/maxmind-country.mmdb {
|
geoip2 /etc/maxmind-country.mmdb {
|
||||||
|
auto_reload 5m;
|
||||||
$geoip2_metadata_country_build metadata build_epoch;
|
$geoip2_metadata_country_build metadata build_epoch;
|
||||||
$geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
|
$geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
|
||||||
$geoip2_data_country_name country names en;
|
$geoip2_data_country_name country names en;
|
||||||
|
@ -78,7 +79,17 @@ Retrieve metadata regarding the geoip database.
|
||||||
```
|
```
|
||||||
$variable_name metadata <field>
|
$variable_name metadata <field>
|
||||||
```
|
```
|
||||||
Currently the only metadata field supported is build_epoch.
|
Available fields:
|
||||||
|
- build_epoch: the build timestamp of the maxmind database.
|
||||||
|
- last_check: the last time the database was checked for changes (when using auto_reload)
|
||||||
|
- last_reload: the last time the database was reloaded (when using auto_reload)
|
||||||
|
|
||||||
|
##### Autoreload (default: disabled):
|
||||||
|
Enabling auto reload will have nginx check the modification time of the database at the specified
|
||||||
|
interval and reload it if it has changed.
|
||||||
|
```
|
||||||
|
auto_reload <interval>
|
||||||
|
```
|
||||||
|
|
||||||
##### GeoIP:
|
##### GeoIP:
|
||||||
```
|
```
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MMDB_s mmdb;
|
MMDB_s mmdb;
|
||||||
MMDB_lookup_result_s result;
|
MMDB_lookup_result_s result;
|
||||||
|
time_t last_check;
|
||||||
|
time_t last_change;
|
||||||
|
time_t check_interval;
|
||||||
#if (NGX_HAVE_INET6)
|
#if (NGX_HAVE_INET6)
|
||||||
uint8_t address[16];
|
uint8_t address[16];
|
||||||
#else
|
#else
|
||||||
|
@ -41,6 +44,8 @@ typedef struct {
|
||||||
} ngx_http_geoip2_metadata_t;
|
} ngx_http_geoip2_metadata_t;
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database,
|
||||||
|
ngx_log_t *log);
|
||||||
static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r,
|
||||||
ngx_http_variable_value_t *v, uintptr_t data);
|
ngx_http_variable_value_t *v, uintptr_t data);
|
||||||
static ngx_int_t ngx_http_geoip2_metadata(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_geoip2_metadata(ngx_http_request_t *r,
|
||||||
|
@ -49,6 +54,8 @@ static void *ngx_http_geoip2_create_conf(ngx_conf_t *cf);
|
||||||
static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf);
|
static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf);
|
||||||
static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
|
static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||||
void *conf);
|
void *conf);
|
||||||
|
static char *ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||||
|
void *conf);
|
||||||
static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
|
static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||||
void *conf);
|
void *conf);
|
||||||
static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf,
|
static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf,
|
||||||
|
@ -129,6 +136,41 @@ ngx_module_t ngx_http_geoip2_module = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database, ngx_log_t *log)
|
||||||
|
{
|
||||||
|
struct stat attr;
|
||||||
|
MMDB_s tmpdb;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (database->check_interval > 0
|
||||||
|
&& database->last_check + database->check_interval <= ngx_time()) {
|
||||||
|
database->last_check = ngx_time();
|
||||||
|
stat(database->mmdb.filename, &attr);
|
||||||
|
|
||||||
|
if (attr.st_mtime > database->last_change) {
|
||||||
|
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);
|
||||||
|
|
||||||
|
if (status != MMDB_SUCCESS) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, log, 0,
|
||||||
|
"MMDB_open(\"%s\") failed to reload - %s",
|
||||||
|
database->mmdb.filename, MMDB_strerror(status));
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
database->last_change = attr.st_mtime;
|
||||||
|
MMDB_close(&database->mmdb);
|
||||||
|
database->mmdb = tmpdb;
|
||||||
|
|
||||||
|
ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
|
||||||
|
tmpdb.filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||||
uintptr_t data)
|
uintptr_t data)
|
||||||
|
@ -149,6 +191,8 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||||
unsigned long address;
|
unsigned long address;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ngx_http_geoip2_reload(database, r->connection->log);
|
||||||
|
|
||||||
if (geoip2->source.value.len > 0) {
|
if (geoip2->source.value.len > 0) {
|
||||||
if (ngx_http_complex_value(r, &geoip2->source, &val) != NGX_OK) {
|
if (ngx_http_complex_value(r, &geoip2->source, &val) != NGX_OK) {
|
||||||
goto not_found;
|
goto not_found;
|
||||||
|
@ -294,8 +338,14 @@ ngx_http_geoip2_metadata(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||||
ngx_http_geoip2_db_t *database = metadata->database;
|
ngx_http_geoip2_db_t *database = metadata->database;
|
||||||
u_char *p;
|
u_char *p;
|
||||||
|
|
||||||
|
ngx_http_geoip2_reload(database, r->connection->log);
|
||||||
|
|
||||||
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
|
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
|
||||||
FORMAT("%uL", database->mmdb.metadata.build_epoch);
|
FORMAT("%uL", database->mmdb.metadata.build_epoch);
|
||||||
|
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
|
||||||
|
FORMAT("%T", database->last_check);
|
||||||
|
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
|
||||||
|
FORMAT("%T", database->last_change);
|
||||||
} else {
|
} else {
|
||||||
v->not_found = 1;
|
v->not_found = 1;
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
@ -376,6 +426,8 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
return NGX_CONF_ERROR;
|
return NGX_CONF_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
database->last_check = database->last_change = ngx_time();
|
||||||
|
|
||||||
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
|
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
|
||||||
|
|
||||||
if (status != MMDB_SUCCESS) {
|
if (status != MMDB_SUCCESS) {
|
||||||
|
@ -392,7 +444,7 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
save = *cf;
|
save = *cf;
|
||||||
cf->handler = ngx_http_geoip2_add_variable;
|
cf->handler = ngx_http_geoip2_parse_config;
|
||||||
cf->handler_conf = (void *) database;
|
cf->handler_conf = (void *) database;
|
||||||
|
|
||||||
rv = ngx_conf_parse(cf, NULL);
|
rv = ngx_conf_parse(cf, NULL);
|
||||||
|
@ -401,6 +453,48 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||||
|
{
|
||||||
|
ngx_http_geoip2_db_t *database;
|
||||||
|
ngx_str_t *value;
|
||||||
|
time_t interval;
|
||||||
|
|
||||||
|
value = cf->args->elts;
|
||||||
|
|
||||||
|
if (value[0].data[0] == '$') {
|
||||||
|
return ngx_http_geoip2_add_variable(cf, dummy, conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value[0].len == 11
|
||||||
|
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
|
||||||
|
if ((int) cf->args->nelts != 2) {
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid number of arguments for auto_reload");
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = ngx_parse_time(&value[1], true);
|
||||||
|
|
||||||
|
if (interval == (time_t) NGX_ERROR) {
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid interval for auto_reload \"%V\"",
|
||||||
|
value[1]);
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
database = (ngx_http_geoip2_db_t *) conf;
|
||||||
|
database->check_interval = interval;
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid setting \"%V\"", &value[0]);
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MMDB_s mmdb;
|
MMDB_s mmdb;
|
||||||
MMDB_lookup_result_s result;
|
MMDB_lookup_result_s result;
|
||||||
|
time_t last_check;
|
||||||
|
time_t last_change;
|
||||||
|
time_t check_interval;
|
||||||
#if (NGX_HAVE_INET6)
|
#if (NGX_HAVE_INET6)
|
||||||
uint8_t address[16];
|
uint8_t address[16];
|
||||||
#else
|
#else
|
||||||
|
@ -40,11 +43,17 @@ typedef struct {
|
||||||
} ngx_stream_geoip2_metadata_t;
|
} ngx_stream_geoip2_metadata_t;
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database,
|
||||||
|
ngx_log_t *log);
|
||||||
static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
|
static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
|
||||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||||
static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s,
|
static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s,
|
||||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||||
static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
|
static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
|
||||||
|
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||||
|
void *conf);
|
||||||
|
static char *ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||||
|
void *conf);
|
||||||
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
|
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||||
void *conf);
|
void *conf);
|
||||||
static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
|
static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||||
|
@ -106,6 +115,41 @@ ngx_module_t ngx_stream_geoip2_module = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database, ngx_log_t *log)
|
||||||
|
{
|
||||||
|
struct stat attr;
|
||||||
|
MMDB_s tmpdb;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (database->check_interval > 0
|
||||||
|
&& database->last_check + database->check_interval <= ngx_time()) {
|
||||||
|
database->last_check = ngx_time();
|
||||||
|
stat(database->mmdb.filename, &attr);
|
||||||
|
|
||||||
|
if (attr.st_mtime > database->last_change) {
|
||||||
|
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);
|
||||||
|
|
||||||
|
if (status != MMDB_SUCCESS) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, log, 0,
|
||||||
|
"MMDB_open(\"%s\") failed to reload - %s",
|
||||||
|
database->mmdb.filename, MMDB_strerror(status));
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
database->last_change = attr.st_mtime;
|
||||||
|
MMDB_close(&database->mmdb);
|
||||||
|
database->mmdb = tmpdb;
|
||||||
|
|
||||||
|
ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
|
||||||
|
tmpdb.filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
|
ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
|
||||||
uintptr_t data)
|
uintptr_t data)
|
||||||
|
@ -124,6 +168,8 @@ ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t
|
||||||
unsigned long address;
|
unsigned long address;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ngx_stream_geoip2_reload(database, s->connection->log);
|
||||||
|
|
||||||
if (geoip2->source.value.len > 0) {
|
if (geoip2->source.value.len > 0) {
|
||||||
if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) {
|
if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) {
|
||||||
goto not_found;
|
goto not_found;
|
||||||
|
@ -263,8 +309,14 @@ ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t
|
||||||
ngx_stream_geoip2_db_t *database = metadata->database;
|
ngx_stream_geoip2_db_t *database = metadata->database;
|
||||||
u_char *p;
|
u_char *p;
|
||||||
|
|
||||||
|
ngx_stream_geoip2_reload(database, s->connection->log);
|
||||||
|
|
||||||
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
|
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
|
||||||
FORMAT("%uL", database->mmdb.metadata.build_epoch);
|
FORMAT("%uL", database->mmdb.metadata.build_epoch);
|
||||||
|
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
|
||||||
|
FORMAT("%T", database->last_check);
|
||||||
|
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
|
||||||
|
FORMAT("%T", database->last_change);
|
||||||
} else {
|
} else {
|
||||||
v->not_found = 1;
|
v->not_found = 1;
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
@ -343,6 +395,8 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
return NGX_CONF_ERROR;
|
return NGX_CONF_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
database->last_check = database->last_change = ngx_time();
|
||||||
|
|
||||||
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
|
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
|
||||||
|
|
||||||
if (status != MMDB_SUCCESS) {
|
if (status != MMDB_SUCCESS) {
|
||||||
|
@ -359,7 +413,7 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
save = *cf;
|
save = *cf;
|
||||||
cf->handler = ngx_stream_geoip2_add_variable;
|
cf->handler = ngx_stream_geoip2_parse_config;
|
||||||
cf->handler_conf = (void *) database;
|
cf->handler_conf = (void *) database;
|
||||||
|
|
||||||
rv = ngx_conf_parse(cf, NULL);
|
rv = ngx_conf_parse(cf, NULL);
|
||||||
|
@ -368,6 +422,48 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||||
|
{
|
||||||
|
ngx_stream_geoip2_db_t *database;
|
||||||
|
ngx_str_t *value;
|
||||||
|
time_t interval;
|
||||||
|
|
||||||
|
value = cf->args->elts;
|
||||||
|
|
||||||
|
if (value[0].data[0] == '$') {
|
||||||
|
return ngx_stream_geoip2_add_variable(cf, dummy, conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value[0].len == 11
|
||||||
|
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
|
||||||
|
if ((int) cf->args->nelts != 2) {
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid number of arguments for auto_reload");
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = ngx_parse_time(&value[1], true);
|
||||||
|
|
||||||
|
if (interval == (time_t) NGX_ERROR) {
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid interval for auto_reload \"%V\"",
|
||||||
|
value[1]);
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
database = (ngx_stream_geoip2_db_t *) conf;
|
||||||
|
database->check_interval = interval;
|
||||||
|
return NGX_CONF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||||
|
"invalid setting \"%V\"", &value[0]);
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue