Newer
Older
session_threads--;
/* host_ip wasn't defined in http_session_thread */
if(trashcan(&scfg,session.host_ip,"ip")) {
lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
close_socket(&session.socket);
sem_wait(&session.output_thread_terminated);
RingBufDispose(&session.outbuf);
thread_down();
session_threads--;
return;
}
protected_int32_adjust(&active_clients, 1);
update_clients();
SAFECOPY(session.username,unknown);
SAFECOPY(session.client.addr,session.host_ip);
SAFECOPY(session.client.host,session.host_name);
session.client.port=ntohs(session.addr.sin_port);
session.client.time=time(NULL);
session.client.protocol="HTTP";
session.client.user=session.username;
session.client.size=sizeof(session.client);
client_on(session.socket, &session.client, /* update existing client record? */FALSE);
session.last_user_num=-1;
session.last_js_user_num=-1;
session.logon_time=0;
session.subscan=(subscan_t*)alloca(sizeof(subscan_t)*scfg.total_subs);
while(!session.finished) {
init_error=FALSE;
memset(&(session.req), 0, sizeof(session.req));
loop_count=0;
if(session.req.ld) {
FREE_AND_NULL(session.req.ld->hostname);
FREE_AND_NULL(session.req.ld->ident);
FREE_AND_NULL(session.req.ld->user);
FREE_AND_NULL(session.req.ld->request);
FREE_AND_NULL(session.req.ld->referrer);
FREE_AND_NULL(session.req.ld->agent);
FREE_AND_NULL(session.req.ld->vhost);
FREE_AND_NULL(session.req.ld);
}
if(startup->options&WEB_OPT_HTTP_LOGGING) {
/* FREE()d in http_logging_thread... passed there by close_request() */
if((session.req.ld=(struct log_data*)malloc(sizeof(struct log_data)))==NULL)
lprintf(LOG_ERR,"%04d Cannot allocate memory for log data!",session.socket);
}
if(session.req.ld!=NULL) {
memset(session.req.ld,0,sizeof(struct log_data));
/* FREE()d in http_logging_thread */
session.req.ld->hostname=strdup(session.host_name);
}
while((redirp==NULL || session.req.send_location >= MOVED_TEMP)
&& !session.finished && !session.req.finished
&& session.socket!=INVALID_SOCKET) {
SAFECOPY(session.req.status,"200 OK");
session.req.send_location=NO_LOCATION;
if(session.req.headers==NULL) {
if((session.req.headers=strListInit())==NULL) {
lprintf(LOG_ERR,"%04d !ERROR allocating memory for header list",session.socket);
init_error=TRUE;
}
if(session.req.cgi_env==NULL) {
if((session.req.cgi_env=strListInit())==NULL) {
lprintf(LOG_ERR,"%04d !ERROR allocating memory for CGI environment list",session.socket);
init_error=TRUE;
}
}
if(session.req.dynamic_heads==NULL) {
if((session.req.dynamic_heads=strListInit())==NULL) {
lprintf(LOG_ERR,"%04d !ERROR allocating memory for dynamic header list",session.socket);
init_error=TRUE;
}
if(get_req(&session,redirp)) {
if(init_error) {
send_error(&session, error_500);
}
/* At this point, if redirp is non-NULL then the headers have already been parsed */
if((session.http_ver<HTTP_1_0)||redirp!=NULL||parse_headers(&session)) {
if(check_request(&session)) {
if(session.req.send_location < MOVED_TEMP || session.req.virtual_path[0]!='/' || loop_count++ >= MAX_REDIR_LOOPS) {
if(read_post_data(&session))
respond(&session);
}
safe_snprintf(redir_req,sizeof(redir_req),"%s %s%s%s",methods[session.req.method]
,session.req.virtual_path,session.http_ver<HTTP_1_0?"":" ",http_vers[session.http_ver]);
lprintf(LOG_DEBUG,"%04d Internal Redirect to: %s",socket,redir_req);
redirp=redir_req;
}
}
else {
session.req.keep_alive=FALSE;
break;
}
close_request(&session);
http_logoff(&session,socket,__LINE__);
if(session.js_cx!=NULL) {
lprintf(LOG_DEBUG,"%04d JavaScript: Destroying context",socket);
JS_DestroyContext(session.js_cx); /* Free Context */
session.js_cx=NULL;
}
#ifndef ONE_JS_RUNTIME
if(session.js_runtime!=NULL) {
lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",socket);
jsrt_Release(session.js_runtime);
#endif
#ifdef _WIN32
if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE))
PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
#endif
close_socket(&session.socket);
sem_wait(&session.output_thread_terminated);
sem_destroy(&session.output_thread_terminated);
RingBufDispose(&session.outbuf);
clients_remain=protected_int32_adjust(&active_clients, -1);
update_clients();
client_off(socket);
session_threads--;
thread_down();
if(startup->index_file_name==NULL || startup->cgi_ext==NULL)
lprintf(LOG_DEBUG,"%04d !!! ALL YOUR BASE ARE BELONG TO US !!!", socket);
lprintf(LOG_INFO,"%04d Session thread terminated (%u clients, %u threads remain, %lu served)"
,socket, clients_remain, thread_count, served);
}
void DLLCALL web_terminate(void)
{
lprintf(LOG_INFO,"%04d Web Server terminate",server_socket);
terminate_server=TRUE;
}
static void cleanup(int code)
{
while(session_threads) {
lprintf(LOG_INFO,"#### Web Server waiting on %d active session threads",session_threads);
SLEEP(1000);
}
free_cfg(&scfg);
listFree(&log_list);
mime_types=iniFreeNamedStringList(mime_types);
cgi_handlers=iniFreeNamedStringList(cgi_handlers);
xjs_handlers=iniFreeNamedStringList(xjs_handlers);
cgi_env=iniFreeStringList(cgi_env);
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(server_socket!=INVALID_SOCKET) {
close_socket(&server_socket);
if(active_clients.value)
lprintf(LOG_WARNING,"#### Web Server terminating with %ld active clients", active_clients.value);
else
protected_int32_destroy(active_clients);
update_clients();
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
#endif
thread_down();
status("Down");
if(terminate_server || code)
lprintf(LOG_INFO,"#### Web Server thread terminated (%lu clients served)", served);
if(thread_count)
lprintf(LOG_WARNING,"#### !Web Server threads (%u) remain after termination", thread_count);
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(startup->cbdata,code);
}
const char* DLLCALL web_ver(void)
{
static char ver[256];
char compiler[32];
DESCRIBE_COMPILER(compiler);
sscanf("$Revision$", "%*s %s", revision);
sprintf(ver,"%s %s%s "
"Compiled %s %s with %s"
,server_name
,revision
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,__DATE__, __TIME__, compiler);
return(ver);
}
void http_logging_thread(void* arg)
{
char base[MAX_PATH+1];
char filename[MAX_PATH+1];
char newfilename[MAX_PATH+1];
FILE* logfile=NULL;
http_logging_thread_running=TRUE;
terminate_http_logging_thread=FALSE;
SAFECOPY(base,arg);
if(!base[0])
SAFEPRINTF(base,"%slogs/http-",scfg.logs_dir);
SetThreadName("HTTP Logging");
filename[0]=0;
newfilename[0]=0;
thread_up(TRUE /* setuid */);
lprintf(LOG_INFO,"%04d HTTP logging thread started", server_socket);
for(;;) {
struct log_data *ld;
char sizestr[100];
if(!listSemTryWait(&log_list)) {
if(logfile!=NULL)
fflush(logfile);
/*
* Because the sem is posted when terminate_http_logging_thread is set, this will
* ensure that all pending log entries are written to disk
*/
if(terminate_http_logging_thread)
break;
lprintf(LOG_ERR,"%04d HTTP logging thread received NULL linked list log entry"
,server_socket);
continue;
}
SAFECOPY(newfilename,base);
if(startup->options&WEB_OPT_VIRTUAL_HOSTS && ld->vhost!=NULL) {
strcat(newfilename,ld->vhost);
if(ld->vhost[0])
strcat(newfilename,"-");
}
strftime(strchr(newfilename,0),15,"%Y-%m-%d.log",&ld->completed);
if(logfile==NULL || strcmp(newfilename,filename)) {
fclose(logfile);
SAFECOPY(filename,newfilename);
logfile=fopen(filename,"ab");
if(logfile)
lprintf(LOG_INFO,"%04d HTTP logfile is now: %s",server_socket,filename);
}
if(logfile!=NULL) {
if(ld->status) {
sprintf(sizestr,"%d",ld->size);
strftime(timestr,sizeof(timestr),"%d/%b/%Y:%H:%M:%S %z",&ld->completed);
/*
* In case of a termination, do no block for a lock... just discard
* the output.
*/
while(lock(fileno(logfile),0,1) && !terminate_http_logging_thread) {
SLEEP(10);
}
fprintf(logfile,"%s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\"\n"
,ld->hostname?(ld->hostname[0]?ld->hostname:"-"):"-"
,ld->ident?(ld->ident[0]?ld->ident:"-"):"-"
,ld->user?(ld->user[0]?ld->user:"-"):"-"
,timestr
,ld->request?(ld->request[0]?ld->request:"-"):"-"
,ld->status
,ld->size?sizestr:"-"
,ld->referrer?(ld->referrer[0]?ld->referrer:"-"):"-"
,ld->agent?(ld->agent[0]?ld->agent:"-"):"-");
unlock(fileno(logfile),0,1);
}
else {
lprintf(LOG_ERR,"%04d HTTP server failed to open logfile %s (%d)!",server_socket,filename,errno);
}
FREE_AND_NULL(ld->hostname);
FREE_AND_NULL(ld->ident);
FREE_AND_NULL(ld->user);
FREE_AND_NULL(ld->request);
FREE_AND_NULL(ld->referrer);
FREE_AND_NULL(ld->agent);
}
fclose(logfile);
thread_down();
lprintf(LOG_INFO,"%04d HTTP logging thread terminated",server_socket);
http_logging_thread_running=FALSE;
}
void DLLCALL web_server(void* arg)
{
int i;
int result;
time_t start;
WORD host_port;
char host_ip[32];
char path[MAX_PATH+1];
char logstr[256];
char mime_types_ini[MAX_PATH+1];
char web_handler_ini[MAX_PATH+1];
SOCKADDR_IN server_addr={0};
SOCKADDR_IN client_addr;
socklen_t client_addr_len;
SOCKET client_socket;
SOCKET high_socket_set;
fd_set socket_set;
time_t t;
time_t initialized=0;
FILE* fp;
char compiler[32];
http_session_t * session=NULL;
struct timeval tv;
#ifdef ONE_JS_RUNTIME
JSRuntime* js_runtime;
#endif
#ifdef SO_ACCEPTFILTER
struct accept_filter_arg afa;
#endif
if(startup==NULL) {
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(web_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
#ifdef _THREAD_SUID_BROKEN
if(thread_suid_broken)
startup->seteuid(TRUE);
#endif
/* Setup intelligent defaults */
if(startup->port==0) startup->port=IPPORT_HTTP;
if(startup->root_dir[0]==0) SAFECOPY(startup->root_dir,WEB_DEFAULT_ROOT_DIR);
if(startup->error_dir[0]==0) SAFECOPY(startup->error_dir,WEB_DEFAULT_ERROR_DIR);
if(startup->default_auth_list[0]==0) SAFECOPY(startup->default_auth_list,WEB_DEFAULT_AUTH_LIST);
if(startup->cgi_dir[0]==0) SAFECOPY(startup->cgi_dir,WEB_DEFAULT_CGI_DIR);
if(startup->default_cgi_content[0]==0) SAFECOPY(startup->default_cgi_content,WEB_DEFAULT_CGI_CONTENT);
if(startup->max_inactivity==0) startup->max_inactivity=120; /* seconds */
if(startup->max_cgi_inactivity==0) startup->max_cgi_inactivity=120; /* seconds */
if(startup->sem_chk_freq==0) startup->sem_chk_freq=2; /* seconds */
if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
if(startup->ssjs_ext[0]==0) SAFECOPY(startup->ssjs_ext,".ssjs");
if(startup->js_ext[0]==0) SAFECOPY(startup->js_ext,".bbs");
ZERO_VAR(js_server_props);
SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision);
js_server_props.version_detail=web_ver();
js_server_props.clients=&active_clients.value;
js_server_props.options=&startup->options;
js_server_props.interface_addr=&startup->interface_addr;
uptime=0;
served=0;
startup->recycle_now=FALSE;
startup->shutdown_now=FALSE;
terminate_server=FALSE;
do {
thread_up(FALSE /* setuid */);
status("Initializing");
/* Copy html directories */
SAFECOPY(root_dir,startup->root_dir);
SAFECOPY(error_dir,startup->error_dir);
SAFECOPY(default_auth_list,startup->default_auth_list);
SAFECOPY(cgi_dir,startup->cgi_dir);
if(startup->temp_dir[0])
SAFECOPY(temp_dir,startup->temp_dir);
else
SAFECOPY(temp_dir,"../temp");
/* Change to absolute path */
prep_dir(startup->ctrl_dir, root_dir, sizeof(root_dir));
prep_dir(startup->ctrl_dir, temp_dir, sizeof(temp_dir));
prep_dir(root_dir, error_dir, sizeof(error_dir));
prep_dir(root_dir, cgi_dir, sizeof(cgi_dir));
/* Trim off trailing slash/backslash */
if(IS_PATH_DELIM(*(p=lastchar(root_dir)))) *p=0;
memset(&scfg, 0, sizeof(scfg));
lprintf(LOG_INFO,"%s Revision %s%s"
,revision
#ifdef _DEBUG
," Debug"
#else
,""
#endif
);
DESCRIBE_COMPILER(compiler);
lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
if(!winsock_startup()) {
cleanup(1);
return;
}
t=time(NULL);
lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
if(chdir(startup->ctrl_dir)!=0)
lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
/* Initial configuration and load from CNF files */
SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
if(!load_cfg(&scfg, NULL, TRUE, logstr)) {
lprintf(LOG_CRIT,"!ERROR %s",logstr);
lprintf(LOG_CRIT,"!FAILED to load configuration files");
cleanup(1);
return;
}
lprintf(LOG_DEBUG,"Temporary file directory: %s", temp_dir);
MKDIR(temp_dir);
if(!isdir(temp_dir)) {
lprintf(LOG_CRIT,"!Invalid temp directory: %s", temp_dir);
cleanup(1);
return;
}
lprintf(LOG_DEBUG,"Root directory: %s", root_dir);
lprintf(LOG_DEBUG,"Error directory: %s", error_dir);
lprintf(LOG_DEBUG,"CGI directory: %s", cgi_dir);
iniFileName(mime_types_ini,sizeof(mime_types_ini),scfg.ctrl_dir,"mime_types.ini");
mime_types=read_ini_list(mime_types_ini,NULL /* root section */,"MIME types"
,mime_types);
iniFileName(web_handler_ini,sizeof(web_handler_ini),scfg.ctrl_dir,"web_handler.ini");
if((cgi_handlers=read_ini_list(web_handler_ini,"CGI."PLATFORM_DESC,"CGI content handlers"
,cgi_handlers))==NULL)
cgi_handlers=read_ini_list(web_handler_ini,"CGI","CGI content handlers"
,cgi_handlers);
xjs_handlers=read_ini_list(web_handler_ini,"JavaScript","JavaScript content handlers"
,xjs_handlers);
/* Don't do this for *each* CGI request, just once here during [re]init */
iniFileName(cgi_env_ini,sizeof(cgi_env_ini),scfg.ctrl_dir,"cgi_env.ini");
if((fp=iniOpenFile(cgi_env_ini,/* create? */FALSE)) != NULL) {
cgi_env = iniReadFile(fp);
iniCloseFile(fp);
}
if(startup->host_name[0]==0)
SAFECOPY(startup->host_name,scfg.sys_inetaddr);
if(uptime==0)
uptime=time(NULL); /* this must be done *after* setting the timezone */
protected_int32_init(&active_clients,0);
update_clients();
/* open a socket and wait for a client */
server_socket = open_socket(SOCK_STREAM);
if(server_socket == INVALID_SOCKET) {
lprintf(LOG_CRIT,"!ERROR %d creating HTTP socket", ERROR_VALUE);
cleanup(1);
return;
}
/*
* i=1;
* if(setsockopt(server_socket, IPPROTO_TCP, TCP_NOPUSH, &i, sizeof(i)))
* lprintf("Cannot set TCP_NOPUSH socket option");
*/
#ifdef SO_ACCEPTFILTER
memset(&afa, 0, sizeof(afa));
strcpy(afa.af_name, "httpready");
setsockopt(server_socket, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
#endif
lprintf(LOG_DEBUG,"%04d Web Server socket opened",server_socket);
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->port);
if(startup->port < IPPORT_RESERVED) {
if(startup->seteuid!=NULL)
startup->seteuid(FALSE);
}
result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
,startup->bind_retry_count,startup->bind_retry_delay,"Web Server",lprintf);
if(startup->port < IPPORT_RESERVED) {
if(startup->seteuid!=NULL)
startup->seteuid(TRUE);
}
if(result != 0) {
lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP);
cleanup(1);
return;
}
result = listen(server_socket, 64);
if(result != 0) {
lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket"
,server_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
lprintf(LOG_INFO,"%04d Web Server listening on port %u"
,server_socket, startup->port);
status("Listening");
listInit(&log_list,/* flags */ LINK_LIST_MUTEX|LINK_LIST_SEMAPHORE);
if(startup->options&WEB_OPT_HTTP_LOGGING) {
/********************/
/* Start log thread */
/********************/
_beginthread(http_logging_thread, 0, startup->logfile_base);
}
#ifdef ONE_JS_RUNTIME
if(js_runtime == NULL) {
lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes"
,server_socket,startup->js.max_bytes);
if((js_runtime=jsrt_GetNew(startup->js.max_bytes, 0, __FILE__, __LINE__))==NULL) {
lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",server_socket);
/* Sleep 15 seconds then try again */
/* ToDo: Something better should be used here. */
SLEEP(15000);
continue;
}
}
#endif
/* Setup recycle/shutdown semaphore file lists */
shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","web");
recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","web");
SAFEPRINTF(path,"%swebsrvr.rec",scfg.ctrl_dir); /* legacy */
semfile_list_add(&recycle_semfiles,path);
semfile_list_add(&recycle_semfiles,mime_types_ini);
semfile_list_add(&recycle_semfiles,web_handler_ini);
semfile_list_add(&recycle_semfiles,cgi_env_ini);
if(!initialized) {
semfile_list_check(&initialized,recycle_semfiles);
semfile_list_check(&initialized,shutdown_semfiles);
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started(startup->cbdata);
lprintf(LOG_INFO,"%04d Web Server thread started", server_socket);
while(server_socket!=INVALID_SOCKET && !terminate_server) {
/* check for re-cycle/shutdown semaphores */
if(active_clients.value==0) {
if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
,server_socket,p);
if(session!=NULL) {
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
}
break;
}
#if 0 /* unused */
if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
startup->recycle_now=TRUE;
#endif
if(startup->recycle_now==TRUE) {
lprintf(LOG_INFO,"%04d Recycle semaphore signaled",server_socket);
startup->recycle_now=FALSE;
if(session!=NULL) {
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
}
break;
}
if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
&& lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
,server_socket,p))
|| (startup->shutdown_now==TRUE
&& lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
,server_socket))) {
startup->shutdown_now=FALSE;
terminate_server=TRUE;
if(session!=NULL) {
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
}
if(session==NULL) {
/* FREE()d at the start of the session thread */
if((session=malloc(sizeof(http_session_t)))==NULL) {
lprintf(LOG_CRIT,"%04d !ERROR allocating %u bytes of memory for http_session_t"
mswait(3000);
continue;
}
memset(session, 0, sizeof(http_session_t));
session->socket=INVALID_SOCKET;
pthread_mutex_init(&session->struct_filled,NULL);
pthread_mutex_lock(&session->struct_filled);
_beginthread(http_session_thread, 0, session);
/* now wait for connection */
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);
high_socket_set=server_socket+1;
tv.tv_sec=startup->sem_chk_freq;
tv.tv_usec=0;
if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
if(i==0)
if(ERROR_VALUE==EINTR)
lprintf(LOG_DEBUG,"Web Server listening interrupted");
else if(ERROR_VALUE == ENOTSOCK)
lprintf(LOG_INFO,"Web Server socket closed");
lprintf(LOG_WARNING,"!ERROR %d selecting socket",ERROR_VALUE);
continue;
if(server_socket==INVALID_SOCKET) { /* terminated */
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
client_addr_len = sizeof(client_addr);
if(server_socket!=INVALID_SOCKET
&& FD_ISSET(server_socket,&socket_set)) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
lprintf(LOG_NOTICE,"!NO SOCKETS set by select");
continue;
}
if(client_socket == INVALID_SOCKET) {
lprintf(LOG_WARNING,"!ERROR %d accepting connection", ERROR_VALUE);
#ifdef _WIN32
if(WSAGetLastError()==WSAENOBUFS) { /* recycle (re-init WinSock) on this error */
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
break;
#endif
if(startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr));
if(trashcan(&scfg,host_ip,"ip-silent")) {
close_socket(&client_socket);
continue;
}
if(startup->max_clients && active_clients.value>=startup->max_clients) {
,client_socket, startup->max_clients);
mswait(3000);
close_socket(&client_socket);
continue;
}
host_port=ntohs(client_addr.sin_port);
lprintf(LOG_INFO,"%04d HTTP connection accepted from: %s port %u"
,host_ip, host_port);
SAFECOPY(session->host_ip,host_ip);
session->addr=client_addr;
session->socket=client_socket;
session->js_branch.auto_terminate=TRUE;
session->js_branch.terminated=&terminate_server;
session->js_branch.limit=startup->js.branch_limit;
session->js_branch.gc_interval=startup->js.gc_interval;
session->js_branch.yield_interval=startup->js.yield_interval;
#ifdef ONE_JS_RUNTIME
session->js_runtime=js_runtime;
#endif
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
served++;
if(session) {
pthread_mutex_unlock(&session->struct_filled);
session=NULL;
}
/* Wait for active clients to terminate */
if(active_clients.value) {
lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
,server_socket, active_clients.value);
start=time(NULL);
while(active_clients.value) {
if(time(NULL)-start>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients"
,server_socket, active_clients.value);
break;
}
mswait(100);
}
}
if(http_logging_thread_running) {
terminate_http_logging_thread=TRUE;
listSemPost(&log_list);
mswait(100);
}
if(http_logging_thread_running) {
lprintf(LOG_DEBUG,"%04d Waiting for HTTP logging thread to terminate..."
,server_socket);
start=time(NULL);
while(http_logging_thread_running) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for HTTP logging thread to "
"terminate", server_socket);
break;
}
mswait(100);
}
}
#ifdef ONE_JS_RUNTIME
lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",server_socket);
jsrt_Release(js_runtime);
js_runtime=NULL;
}
#endif
if(!terminate_server) {
lprintf(LOG_INFO,"Recycling server...");
if(startup->recycle!=NULL)
startup->recycle(startup->cbdata);
} while(!terminate_server);