/*
* Copyright (C) Igor Sysoev, Alexandr Gomoliako
* Copyright (C) Nginx, Inc.
*/
/* TODO
- separate tests for reader, writer, connector, timer;
use the same layout for resolver's test as well;
- RVs as buffers, can save some memcpy, sometimes a lot;
- fix debug messages;
- eader/writer: avoid ngx_perl_read/write for EAGAIN calls;
- cleanup timer, Newz on ngx_event_t instead of allocating
new connectiion;
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_http_perl_module.h>
typedef struct {
ngx_array_t *modules;
ngx_array_t *requires;
ngx_array_t *init_worker;
ngx_array_t *exit_worker;
} ngx_http_perl_main_conf_t;
typedef struct {
SV *sub;
ngx_str_t handler;
SV *access_sub;
ngx_str_t access_handler;
#if (NGX_HTTP_SSL)
ngx_ssl_t *ssl;
#endif
ngx_int_t read_body;
} ngx_http_perl_loc_conf_t;
typedef struct {
SV *sub;
ngx_str_t handler;
} ngx_http_perl_variable_t;
#if (NGX_HTTP_SSI)
static ngx_int_t ngx_http_perl_ssi(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);
#endif
static char *ngx_http_perl_init_interpreter(ngx_conf_t *cf,
ngx_http_perl_main_conf_t *pmcf);
static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,
ngx_http_perl_main_conf_t *pmcf);
static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,
ngx_log_t *log);
static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);
static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);
static ngx_int_t ngx_http_perl_postconfiguration(ngx_conf_t *cf);
static void *ngx_http_perl_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_perl_app(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_perl_access(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_perl_eval(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_perl_init_worker(ngx_cycle_t *cycle);
static void ngx_http_perl_exit_worker(ngx_cycle_t *cycle);
static void ngx_http_perl_exit(ngx_cycle_t *cycle);
static void ngx_perl_timer_callback(ngx_event_t *ev);
static void ngx_perl_connection_cleanup(void *data);
static void ngx_perl_connect_handler(ngx_event_t *ev);
static void ngx_perl_dummy_handler(ngx_event_t *ev);
static void ngx_perl_read_handler(ngx_event_t *ev);
static void ngx_perl_write_handler(ngx_event_t *ev);
static void ngx_perl_resolver_timeout_handler(ngx_event_t *ev);
static void ngx_perl_resolver_handler(ngx_resolver_ctx_t *ctx);
#if (NGX_HTTP_SSL)
static ngx_int_t ngx_http_perl_set_ssl(ngx_conf_t *cf,
ngx_http_perl_loc_conf_t *plcf);
static void ngx_perl_ssl_handshake_handler(ngx_connection_t *c);
#endif
static void ngx_http_perl_ctx_cleanup(void *data);
static ngx_command_t ngx_http_perl_commands[] = {
{ ngx_string("perl_modules"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_perl_main_conf_t, modules),
NULL },
{ ngx_string("perl_inc"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_perl_main_conf_t, modules),
NULL },
{ ngx_string("perl_require"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_perl_main_conf_t, requires),
NULL },
{ ngx_string("perl_init_worker"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_perl_main_conf_t, init_worker),
NULL },
{ ngx_string("perl_exit_worker"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_perl_main_conf_t, exit_worker),
NULL },
{ ngx_string("perl_handler"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_http_perl,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("perl_content"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_http_perl,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("perl_app"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_http_perl_app,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("perl_access"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_http_perl_access,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("perl_set"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
ngx_http_perl_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("perl_eval"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_perl_eval,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_perl_module_ctx = {
ngx_http_perl_preconfiguration, /* preconfiguration */
ngx_http_perl_postconfiguration, /* postconfiguration */
ngx_http_perl_create_main_conf, /* create main configuration */
ngx_http_perl_init_main_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_perl_create_loc_conf, /* create location configuration */
ngx_http_perl_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_perl_module = {
NGX_MODULE_V1,
&ngx_http_perl_module_ctx, /* module context */
ngx_http_perl_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_http_perl_init_worker, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_http_perl_exit_worker, /* exit process */
ngx_http_perl_exit, /* exit master */
NGX_MODULE_V1_PADDING
};
#if (NGX_HTTP_SSI)
#define NGX_HTTP_PERL_SSI_SUB 0
#define NGX_HTTP_PERL_SSI_ARG 1
static ngx_http_ssi_param_t ngx_http_perl_ssi_params[] = {
{ ngx_string("sub"), NGX_HTTP_PERL_SSI_SUB, 1, 0 },
{ ngx_string("arg"), NGX_HTTP_PERL_SSI_ARG, 0, 1 },
{ ngx_null_string, 0, 0, 0 }
};
static ngx_http_ssi_command_t ngx_http_perl_ssi_command = {
ngx_string("perl"), ngx_http_perl_ssi, ngx_http_perl_ssi_params, 0, 0, 1
};
#endif
static ngx_str_t ngx_null_name = ngx_null_string;
static HV *nginx_stash;
static PerlInterpreter *my_perl;
ngx_log_t *ngx_perl_log;
static void
ngx_http_perl_xs_init(pTHX)
{
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
nginx_stash = gv_stashpv("Nginx", TRUE);
}
static void
ngx_http_perl_ctx_cleanup(void *data)
{
ngx_http_perl_ctx_t *ctx;
ctx = (ngx_http_perl_ctx_t *) data;
if (ctx->ctx != NULL) {
SvREFCNT_dec(ctx->ctx);
ctx->ctx = NULL;
}
if (ctx->r != NULL) {
SvOK_off(SvRV(ctx->r));
SvREFCNT_dec(ctx->r);
ctx->r = NULL;
}
return;
}
static ngx_int_t
ngx_http_perl_handler(ngx_http_request_t *r)
{
r->main->count++;
ngx_http_perl_handle_request(r);
return NGX_DONE;
}
static ngx_int_t
ngx_http_perl_access_handler(ngx_http_request_t *r)
{
ngx_http_perl_loc_conf_t *plcf;
ngx_http_perl_ctx_t *ctx;
ngx_pool_cleanup_t *ctxcln;
ngx_int_t rc;
dSP;
ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctxcln = ngx_pool_cleanup_add(r->pool, 0);
if (ctxcln == NULL) {
return NGX_ERROR;
}
ctxcln->data = (void *) ctx;
ctxcln->handler = ngx_http_perl_ctx_cleanup;
ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
ctx->r = sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash);
}
plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);
if (!plcf->access_sub) {
return NGX_OK;
}
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(ctx->r);
PUTBACK;
call_sv(plcf->access_sub, G_SCALAR);
SPAGAIN;
rc = POPi;
PUTBACK;
FREETMPS;
LEAVE;
return rc;
}
void
ngx_http_perl_handle_request(ngx_http_request_t *r)
{
SV *sub;
ngx_int_t rc;
ngx_str_t uri, args, *handler;
ngx_http_perl_ctx_t *ctx;
ngx_pool_cleanup_t *ctxcln;
ngx_http_perl_loc_conf_t *plcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl handler");
ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
if (ctx == NULL) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
ctxcln = ngx_pool_cleanup_add(r->pool, 0);
if (ctxcln == NULL) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
ctxcln->data = (void *) ctx;
ctxcln->handler = ngx_http_perl_ctx_cleanup;
ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
ctx->r = sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash);
}
plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);
if (plcf->read_body && !ctx->have_body &&
r->headers_in.content_length_n > 0)
{
r->request_body_in_single_buf = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
if (r->request_body_in_file_only) {
r->request_body_file_log_level = 0;
}
ctx->have_body = 1;
ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
ngx_http_finalize_request(r, NGX_DONE);
return;
}
if (ctx->next == NULL) {
sub = plcf->sub;
handler = &plcf->handler;
} else {
sub = ctx->next;
handler = &ngx_null_name;
ctx->next = NULL;
}
rc = ngx_http_perl_call_handler(aTHX_ r, sub, NULL, handler, NULL);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"perl handler done: %i", rc);
if (rc == NGX_DONE) {
ngx_http_finalize_request(r, rc);
return;
}
if (rc > 600) {
rc = NGX_OK;
}
if (ctx->redirect_uri.len) {
uri = ctx->redirect_uri;
args = ctx->redirect_args;
} else {
uri.len = 0;
}
ctx->filename.data = NULL;
ctx->redirect_uri.len = 0;
if (ctx->done || ctx->next) {
ngx_http_finalize_request(r, NGX_DONE);
return;
}
if (uri.len) {
ngx_http_internal_redirect(r, &uri, &args);
ngx_http_finalize_request(r, NGX_DONE);
return;
}
if (rc == NGX_OK || rc == NGX_HTTP_OK) {
ngx_http_send_special(r, NGX_HTTP_LAST);
ctx->done = 1;
}
ngx_http_finalize_request(r, rc);
}
void
ngx_http_perl_sleep_handler(ngx_http_request_t *r)
{
ngx_event_t *wev;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"perl sleep handler");
wev = r->connection->write;
if (wev->timedout) {
wev->timedout = 0;
ngx_http_perl_handle_request(r);
return;
}
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
static ngx_int_t
ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_perl_variable_t *pv = (ngx_http_perl_variable_t *) data;
ngx_int_t rc;
ngx_str_t value;
ngx_http_perl_ctx_t *ctx;
ngx_pool_cleanup_t *ctxcln;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"perl variable handler");
ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctxcln = ngx_pool_cleanup_add(r->pool, 0);
if (ctxcln == NULL) {
return NGX_ERROR;
}
ctxcln->data = (void *) ctx;
ctxcln->handler = ngx_http_perl_ctx_cleanup;
ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
ctx->r = sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash);
}
value.data = NULL;
rc = ngx_http_perl_call_handler(aTHX_ r, pv->sub, NULL,
&pv->handler, &value);
if (value.data) {
v->len = value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = value.data;
} else {
v->not_found = 1;
}
ctx->filename.data = NULL;
ctx->redirect_uri.len = 0;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"perl variable done");
return rc;
}
#if (NGX_HTTP_SSI)
static ngx_int_t
ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
ngx_str_t **params)
{
SV *sv, **asv;
ngx_int_t rc;
ngx_str_t *handler, **args;
ngx_uint_t i;
ngx_http_perl_ctx_t *ctx;
ngx_pool_cleanup_t *ctxcln;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"perl ssi handler");
ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctxcln = ngx_pool_cleanup_add(r->pool, 0);
if (ctxcln == NULL) {
return NGX_ERROR;
}
ctxcln->data = (void *) ctx;
ctxcln->handler = ngx_http_perl_ctx_cleanup;
ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
ctx->r = sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash);
}
ctx->ssi = ssi_ctx;
handler = params[NGX_HTTP_PERL_SSI_SUB];
handler->data[handler->len] = '\0';
#if 0
/* the code is disabled to force the precompiled perl code using only */
ngx_http_perl_eval_anon_sub(aTHX_ handler, &sv);
if (sv == &PL_sv_undef) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"eval_pv(\"%V\") failed", handler);
return NGX_ERROR;
}
if (sv == NULL) {
sv = newSVpvn((char *) handler->data, handler->len);
}
#endif
sv = newSVpvn((char *) handler->data, handler->len);
args = ¶ms[NGX_HTTP_PERL_SSI_ARG];
if (args) {
for (i = 0; args[i]; i++) { /* void */ }
asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *));
if (asv == NULL) {
SvREFCNT_dec(sv);
return NGX_ERROR;
}
asv[0] = (SV *) i;
for (i = 0; args[i]; i++) {
asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len);
}
} else {
asv = NULL;
}
rc = ngx_http_perl_call_handler(aTHX_ r, sv, asv, handler,
NULL);
SvREFCNT_dec(sv);
ctx->filename.data = NULL;
ctx->redirect_uri.len = 0;
ctx->ssi = NULL;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl ssi done");
return rc;
}
#endif
static char *
ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)
{
ngx_str_t *m;
ngx_uint_t i;
#ifdef NGX_PERL_MODULES
if (pmcf->modules == NGX_CONF_UNSET_PTR) {
pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
if (pmcf->modules == NULL) {
return NGX_CONF_ERROR;
}
m = ngx_array_push(pmcf->modules);
if (m == NULL) {
return NGX_CONF_ERROR;
}
ngx_str_set(m, NGX_PERL_MODULES);
}
#endif
if (pmcf->modules != NGX_CONF_UNSET_PTR) {
m = pmcf->modules->elts;
for (i = 0; i < pmcf->modules->nelts; i++) {
if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
if (my_perl) {
if (ngx_set_environment(cf->cycle, NULL) == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
if (nginx_stash == NULL) {
PERL_SYS_INIT(&ngx_argc, &ngx_argv);
}
my_perl = ngx_http_perl_create_interpreter(cf, pmcf);
if (my_perl == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static PerlInterpreter *
ngx_http_perl_create_interpreter(ngx_conf_t *cf,
ngx_http_perl_main_conf_t *pmcf)
{
int n;
STRLEN len;
SV *sv;
char *ver, **embedding;
ngx_str_t *m;
ngx_uint_t i;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "create perl interpreter");
if (ngx_set_environment(cf->cycle, NULL) == NULL) {
return NULL;
}
my_perl = perl_alloc();
if (my_perl == NULL) {
ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_alloc() failed");
return NULL;
}
{
perl_construct(my_perl);
PL_origalen = 1;
#ifdef PERL_EXIT_DESTRUCT_END
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
#endif
n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0;
embedding = ngx_palloc(cf->pool, (4 + n) * sizeof(char *));
if (embedding == NULL) {
goto fail;
}
embedding[0] = "";
if (n++) {
m = pmcf->modules->elts;
for (i = 0; i < pmcf->modules->nelts; i++) {
embedding[2 * i + 1] = "-I";
embedding[2 * i + 2] = (char *) m[i].data;
}
}
embedding[n++] = "-MNginx";
embedding[n++] = "-e";
embedding[n++] = "0";
n = perl_parse(my_perl, ngx_http_perl_xs_init, n, embedding, NULL);
if (n != 0) {
ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_parse() failed: %d", n);
goto fail;
}
sv = get_sv("Nginx::VERSION", FALSE);
ver = SvPV(sv, len);
if (ngx_strcmp(ver, NGINX_VERSION) != 0) {
ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
"version " NGINX_VERSION " of Nginx.pm is required, "
"but %s was found", ver);
goto fail;
}
if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) {
goto fail;
}
}
return my_perl;
fail:
(void) perl_destruct(my_perl);
perl_free(my_perl);
return NULL;
}
static ngx_int_t
ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)
{
u_char *err;
STRLEN len;
ngx_str_t *script;
ngx_uint_t i;
if (requires == NGX_CONF_UNSET_PTR) {
return NGX_OK;
}
script = requires->elts;
for (i = 0; i < requires->nelts; i++) {
require_pv((char *) script[i].data);
if (SvTRUE(ERRSV)) {
err = (u_char *) SvPV(ERRSV, len);
while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
ngx_log_error(NGX_LOG_EMERG, log, 0,
"require_pv(\"%s\") failed: \"%*s\"",
script[i].data, len + 1, err);
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, SV *sub,
SV **args, ngx_str_t *handler, ngx_str_t *rv)
{
int n, status;
char *line;
u_char *err;
STRLEN len, n_a;
ngx_uint_t i;
ngx_connection_t *c;
ngx_http_perl_ctx_t *ctx;
dSP;
ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
status = 0;
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(ctx->r);
if (args) {
EXTEND(sp, (intptr_t) args[0]);
for (i = 1; i <= (ngx_uint_t) args[0]; i++) {
PUSHs(sv_2mortal(args[i]));
}
}
PUTBACK;
c = r->connection;
n = call_sv(sub, G_EVAL);
SPAGAIN;
if (n) {
if (rv == NULL) {
status = POPi;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"call_sv: %d", status);
} else {
line = SvPVx(POPs, n_a);
rv->len = n_a;
rv->data = ngx_pnalloc(r->pool, n_a);
if (rv->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(rv->data, line, n_a);
}
}
PUTBACK;
FREETMPS;
LEAVE;
/* check $@ */
if (SvTRUE(ERRSV)) {
err = (u_char *) SvPV(ERRSV, len);
while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"call_sv(\"%V\") failed: \"%*s\"", handler, len + 1, err);
if (rv) {
return NGX_ERROR;
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (n != 1) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"call_sv(\"%V\") returned %d results", handler, n);
status = NGX_OK;
}
if (rv) {
return NGX_OK;
}
return (ngx_int_t) status;
}
static void
ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv)
{
u_char *p;
for (p = handler->data; *p; p++) {
if (*p != ' ' && *p != '\t' && *p != CR && *p != LF) {
break;
}
}
if (ngx_strncmp(p, "sub ", 4) == 0
|| ngx_strncmp(p, "sub{", 4) == 0
|| ngx_strncmp(p, "use ", 4) == 0)
{
*sv = eval_pv((char *) p, FALSE);
/* eval_pv() does not set ERRSV on failure */
return;
}
*sv = NULL;
}
static void *
ngx_http_perl_create_main_conf(ngx_conf_t *cf)
{
ngx_http_perl_main_conf_t *pmcf;
pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t));
if (pmcf == NULL) {
return NULL;
}
pmcf->modules = NGX_CONF_UNSET_PTR;
pmcf->requires = NGX_CONF_UNSET_PTR;
pmcf->init_worker = NGX_CONF_UNSET_PTR;
pmcf->exit_worker = NGX_CONF_UNSET_PTR;
return pmcf;
}
static char *
ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_http_perl_main_conf_t *pmcf = conf;
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_perl_preconfiguration(ngx_conf_t *cf)
{
#if (NGX_HTTP_SSI)
ngx_int_t rc;
ngx_http_ssi_main_conf_t *smcf;
smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
rc = ngx_hash_add_key(&smcf->commands, &ngx_http_perl_ssi_command.name,
&ngx_http_perl_ssi_command, NGX_HASH_READONLY_KEY);
if (rc != NGX_OK) {
if (rc == NGX_BUSY) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"conflicting SSI command \"%V\"",
&ngx_http_perl_ssi_command.name);
}
return NGX_ERROR;
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_perl_postconfiguration(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_perl_access_handler;
return NGX_OK;
}
#if (NGX_HTTP_SSL)
static ngx_int_t
ngx_http_perl_set_ssl(ngx_conf_t *cf, ngx_http_perl_loc_conf_t *plcf)
{
ngx_pool_cleanup_t *cln;
plcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
if (plcf->ssl == NULL) {
return NGX_ERROR;
}
plcf->ssl->log = cf->log;
if (ngx_ssl_create(plcf->ssl,
NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL)
!= NGX_OK)
{
return NGX_ERROR;
}
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_ssl_cleanup_ctx;
cln->data = plcf->ssl;
return NGX_OK;
}
#endif
static void *
ngx_http_perl_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_perl_loc_conf_t *plcf;
plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t));
if (plcf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* plcf->handler = { 0, NULL };
*/
#if (NGX_HTTP_SSL)
if (ngx_http_perl_set_ssl(cf, plcf) != NGX_OK) {
return NULL;
}
#endif
return plcf;
}
static char *
ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_perl_loc_conf_t *prev = parent;
ngx_http_perl_loc_conf_t *conf = child;
if (conf->sub == NULL) {
conf->sub = prev->sub;
conf->handler = prev->handler;
}
if (conf->access_sub == NULL) {
conf->access_sub = prev->access_sub;
conf->access_handler = prev->access_handler;
}
return NGX_CONF_OK;
}
static char *
ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_perl_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_core_loc_conf_t *clcf;
ngx_http_perl_main_conf_t *pmcf;
value = cf->args->elts;
if (plcf->handler.data) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate perl handler \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
plcf->handler = value[1];
ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub);
if (plcf->sub == &PL_sv_undef) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"eval_pv(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (plcf->sub == NULL) {
plcf->sub = newSVpvn((char *) value[1].data, value[1].len);
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_perl_handler;
return NGX_CONF_OK;
}
static char *
ngx_http_perl_app(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_perl_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_core_loc_conf_t *clcf;
ngx_http_perl_main_conf_t *pmcf;
SV *sv;
value = cf->args->elts;
if (plcf->handler.data) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate perl_handler \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
plcf->handler = value[1];
sv = newSVpvf("require '%s'", (char *) value[1].data);
plcf->sub = eval_pv(SvPVX(sv), 1);
SvREFCNT_dec(sv);
plcf->read_body = 1;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_perl_handler;
return NGX_CONF_OK;
}
static char *
ngx_http_perl_access(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_perl_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_perl_main_conf_t *pmcf;
value = cf->args->elts;
if (plcf->access_handler.data) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate perl_access handler \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
plcf->access_handler = value[1];
ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->access_sub);
if (plcf->access_sub == &PL_sv_undef) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"eval_pv(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (plcf->access_sub == NULL) {
plcf->access_sub = newSVpvn((char *) value[1].data, value[1].len);
}
return NGX_CONF_OK;
}
static char *
ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_int_t index;
ngx_str_t *value;
ngx_http_variable_t *v;
ngx_http_perl_variable_t *pv;
ngx_http_perl_main_conf_t *pmcf;
value = cf->args->elts;
if (value[1].data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
value[1].len--;
value[1].data++;
v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
if (v == NULL) {
return NGX_CONF_ERROR;
}
pv = ngx_palloc(cf->pool, sizeof(ngx_http_perl_variable_t));
if (pv == NULL) {
return NGX_CONF_ERROR;
}
index = ngx_http_get_variable_index(cf, &value[1]);
if (index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
pv->handler = value[2];
{
ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub);
if (pv->sub == &PL_sv_undef) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"eval_pv(\"%V\") failed", &value[2]);
return NGX_CONF_ERROR;
}
if (pv->sub == NULL) {
pv->sub = newSVpvn((char *) value[2].data, value[2].len);
}
}
v->get_handler = ngx_http_perl_variable;
v->data = (uintptr_t) pv;
return NGX_CONF_OK;
}
static char *
ngx_http_perl_eval(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value;
ngx_http_perl_main_conf_t *pmcf;
value = cf->args->elts;
pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
if (my_perl == NULL) {
if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
}
eval_pv((char *) value[1].data, TRUE);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_perl_init_worker(ngx_cycle_t *cycle)
{
ngx_http_perl_main_conf_t *pmcf;
pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
if (pmcf) {
/* set worker's $$ */
sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid);
}
ngx_perl_log = ngx_cycle->log;
if (pmcf) {
ngx_str_t *script;
ngx_uint_t i;
SV *cb;
dSP;
if (pmcf->init_worker == NGX_CONF_UNSET_PTR) {
return NGX_OK;
}
script = pmcf->init_worker->elts;
for (i = 0; i < pmcf->init_worker->nelts; i++) {
cb = sv_2mortal (
newSVpvn ((char *) script[i].data, script[i].len)
);
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
}
}
return NGX_OK;
}
static void
ngx_http_perl_exit_worker(ngx_cycle_t *cycle)
{
ngx_http_perl_main_conf_t *pmcf;
pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
if (pmcf) {
ngx_str_t *script;
ngx_uint_t i;
SV *cb;
dSP;
if (pmcf->exit_worker == NGX_CONF_UNSET_PTR) {
return;
}
script = pmcf->exit_worker->elts;
for (i = 0; i < pmcf->exit_worker->nelts; i++) {
cb = sv_2mortal (
newSVpvn ((char *) script[i].data, script[i].len)
);
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
}
}
return;
}
static void
ngx_http_perl_exit(ngx_cycle_t *cycle)
{
if (nginx_stash) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term");
(void) perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
}
ngx_connection_t *
ngx_perl_timer(ngx_int_t after, SV *repeat, SV *cb)
{
ngx_connection_t *c;
ngx_perl_timer_t *t;
c = ngx_get_connection((ngx_socket_t) 0, ngx_perl_log);
if (c == NULL) {
return NULL;
}
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
c->data = NULL;
Newz(0, t, 1, ngx_perl_timer_t);
if (t == NULL) {
ngx_perl_timer_clear(c);
return NULL;
}
c->read->handler = ngx_perl_timer_callback;
c->read->active = 1;
c->read->log = c->log;
c->data = (void *) t;
t->after = after;
t->repeat = repeat;
t->cb = cb;
SvREFCNT_inc(t->repeat);
SvREFCNT_inc(t->cb);
ngx_add_timer(c->read, t->after * 1000);
return c;
}
void
ngx_perl_timer_clear(ngx_connection_t *c)
{
ngx_perl_timer_t *t;
if (c->destroyed) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"ngx_perl_timer_clear: connection already destroyed");
return;
}
if (c->data) {
t = (ngx_perl_timer_t *) c->data;
if (t->repeat) {
SvREFCNT_dec(t->repeat);
t->repeat = NULL;
}
if (t->cb) {
SvREFCNT_dec(t->cb);
t->cb = NULL;
}
safefree(t);
}
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
c->destroyed = 1;
ngx_free_connection(c);
return;
}
static void
ngx_perl_timer_callback(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_perl_timer_t *t;
SV *cb;
dSP;
if (ev->timer_set) {
ngx_del_timer(ev);
}
c = (ngx_connection_t *) ev->data;
t = (ngx_perl_timer_t *) c->data;
cb = t->cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(PTR2IV(c))));
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
if (!c->destroyed && SvOK(t->repeat) && SvIV(t->repeat) > 0) {
ngx_add_timer(ev, SvIV(t->repeat) * 1000);
} else {
ngx_perl_timer_clear(c);
}
return;
}
void
ngx_perl_resolver(SV *name, SV *timeout, SV *cb)
{
ngx_http_core_loc_conf_t *clcf;
ngx_perl_resolver_t *pr;
ngx_resolver_ctx_t *ctx, temp;
ngx_event_t *ev;
dSP;
clcf = ngx_http_cycle_get_module_loc_conf(ngx_cycle,
ngx_http_core_module);
errno = 0;
Newz(0, pr, 1, ngx_perl_resolver_t);
if (pr == NULL) {
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
Newz(0, pr->name.data, SvCUR(name), u_char);
if (pr->name.data == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_perl_log, 0,
"ngx_perl_resolver: "
"Newz failed");
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
ngx_memcpy(pr->name.data, SvPV_nolen(name), SvCUR(name));
pr->name.len = SvCUR(name);
temp.name = pr->name;
pr->cb = cb;
SvREFCNT_inc(cb);
ctx = ngx_resolve_start(clcf->resolver, &temp);
if (ctx == NULL) {
errno = NGX_PERL_EBADE;
goto FATAL;
}
if (ctx == NGX_NO_RESOLVER) {
ngx_log_error(NGX_LOG_ERR, ngx_perl_log, 0,
"ngx_perl_resolver: "
"no resolver defined to resolve %V",
&temp.name);
errno = NGX_PERL_EBADE;
goto FATAL;
}
ctx->name = pr->name;
ctx->type = NGX_RESOLVE_A;
ctx->handler = ngx_perl_resolver_handler;
ctx->data = pr;
ctx->timeout = clcf->resolver_timeout;
if (clcf->resolver_timeout == NGX_CONF_UNSET_MSEC) {
ctx->timeout = 30000;
}
/* timer */
Newz(0, ev, 1, ngx_event_t);
if (ev == NULL) {
ngx_resolve_name_done(ctx);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
ev->data = ctx;
ev->handler = ngx_perl_resolver_timeout_handler;
pr->ev = ev;
ngx_add_timer ( ev, SvOK (timeout) && SvIV (timeout) > 0
? SvIV (timeout) * 1000
: 15000 );
if (ngx_resolve_name(ctx) != NGX_OK) {
errno = NGX_PERL_EBADE;
goto FATAL;
}
return;
FATAL:
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if (pr) {
if (pr->name.data) {
safefree(pr->name.data);
pr->name.data = NULL;
}
if (pr->cb) {
SvREFCNT_dec(pr->cb);
pr->cb = NULL;
}
if (pr->ev) {
if (pr->ev->timer_set) {
ngx_del_timer(pr->ev);
}
safefree(pr->ev);
pr->ev = NULL;
}
safefree(pr);
}
return;
}
static void
ngx_perl_resolver_timeout_handler(ngx_event_t *ev)
{
ngx_resolver_ctx_t *ctx;
ctx = (ngx_resolver_ctx_t *) ev->data;
if (ev->timer_set) {
ngx_del_timer(ev);
}
ngx_perl_resolver_handler(ctx);
return;
}
static void
ngx_perl_resolver_handler(ngx_resolver_ctx_t *ctx)
{
ngx_perl_resolver_t *pr;
in_addr_t addr;
ngx_uint_t i;
SV *cb;
dSP;
pr = (ngx_perl_resolver_t *) ctx->data;
if (pr->ev->timer_set) {
ngx_del_timer(pr->ev);
}
errno = 0;
cb = pr->cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
if (ctx->state) {
ngx_log_error(NGX_LOG_ERR, ngx_perl_log, 0,
"ngx_perl_resolver: "
"\"%V\" could not be resolved (%i: %s)",
&ctx->name, ctx->state,
ngx_resolver_strerror(ctx->state));
if (ctx->state == NGX_RESOLVE_TIMEDOUT) {
errno = NGX_PERL_ETIMEDOUT;
} else if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
errno = NGX_PERL_ENOMSG;
} else {
errno = NGX_PERL_EAGAIN;
}
XPUSHs(newSViv(ctx->state));
XPUSHs(newSVpvf("%s", ngx_resolver_strerror(ctx->state)));
} else {
EXTEND(SP, ctx->naddrs);
for (i = 0; i < ctx->naddrs; i++) {
addr = ctx->addrs[i];
PUSHs(newSVpvf("%u.%u.%u.%u",
(ntohl(addr) >> 24) & 0xff,
(ntohl(addr) >> 16) & 0xff,
(ntohl(addr) >> 8) & 0xff,
ntohl(addr) & 0xff));
}
}
ngx_resolve_name_done(ctx);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if (pr) {
if (pr->name.data) {
safefree(pr->name.data);
pr->name.data = NULL;
}
if (pr->cb) {
SvREFCNT_dec(pr->cb);
pr->cb = NULL;
}
if (pr->ev) {
if (pr->ev->timer_set) {
ngx_del_timer(pr->ev);
}
safefree(pr->ev);
pr->ev = NULL;
}
safefree(pr);
}
return;
}
ngx_int_t
ngx_perl_connection_init(ngx_connection_t *c)
{
ngx_pool_t *pool;
ngx_perl_connection_t *plc;
ngx_pool_cleanup_t *plccln;
errno = 0;
pool = c->pool;
plc = ngx_pcalloc(pool, sizeof(ngx_perl_connection_t));
if (plc == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connection_init: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
return 0;
}
plccln = ngx_pool_cleanup_add(pool, 0);
if (plccln == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connection_init: ngx_pool_cleanup_add() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
return 0;
}
plccln->data = (void *) plc;
plccln->handler = ngx_perl_connection_cleanup;
c->data = (void *) plc;
c->write->handler = ngx_perl_dummy_handler;
c->read->handler = ngx_perl_dummy_handler;
return 1;
}
ngx_connection_t *
ngx_perl_connector(SV *address, SV *port, SV *timeout, SV *cb)
{
in_addr_t inaddr;
in_port_t inport;
ngx_pool_t *pool;
struct sockaddr_in *sin;
ngx_peer_connection_t *peer;
ngx_perl_connection_t *plc;
ngx_pool_cleanup_t *plccln;
ngx_connection_t *c;
ngx_int_t rc;
dSP;
errno = 0;
if (!SvOK(address) || !SvOK(port) || !SvOK(timeout)) {
ngx_log_error(NGX_LOG_ERR, ngx_perl_log, 0,
"ngx_perl_connector: incorrect argument(s)");
errno = NGX_PERL_EINVAL;
goto FATAL;
}
inport = (in_port_t) SvIV(port);
inaddr = ngx_inet_addr((u_char *) SvPV_nolen(address), SvCUR(address));
if (inaddr == INADDR_NONE) {
ngx_log_error(NGX_LOG_ERR, ngx_perl_log, 0,
"ngx_perl_connector: incorrect address");
errno = NGX_PERL_EINVAL;
goto FATAL;
}
pool = ngx_create_pool(1024, ngx_perl_log);
if (pool == NULL) {
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
if (sin == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connector: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inaddr;
sin->sin_port = htons(inport);
plc = ngx_pcalloc(pool, sizeof(ngx_perl_connection_t));
if (plc == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connector: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
plccln = ngx_pool_cleanup_add(pool, 0);
if (plccln == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connector: ngx_pool_cleanup_add() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
plccln->data = (void *) plc;
plccln->handler = ngx_perl_connection_cleanup;
peer = ngx_pcalloc(pool, sizeof(ngx_peer_connection_t));
if (peer == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connector: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
peer->sockaddr = (struct sockaddr *) sin;
peer->socklen = sizeof(struct sockaddr_in);
peer->get = ngx_event_get_peer;
peer->log = pool->log;
peer->log_error = NGX_ERROR_ERR;
peer->name = ngx_pcalloc(pool, sizeof(ngx_str_t));
if (peer->name == NULL) {
ngx_log_error(NGX_LOG_ERR, peer->log, 0,
"ngx_perl_connector: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
peer->name->data = ngx_pcalloc(pool, SvCUR(address));
if (peer->name->data == NULL) {
ngx_log_error(NGX_LOG_ERR, peer->log, 0,
"ngx_perl_connector: ngx_pcalloc() failed");
ngx_destroy_pool(pool);
errno = NGX_PERL_ENOMEM;
goto FATAL;
}
ngx_memcpy(peer->name->data, SvPV_nolen(address), SvCUR(address));
peer->name->len = SvCUR(address);
rc = ngx_event_connect_peer(peer);
if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_ERR, peer->log, 0,
"ngx_perl_connector: ngx_event_connect_peer() failed");
if (peer->connection)
ngx_close_connection(peer->connection);
ngx_destroy_pool(pool);
errno = NGX_PERL_EBADF;
goto FATAL;
}
c = peer->connection;
if (c == NULL) {
ngx_log_error(NGX_LOG_ERR, pool->log, 0,
"ngx_perl_connector: no peer->connection");
ngx_destroy_pool(pool);
errno = NGX_PERL_EBADF;
goto FATAL;
}
c->pool = pool;
c->log = pool->log;
c->data = (void *) plc;
plc->connect_cb = cb;
SvREFCNT_inc(cb);
c->write->handler = ngx_perl_connect_handler;
c->read->handler = ngx_perl_connect_handler;
ngx_add_timer(c->write, SvOK(timeout) && SvIV(timeout)
? SvIV(timeout) * 1000 : 15000);
if (rc == NGX_OK) {
c->write->handler(c->write);
return c;
}
return c;
FATAL:
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
return NULL;
}
void
ngx_perl_reader(ngx_connection_t *c, SV *buf, SV *min, SV *max,
SV *timeout, SV *cb)
{
ngx_perl_connection_t *plc;
plc = (ngx_perl_connection_t *) c->data;
if (plc->read_min) {
SvREFCNT_dec(plc->read_min);
plc->read_min = NULL;
}
if (plc->read_max) {
SvREFCNT_dec(plc->read_max);
plc->read_max = NULL;
}
if (plc->read_timeout) {
SvREFCNT_dec(plc->read_timeout);
plc->read_timeout = NULL;
}
if (plc->read_buffer) {
SvREFCNT_dec(plc->read_buffer);
plc->read_buffer = NULL;
}
if (plc->read_cb) {
SvREFCNT_dec(plc->read_cb);
plc->read_cb = NULL;
}
plc->read_min = min;
plc->read_max = max;
plc->read_buffer = buf;
plc->read_timeout = timeout;
plc->read_cb = cb;
SvREFCNT_inc(min);
SvREFCNT_inc(max);
SvREFCNT_inc(buf);
SvREFCNT_inc(timeout);
SvREFCNT_inc(cb);
return;
}
void
ngx_perl_writer(ngx_connection_t *c, SV *buf, SV *timeout, SV *cb)
{
ngx_perl_connection_t *plc;
plc = (ngx_perl_connection_t *) c->data;
if (plc->write_timeout) {
SvREFCNT_dec(plc->write_timeout);
plc->write_timeout = NULL;
}
if (plc->write_buffer) {
SvREFCNT_dec(plc->write_buffer);
plc->write_buffer = NULL;
}
if (plc->write_cb) {
SvREFCNT_dec(plc->write_cb);
plc->write_cb = NULL;
}
plc->write_buffer = buf;
plc->write_timeout = timeout;
plc->write_cb = cb;
SvREFCNT_inc(buf);
SvREFCNT_inc(timeout);
SvREFCNT_inc(cb);
return;
}
void
ngx_perl_ssl_handshaker(ngx_connection_t *c, SV *timeout, SV *cb)
{
#if (NGX_HTTP_SSL)
ngx_perl_connection_t *plc;
ngx_http_perl_loc_conf_t *plcf;
plc = (ngx_perl_connection_t *) c->data;
if (plc->ssl_handshake_timeout) {
SvREFCNT_dec(plc->ssl_handshake_timeout);
plc->ssl_handshake_timeout = NULL;
}
if (plc->ssl_handshake_cb) {
SvREFCNT_dec(plc->ssl_handshake_cb);
plc->ssl_handshake_cb = NULL;
}
plc->ssl = 1;
plc->ssl_handshake_cb = cb;
plc->ssl_handshake_timeout = timeout;
SvREFCNT_inc(cb);
SvREFCNT_inc(timeout);
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (!c->write->timer_set) {
if (plc->ssl_handshake_timeout != NULL &&
SvOK (plc->ssl_handshake_timeout) &&
SvIV (plc->ssl_handshake_timeout) >= 0)
{
ngx_add_timer(c->write, SvIV(plc->ssl_handshake_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_ssl_handshaker: "
"incorrent timeout, using 15 s instead");
ngx_add_timer(c->write, 15000);
}
}
plcf = ngx_http_cycle_get_module_loc_conf(ngx_cycle, ngx_http_perl_module);
if ( ngx_ssl_create_connection
( plcf->ssl,
c,
NGX_SSL_BUFFER|NGX_SSL_CLIENT ) != NGX_OK )
{
c->error = 1;
ngx_perl_ssl_handshake_handler(c);
return;
}
c->sendfile = 0;
c->log->action = "SSL handshaking with peer";
return;
#else
dSP;
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_ssl_handshake: "
"SSL support is required to use ngx_ssl_handshaker()");
errno = NGX_PERL_ENOTSUP;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD);
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
return;
#endif
}
static void
ngx_perl_connection_cleanup(void *data)
{
ngx_perl_connection_t *plc;
plc = (ngx_perl_connection_t *) data;
if (plc->connect_cb) {
SvREFCNT_dec(plc->connect_cb);
plc->connect_cb = NULL;
}
if (plc->read_min) {
SvREFCNT_dec(plc->read_min);
plc->read_min = NULL;
}
if (plc->read_max) {
SvREFCNT_dec(plc->read_max);
plc->read_max = NULL;
}
if (plc->read_timeout) {
SvREFCNT_dec(plc->read_timeout);
plc->read_timeout = NULL;
}
if (plc->read_buffer) {
SvREFCNT_dec(plc->read_buffer);
plc->read_buffer = NULL;
}
if (plc->read_cb) {
SvREFCNT_dec(plc->read_cb);
plc->read_cb = NULL;
}
if (plc->write_timeout) {
SvREFCNT_dec(plc->write_timeout);
plc->write_timeout = NULL;
}
if (plc->write_buffer) {
SvREFCNT_dec(plc->write_buffer);
plc->write_buffer = NULL;
}
if (plc->write_cb) {
SvREFCNT_dec(plc->write_cb);
plc->write_cb = NULL;
}
#if (NGX_HTTP_SSL)
if (plc->ssl_handshake_timeout) {
SvREFCNT_dec(plc->ssl_handshake_timeout);
plc->ssl_handshake_timeout = NULL;
}
if (plc->ssl_handshake_cb) {
SvREFCNT_dec(plc->ssl_handshake_cb);
plc->ssl_handshake_cb = NULL;
}
#endif
return;
}
void
ngx_perl_close(ngx_connection_t *c)
{
ngx_pool_t *pool;
if (c->destroyed) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_close: "
"connection already destroyed");
return;
}
#if (NGX_HTTP_SSL)
/* XXX taken from ngx_http_upstream.c */
/* TODO: do not shutdown persistent connection */
if (c->ssl) {
/*
* We send the "close notify" shutdown alert to the upstream only
* and do not wait its "close notify" shutdown alert.
* It is acceptable according to the TLS standard.
*/
c->ssl->no_wait_shutdown = 1;
(void) ngx_ssl_shutdown(c);
}
#endif
pool = c->pool;
ngx_close_connection(c);
ngx_destroy_pool(pool);
return;
}
void
ngx_perl_read(ngx_connection_t *c)
{
ngx_perl_connection_t *plc;
plc = (ngx_perl_connection_t *) c->data;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
c->read->handler = ngx_perl_read_handler;
c->write->handler = ngx_perl_dummy_handler;
if (c->read->ready) {
c->read->handler(c->read);
return;
}
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
if (c->read->error == 0)
c->read->error = 1;
c->read->handler(c->read);
return;
}
if (!c->read->timer_set) {
if (plc->read_timeout != NULL &&
SvOK (plc->read_timeout) &&
SvIV (plc->read_timeout) >= 0)
{
ngx_add_timer(c->read, SvIV(plc->read_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_read: "
"incorrent read timeout, using 15 s instead");
ngx_add_timer(c->read, 15000);
}
}
return;
}
void
ngx_perl_write(ngx_connection_t *c)
{
ngx_perl_connection_t *plc;
plc = (ngx_perl_connection_t *) c->data;
plc->write_offset = 0;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
c->read->handler = ngx_perl_dummy_handler;
c->write->handler = ngx_perl_write_handler;
if (c->write->ready) {
ngx_post_event(c->write, &ngx_posted_events);
return;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
if (c->write->error == 0)
c->write->error = 1;
c->write->handler(c->write);
return;
}
if (!c->write->timer_set) {
if (plc->write_timeout != NULL &&
SvOK (plc->write_timeout) &&
SvIV (plc->write_timeout) >= 0)
{
ngx_add_timer(c->write, SvIV(plc->write_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_write: "
"incorrent write timeout, using 15 s instead");
ngx_add_timer(c->write, 15000);
}
}
return;
}
#if (NGX_HTTP_SSL)
void
ngx_perl_ssl_handshake(ngx_connection_t *c)
{
ngx_perl_connection_t *plc;
ngx_int_t rc;
plc = (ngx_perl_connection_t *) c->data;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (!c->write->timer_set) {
if (plc->ssl_handshake_timeout != NULL &&
SvOK (plc->ssl_handshake_timeout) &&
SvIV (plc->ssl_handshake_timeout) >= 0)
{
ngx_add_timer(c->write, SvIV(plc->ssl_handshake_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_ssl_handshaker: "
"incorrent timeout, using 15 s instead");
ngx_add_timer(c->write, 15000);
}
}
rc = ngx_ssl_handshake(c);
if (rc == NGX_AGAIN) {
c->ssl->handler = ngx_perl_ssl_handshake_handler;
return;
}
ngx_perl_ssl_handshake_handler(c);
return;
}
#endif
void
ngx_perl_noop(ngx_connection_t *c)
{
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
c->read->handler = ngx_perl_dummy_handler;
c->write->handler = ngx_perl_dummy_handler;
return;
}
static void
ngx_perl_dummy_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
c = (ngx_connection_t *) ev->data;
ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,
"ngx_perl_dummy_handler called");
return;
}
static void
ngx_perl_connect_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_perl_connection_t *plc;
ngx_int_t cmd, count;
SV *cb;
dSP;
c = (ngx_connection_t *) ev->data;
plc = (ngx_perl_connection_t *) c->data;
errno = 0;
c->read->handler = ngx_perl_dummy_handler;
c->write->handler = ngx_perl_dummy_handler;
if (ev->timer_set) {
ngx_del_timer(ev);
}
if (ev->timedout) {
errno = NGX_PERL_ETIMEDOUT;
goto CALLBACK;
}
if (ev->error || c->error) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
errno = 0;
CALLBACK:
cb = plc->connect_cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(PTR2IV(c))));
PUTBACK;
count = call_sv(cb, G_SCALAR);
if (count != 1) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_connect_handler: "
"call_sv returned wrong count = %i",
count);
}
SPAGAIN;
cmd = POPi;
PUTBACK;
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if ((ev->error || c->error || ev->timedout) && cmd != NGX_PERL_CLOSE) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_connect_handler: "
"NGX_CLOSE required on error and timeout here, forcing");
ngx_perl_close(c);
return;
}
switch (cmd) {
case NGX_PERL_CLOSE:
ngx_perl_close(c);
break;
case NGX_PERL_READ:
ngx_perl_read(c);
break;
case NGX_PERL_WRITE:
ngx_perl_write(c);
break;
case NGX_PERL_SSL_HANDSHAKE:
#if (NGX_HTTP_SSL)
ngx_perl_ssl_handshake(c);
#else
ngx_perl_close(c);
#endif
break;
case NGX_PERL_NOOP:
ngx_perl_noop(c);
break;
}
return;
}
static void
ngx_perl_read_handler(ngx_event_t *ev)
{
ssize_t n;
ngx_connection_t *c;
ngx_perl_connection_t *plc;
SV *sv, *cb;
U32 min, max;
ngx_int_t cmd, count;
dSP;
c = (ngx_connection_t *) ev->data;
plc = (ngx_perl_connection_t *) c->data;
errno = 0;
if (ev->timer_set) {
ngx_del_timer(ev);
}
if (ev->timedout) {
errno = NGX_PERL_ETIMEDOUT;
goto CALLBACK;
}
AGAIN:
if (ev->error || c->error) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
min = 0;
max = 0;
if ( SvOK ( plc->read_min ) &&
SvIV ( plc->read_min ) )
min = SvIV ( plc->read_min );
if ( SvOK ( plc->read_max ) &&
SvIV ( plc->read_max ) )
max = SvIV ( plc->read_max );
sv = plc->read_buffer;
if (SvTYPE (sv) != SVt_PV) {
SvUPGRADE (sv, SVt_PV);
SvPOK_on (sv);
}
for ( ;; ) {
if ( SvLEN(sv) - SvCUR(sv) < 1500) {
if ( max ) {
if ( SvLEN(sv) < max + 1 ) {
SvGROW(sv, ( SvCUR(sv) * 2 ) + 1500);
} else {
if ( SvCUR(sv) >= max ) {
errno = NGX_PERL_ENOMEM;
goto CALLBACK;
}
}
} else {
SvGROW(sv, ( SvCUR(sv) * 2 ) + 1500);
}
}
ngx_socket_errno = 0;
n = c->recv(c, (u_char *) SvPVX (sv) + SvCUR (sv),
( max && SvLEN (sv) > max
? max : SvLEN (sv) - 1 ) - SvCUR (sv));
if (n == NGX_AGAIN) {
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
if (!c->read->timer_set) {
if (plc->read_timeout != NULL &&
SvOK (plc->read_timeout) &&
SvIV (plc->read_timeout) >= 0)
{
ngx_add_timer(c->read, SvIV(plc->read_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_read_handler: "
"incorrent read timeout, using 15 s instead");
ngx_add_timer(c->read, 15000);
}
}
return;
}
if (n == 0) {
errno = NGX_PERL_EOF;
goto CALLBACK;
}
if (n == NGX_ERROR) {
errno = ngx_socket_errno ? ngx_socket_errno : NGX_PERL_EBADE;
goto CALLBACK;
}
SvCUR_set (sv, SvCUR (sv) + n);
if ( SvCUR (sv) < min ) {
continue;
}
break;
}
errno = 0;
CALLBACK:
cb = plc->read_cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(PTR2IV(c))));
PUTBACK;
ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,
"ngx_perl_read_handler: "
"ev->eof = %i, ev->error = %i, c->error = %i",
ev->eof, ev->error, c->error);
count = call_sv(cb, G_SCALAR);
if (count != 1) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_read_handler: "
"call_sv returned wrong count = %i",
count);
}
SPAGAIN;
cmd = POPi;
PUTBACK;
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if ((ev->error || c->error) && cmd != NGX_PERL_CLOSE) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_read_handler: "
"NGX_CLOSE required on error here, forcing");
ngx_perl_close(c);
return;
}
switch (cmd) {
case NGX_PERL_CLOSE:
ngx_perl_close(c);
break;
case NGX_PERL_READ:
if (c->read->ready) {
goto AGAIN;
} else {
ngx_perl_read(c);
}
break;
case NGX_PERL_WRITE:
ngx_perl_write(c);
break;
case NGX_PERL_SSL_HANDSHAKE:
#if (NGX_HTTP_SSL)
ngx_perl_ssl_handshake(c);
#else
ngx_perl_close(c);
#endif
break;
case NGX_PERL_NOOP:
ngx_perl_noop(c);
break;
}
return;
}
static void
ngx_perl_write_handler(ngx_event_t *ev)
{
ssize_t n;
ngx_connection_t *c;
ngx_perl_connection_t *plc;
SV *sv, *cb;
ngx_int_t cmd, count;
dSP;
c = (ngx_connection_t *) ev->data;
plc = (ngx_perl_connection_t *) c->data;
errno = 0;
if (ev->timer_set) {
ngx_del_timer(ev);
}
if (ev->timedout) {
errno = NGX_PERL_ETIMEDOUT;
goto CALLBACK;
}
AGAIN:
if (ev->error || c->error) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
sv = plc->write_buffer;
if (SvTYPE (sv) != SVt_PV || SvCUR (sv) <= 0) {
errno = 0;
goto CALLBACK;
}
for ( ;; ) {
ngx_socket_errno = 0;
n = c->send(c, (u_char *) SvPV_nolen (sv) + plc->write_offset,
SvCUR (sv) - plc->write_offset);
if (n == NGX_AGAIN) {
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
if (!c->write->timer_set) {
if (plc->write_timeout != NULL &&
SvOK (plc->write_timeout) &&
SvIV (plc->write_timeout) >= 0)
{
ngx_add_timer(c->write, SvIV(plc->write_timeout) * 1000);
} else {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_write_handler: "
"incorrent write timeout, using 15 s instead");
ngx_add_timer(c->write, 15000);
}
}
return;
}
if (n == 0) {
errno = NGX_PERL_EOF;
goto CALLBACK;
}
if (n == NGX_ERROR) {
errno = ngx_socket_errno ? ngx_socket_errno : NGX_PERL_EBADE;
goto CALLBACK;
}
plc->write_offset += n;
if (SvCUR(sv) - plc->write_offset > 0) {
continue;
}
break;
}
errno = 0;
CALLBACK:
plc->write_offset = 0;
cb = plc->write_cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(PTR2IV(c))));
PUTBACK;
count = call_sv(cb, G_SCALAR);
if (count != 1) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_write_handler: "
"call_sv returned wrong count = %i",
count);
}
SPAGAIN;
cmd = POPi;
PUTBACK;
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if ((ev->error || c->error) && cmd != NGX_PERL_CLOSE) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_write_handler: "
"NGX_CLOSE required on error here, forcing");
ngx_perl_close(c);
return;
}
switch (cmd) {
case NGX_PERL_CLOSE:
ngx_perl_close(c);
break;
case NGX_PERL_READ:
ngx_perl_read(c);
break;
case NGX_PERL_WRITE:
if (c->write->ready) {
goto AGAIN;
} else {
ngx_perl_write(c);
}
break;
case NGX_PERL_SSL_HANDSHAKE:
#if (NGX_HTTP_SSL)
ngx_perl_ssl_handshake(c);
#else
ngx_perl_close(c);
#endif
break;
case NGX_PERL_NOOP:
ngx_perl_noop(c);
break;
}
return;
}
#if (NGX_HTTP_SSL)
static void
ngx_perl_ssl_handshake_handler(ngx_connection_t *c)
{
ngx_perl_connection_t *plc;
SV *cb;
ngx_int_t cmd, count;
dSP;
plc = (ngx_perl_connection_t *) c->data;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (c->write->timedout) {
errno = NGX_PERL_ETIMEDOUT;
goto CALLBACK;
}
if (c->error) {
errno = NGX_PERL_EBADE;
goto CALLBACK;
}
if (c->ssl->handshaked) {
errno = 0;
goto CALLBACK;
}
c->error = 1;
errno = NGX_PERL_EBADE;
CALLBACK:
cb = plc->ssl_handshake_cb;
SvREFCNT_inc(cb);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(PTR2IV(c))));
PUTBACK;
count = call_sv(cb, G_SCALAR);
if (count != 1) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_ssl_handshake_handler: "
"call_sv returned wrong count = %i",
count);
}
SPAGAIN;
cmd = POPi;
PUTBACK;
FREETMPS;
LEAVE;
SvREFCNT_dec(cb);
errno = 0;
if ((c->error) && cmd != NGX_PERL_CLOSE) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"ngx_perl_ssl_handshake_handler: "
"NGX_CLOSE required on error here, forcing");
ngx_perl_close(c);
return;
}
switch (cmd) {
case NGX_PERL_CLOSE:
ngx_perl_close(c);
break;
case NGX_PERL_READ:
ngx_perl_read(c);
break;
case NGX_PERL_WRITE:
ngx_perl_write(c);
break;
case NGX_PERL_SSL_HANDSHAKE:
ngx_perl_ssl_handshake(c);
break;
case NGX_PERL_NOOP:
ngx_perl_noop(c);
break;
}
return;
}
#endif