Hi
Recently there was some discussion of mod_scgi doing communication over named
(unix domain) sockets.
For those of you still interested in mod_scgi doing that here is a modified
apache1 mod_scgi which does that.
The directive is SCGIServerUnix <path> in the apache config file.
What happens if you have both SCGIServer and SCGIServerUnix at once
I'm not exactly sure and my apache knowledge is limited, as is my time!
with the scripts I posted yesterday it seems to work quite well, but
take care on the permissioning on the named socket or you will
probably get permission denied errors .....
Jon
ps I've cc'd a few of who seemed to be interested in this, judging by the
original mails.
#define MOD_SCGI_VERSION "1.4"
#define SCGI_PROTOCOL_VERSION "1"
/* #define VERBOSE_DEBUG */
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"
#include <netinet/tcp.h> /* for TCP_NODELAY */
#include <sys/un.h> /*sockaddr_un*/
#define UNSET 0
#define ENABLED 1
#define DISABLED 2
/*
* Configuration record. Used per-directory configuration data.
*/
typedef struct {
unsigned long addr; /* address in network byte order */
unsigned short port; /* port in network byte order */
int enabled; /* mod_scgi enabled from this directory */
char* socket_filename;
int unix_socket;
} scgi_cfg;
/*
* Declare ourselves so the configuration routines can find and know us.
* We'll fill it in at the end of the module.
*/
module MODULE_VAR_EXPORT scgi_module;
/*
* Locate our directory configuration record for the current request.
*/
static scgi_cfg *our_dconfig(request_rec *r)
{
return (scgi_cfg *) ap_get_module_config(r->per_dir_config, &scgi_module);
}
static int scgi_trans(request_rec *r)
{
scgi_cfg *cfg = our_dconfig(r);
if (cfg->enabled == ENABLED) {
r->handler = "scgi-handler";
return OK;
}
return DECLINED;
}
int open_socket_unix(request_rec *r, scgi_cfg *cfg){
int retries, sleeptime, rv;
struct sockaddr_un addr;
int sock;
if (cfg->socket_filename == NULL)
strcpy((char*)&addr.sun_path,"/tmp/scgi.socket");
else
strcpy((char*)&addr.sun_path,cfg->socket_filename);
addr.sun_family = AF_UNIX;
/* try to connect */
retries = 4;
sleeptime = 1;
restart:
/* create the socket */
sock = ap_psocket(r->pool, AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
return -1;
rv = connect(sock, (struct sockaddr *)&addr, sizeof addr);
if (rv != 0) {
ap_pclosesocket(r->pool, sock);
if (errno == EACCES) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"scgi: permission denied via unix socket");
return -1;
}
if (errno == EINTR)
goto restart; /* signals suck */
if (errno == ECONNREFUSED && retries > 0) {
/* server may be temporarily down, retry */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
"scgi: connection refused, retrying");
sleep(sleeptime);
--retries;
sleeptime *= 2;
goto restart;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"scgi: connecting to server via unix socket");
return -1;
}
return sock;
}
int open_socket_inet(request_rec *r, scgi_cfg *cfg)
{
int retries, sleeptime, rv;
struct sockaddr_in addr;
int sock;
if (cfg->addr == UNSET)
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
else
addr.sin_addr.s_addr = cfg->addr;
if (cfg->port == UNSET)
addr.sin_port = htons(4000);
else
addr.sin_port = cfg->port;
addr.sin_family = AF_INET;
/* try to connect */
retries = 4;
sleeptime = 1;
restart:
/* create the socket */
sock = ap_psocket(r->pool, AF_INET, SOCK_STREAM, 0);
if (sock == -1)
return -1;
rv = connect(sock, (struct sockaddr *)&addr, sizeof addr);
if (rv != 0) {
ap_pclosesocket(r->pool, sock);
if (errno == EINTR)
goto restart; /* signals suck */
if (errno == ECONNREFUSED && retries > 0) {
/* server may be temporarily down, retry */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
"scgi: connection refused, retrying");
sleep(sleeptime);
--retries;
sleeptime *= 2;
goto restart;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"scgi: connecting to server");
return -1;
}
#ifdef TCP_NODELAY
if (addr.sin_family == AF_INET) {
/* disable Nagle */
int set = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set));
}
#endif
return sock;
}
static char *lookup_name(table *t, const char *name)
{
array_header *hdrs_arr;
table_entry *hdrs;
int i;
hdrs_arr = ap_table_elts(t);
hdrs = (table_entry *)hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (hdrs[i].key == NULL) {
continue;
}
if (strcasecmp(hdrs[i].key, name) == 0) {
return hdrs[i].val;
}
}
return NULL;
}
static char *lookup_header(request_rec *r, const char *name)
{
return lookup_name(r->headers_in, name);
}
static char *lookup_env(request_rec *r, const char *name)
{
return lookup_name(r->subprocess_env, name);
}
static char *original_uri(request_rec *r)
{
char *first, *last;
if (r->the_request == NULL) {
return (char *) ap_pcalloc(r->pool, 1);
}
first = r->the_request; /* use the request-line */
while (*first && !ap_isspace(*first)) {
++first; /* skip over the method */
}
while (ap_isspace(*first)) {
++first; /* and the space(s) */
}
last = first;
while (*last && !ap_isspace(*last)) {
++last; /* end at next whitespace */
}
return ap_pstrndup(r->pool, first, last - first);
}
static void log_err(request_rec *r, const char *msg)
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"scgi: %s", msg);
}
static void log_debug(request_rec *r, const char *msg)
{
#ifdef VERBOSE_DEBUG
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
"scgi: %s", msg);
#endif
}
static void add_header(table *t, const char *name, const char *value)
{
if (name != NULL && value != NULL) {
ap_table_addn(t, name, value);
}
}
static int send_headers(request_rec *r, BUFF *f, scgi_cfg *cfg)
{
table *t;
array_header *hdrs_arr, *env_arr;
table_entry *hdrs, *env;
int i;
unsigned long n;
log_debug(r, "sending headers");
t = ap_make_table(r->pool, 40); /* headers to send */
if (!t)
return 0;
/* CONTENT_LENGTH must come first and always be present */
add_header(t, "CONTENT_LENGTH",
ap_psprintf(r->pool, "%ld", r->remaining));
add_header(t, "SCGI", SCGI_PROTOCOL_VERSION);
add_header(t, "SERVER_SOFTWARE", ap_get_server_version());
add_header(t, "SERVER_PROTOCOL", r->protocol);
add_header(t, "SERVER_NAME", ap_get_server_name(r));
add_header(t, "SERVER_ADMIN", r->server->server_admin);
add_header(t, "SERVER_ADDR", r->connection->local_ip);
add_header(t, "SERVER_PORT",
ap_psprintf(r->pool, "%u", ap_get_server_port(r)));
add_header(t, "REMOTE_ADDR", r->connection->remote_ip);
add_header(t, "REMOTE_PORT",
ap_psprintf(r->pool, "%d",
ntohs(r->connection->remote_addr.sin_port)));
add_header(t, "REMOTE_USER", r->connection->user);
add_header(t, "REQUEST_METHOD", r->method);
add_header(t, "REQUEST_URI", original_uri(r));
add_header(t, "QUERY_STRING", r->args ? r->args : "");
add_header(t, "SCRIPT_NAME", r->uri);
/* skip PATH_INFO, it's always empty */
add_header(t, "HTTPS", lookup_env(r, "HTTPS"));
add_header(t, "CONTENT_TYPE", lookup_header(r, "Content-type"));
add_header(t, "DOCUMENT_ROOT", ap_document_root(r));
add_header(t, "HTTP_ACCEPT", lookup_header(r, "Accept"));
add_header(t, "HTTP_ACCEPT_CHARSET",
lookup_header(r, "Accept-Charset"));
add_header(t, "HTTP_ACCEPT_ENCODING",
lookup_header(r, "Accept-Encoding"));
add_header(t, "HTTP_ACCEPT_LANGUAGE",
lookup_header(r, "Accept-Language"));
add_header(t, "HTTP_AUTHORIZATION", lookup_header(r, "Authorization"));
add_header(t, "HTTP_COOKIE", lookup_header(r, "Cookie"));
add_header(t, "HTTP_EXPECT", lookup_header(r, "Expect"));
add_header(t, "HTTP_FROM", lookup_header(r, "From"));
add_header(t, "HTTP_HOST", lookup_header(r, "Host"));
add_header(t, "HTTP_IF_MATCH", lookup_header(r, "If-Match"));
add_header(t, "HTTP_IF_MODIFIED_SINCE",
lookup_header(r, "If-Modified-Since"));
add_header(t, "HTTP_IF_NONE_MATCH", lookup_header(r, "If-None-Match"));
add_header(t, "HTTP_IF_RANGE", lookup_header(r, "If-Range"));
add_header(t, "HTTP_IF_UNMODIFIED_SINCE",
lookup_header(r, "If-Unmodified-Since"));
add_header(t, "HTTP_RANGE", lookup_header(r, "Range"));
add_header(t, "HTTP_REFERER", lookup_header(r, "Referer"));
add_header(t, "HTTP_TE", lookup_header(r, "TE"));
add_header(t, "HTTP_USER_AGENT", lookup_header(r, "User-Agent"));
/* environment variables */
env_arr = ap_table_elts(r->subprocess_env);
env = (table_entry *)env_arr->elts;
for (i = 0; i < env_arr->nelts; ++i) {
add_header(t, env[i].key, env[i].val);
}
hdrs_arr = ap_table_elts(t);
hdrs = (table_entry *)hdrs_arr->elts;
/* calculate length of header data (including nulls) */
n = 0;
for (i = 0; i < hdrs_arr->nelts; ++i) {
n += strlen(hdrs[i].key) + 1;
n += strlen(hdrs[i].val) + 1;
}
/* send header data as netstring */
if (ap_bprintf(f, "%lu:", n) < 0) {
return 0;
}
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (ap_bputs(hdrs[i].key, f) < 0) return 0;
if (ap_bputc('\0', f) < 0) return 0;
if (ap_bputs(hdrs[i].val, f) < 0) return 0;
if (ap_bputc('\0', f) < 0) return 0;
}
if (ap_bputc(',', f) < 0) return 0;
return 1;
}
static int send_request_body (request_rec *r, BUFF *f)
{
if (ap_should_client_block(r)) {
int n;
char buffer[HUGE_STRING_LEN];
while ((n = ap_get_client_block(r, buffer,
sizeof buffer)) > 0) {
if (ap_bwrite(f, buffer, n) != n) return 0;
ap_reset_timeout(r);
}
}
if (ap_bflush(f) < 0) return 0;
return 1;
}
static int scgi_handler(request_rec *r)
{
int ret, sock;
BUFF *f;
char *request_body = NULL;
const char *location;
scgi_cfg *cfg = our_dconfig(r);
if ((ret = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
return ret;
}
/* connect to scgi server */
ap_hard_timeout("scgi connect", r);
log_debug(r, "connecting to server");
if (cfg->unix_socket == DISABLED)
sock = open_socket_inet(r, cfg);
else
sock = open_socket_unix(r, cfg);
if (sock == -1) {
if (request_body)
free(request_body);
return SERVER_ERROR;
}
ap_kill_timeout(r);
f = ap_bcreate(r->pool, B_RDWR | B_SOCKET);
ap_bpushfd(f, sock, sock);
ap_hard_timeout("scgi sending request", r);
/* send headers */
if (!send_headers(r, f, cfg)) {
log_err(r, "error sending response headers");
return SERVER_ERROR;
}
/* send request data */
if (!send_request_body(r, f)) {
log_err(r, "error sending response body");
return SERVER_ERROR;
}
ap_kill_timeout(r);
log_debug(r, "reading response headers");
ret = ap_scan_script_header_err_buff(r, f, NULL);
if (ret) {
if (ret == SERVER_ERROR) {
log_err(r, "error reading response headers");
}
else {
/* Work around an Apache bug whereby the returned
* status is ignored and status_line is used instead.
* This bug is present at least in Apache 1.3.33.
*/
r->status_line = NULL;
}
ap_bclose(f);
return ret;
}
location = ap_table_get(r->headers_out, "Location");
if (location && location[0] == '/' &&
((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) {
ap_bclose(f);
/* Internal redirect -- fake-up a pseudo-request */
r->status = HTTP_OK;
/* This redirect needs to be a GET no matter what the original
* method was.
*/
r->method = ap_pstrdup(r->pool, "GET");
r->method_number = M_GET;
ap_internal_redirect_handler(location, r);
return OK;
}
/* write headers to client */
ap_send_http_header(r);
/* write body to client */
if (!r->header_only) {
ap_send_fb(f, r);
}
ap_bclose(f);
return OK;
}
static void scgi_init(server_rec *s, pool *p)
{
ap_add_version_component("mod_scgi/" MOD_SCGI_VERSION);
}
static void *scgi_create_dir_config(pool *p, char *dirspec)
{
scgi_cfg *cfg = (scgi_cfg *) ap_pcalloc(p, sizeof(scgi_cfg));
cfg->enabled = UNSET;
cfg->addr = UNSET;
cfg->port = UNSET;
cfg->socket_filename = NULL;
cfg->unix_socket= UNSET;
return (void *) cfg;
}
#define MERGE(b, n, a) (n->a == UNSET ? b->a : n->a)
static void *
scgi_merge_dir_config(pool *p, void *basev, void *newv)
{
scgi_cfg *base, *new;
scgi_cfg *cfg = (scgi_cfg *) ap_pcalloc(p, sizeof(scgi_cfg));
base = (scgi_cfg *) basev;
new = (scgi_cfg *) newv;
cfg->enabled = MERGE(base, new, enabled);
cfg->addr = MERGE(base, new, addr);
cfg->port = MERGE(base, new, port);
cfg->unix_socket = MERGE(base, new, unix_socket);
/* wasnt sure if the MERGE macro was correct for char*,so ... */
if (new->socket_filename == NULL)
cfg->socket_filename = ap_pstrdup(p,base->socket_filename);
else
cfg->socket_filename = ap_pstrdup(p,new->socket_filename);
return (void *) cfg;
}
static const char *
cmd_server(cmd_parms *cmd, scgi_cfg *dcfg, char *addr, char *port)
{
int n;
char *tmp;
if (cmd->path == NULL) { /* server command */
return "not a server command";
}
if ((dcfg->addr = inet_addr(addr)) == INADDR_NONE)
return "Invalid syntax for server address";
n = strtol(port, &tmp, 0);
if (tmp[0] != 0 || n < 0 || n > 65535)
return "Invalid server port";
dcfg->port = htons((unsigned short) n);
dcfg->unix_socket = DISABLED;
return NULL;
}
static const char *
cmd_server_unix(cmd_parms *cmd, scgi_cfg *dcfg, char *socketfilename)
{
int n;
char *tmp;
if (cmd->path == NULL) { /* server command */
return "not a server command";
}
if (socketfilename != NULL)
dcfg->socket_filename = ap_pstrdup(cmd->pool,socketfilename);
dcfg->unix_socket = ENABLED;
return NULL;
}
static const char *
cmd_handler(cmd_parms *cmd, scgi_cfg *dcfg, int flag)
{
if (cmd->path == NULL) { /* server command */
return "not a server command";
}
if (flag) {
dcfg->enabled = ENABLED;
}
else {
dcfg->enabled = DISABLED;
}
return NULL;
}
static const command_rec scgi_cmds[] =
{
{ "SCGIServer", cmd_server, NULL, ACCESS_CONF, TAKE2,
"address and port of an SCGI server (e.g. 127.0.0.1 4000)"},
{ "SCGIServerUnix", cmd_server_unix, NULL, ACCESS_CONF, TAKE1,
"filename of an SCGI unix socket server (e.g. /tmp/scgi.socket)"},
{ "SCGIHandler", cmd_handler, NULL, ACCESS_CONF, FLAG,
"On or Off to enable or disable the SCGI handler"},
{NULL}
};
static const handler_rec scgi_handlers[] =
{
{"scgi-handler", scgi_handler},
{NULL}
};
module scgi_module =
{
STANDARD_MODULE_STUFF,
scgi_init, /* module initializer */
scgi_create_dir_config, /* per-directory config creator */
scgi_merge_dir_config, /* dir config merger */
NULL, /* server config creator */
NULL, /* server config merger */
scgi_cmds, /* command table */
scgi_handlers, /* [7] list of handlers */
scgi_trans, /* [2] filename-to-URI translation */
NULL, /* [5] check/validate user_id */
NULL, /* [6] check user_id is valid *here* */
NULL, /* [4] check access by host address */
NULL, /* [7] MIME type checker/setter */
NULL, /* [8] fixups */
NULL, /* [10] logger */
#if MODULE_MAGIC_NUMBER >= 19970103
NULL, /* [3] header parser */
#endif
#if MODULE_MAGIC_NUMBER >= 19970719
NULL, /* process initializer */
#endif
#if MODULE_MAGIC_NUMBER >= 19970728
NULL, /* process exit/cleanup */
#endif
#if MODULE_MAGIC_NUMBER >= 19970902
NULL /* [1] post read_request handling */
#endif
};
_______________________________________________
Quixote-users mailing list
[email protected]
http://mail.mems-exchange.org/mailman/listinfo/quixote-users