You've already forked qlg.tsgz.moe
Init Repo
This commit is contained in:
1548
vendor/swoole/src/network/Client.c
vendored
Executable file
1548
vendor/swoole/src/network/Client.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
374
vendor/swoole/src/network/Connection.c
vendored
Executable file
374
vendor/swoole/src/network/Connection.c
vendored
Executable file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2012-2015 The Swoole Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Server.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
int swConnection_onSendfile(swConnection *conn, swBuffer_trunk *chunk)
|
||||
{
|
||||
int ret;
|
||||
swTask_sendfile *task = chunk->store.ptr;
|
||||
|
||||
#ifdef HAVE_TCP_NOPUSH
|
||||
if (task->offset == 0 && conn->tcp_nopush == 0)
|
||||
{
|
||||
/**
|
||||
* disable tcp_nodelay
|
||||
*/
|
||||
if (conn->tcp_nodelay)
|
||||
{
|
||||
int tcp_nodelay = 0;
|
||||
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1)
|
||||
{
|
||||
swWarn("setsockopt(TCP_NODELAY) failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* enable tcp_nopush
|
||||
*/
|
||||
if (swSocket_tcp_nopush(conn->fd, 1) == -1)
|
||||
{
|
||||
swWarn("swSocket_tcp_nopush() failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
conn->tcp_nopush = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sendn = (task->length - task->offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : task->length - task->offset;
|
||||
|
||||
#ifdef SW_USE_OPENSSL
|
||||
if (conn->ssl)
|
||||
{
|
||||
ret = swSSL_sendfile(conn, task->fd, &task->offset, sendn);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ret = swoole_sendfile(conn->fd, task->fd, &task->offset, sendn);
|
||||
}
|
||||
|
||||
swTrace("ret=%d|task->offset=%ld|sendn=%d|filesize=%ld", ret, (long)task->offset, sendn, task->length);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
switch (swConnection_error(errno))
|
||||
{
|
||||
case SW_ERROR:
|
||||
swSysError("sendfile(%s, %ld, %d) failed.", task->filename, (long)task->offset, sendn);
|
||||
swBuffer_pop_trunk(conn->out_buffer, chunk);
|
||||
return SW_OK;
|
||||
case SW_CLOSE:
|
||||
conn->close_wait = 1;
|
||||
return SW_ERR;
|
||||
case SW_WAIT:
|
||||
conn->send_wait = 1;
|
||||
return SW_ERR;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//sendfile finish
|
||||
if (task->offset >= task->length)
|
||||
{
|
||||
swBuffer_pop_trunk(conn->out_buffer, chunk);
|
||||
|
||||
#ifdef HAVE_TCP_NOPUSH
|
||||
/**
|
||||
* disable tcp_nopush
|
||||
*/
|
||||
if (swSocket_tcp_nopush(conn->fd, 0) == -1)
|
||||
{
|
||||
swWarn("swSocket_tcp_nopush() failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
conn->tcp_nopush = 0;
|
||||
|
||||
/**
|
||||
* enable tcp_nodelay
|
||||
*/
|
||||
if (conn->tcp_nodelay)
|
||||
{
|
||||
int value = 1;
|
||||
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &value, sizeof(int)) == -1)
|
||||
{
|
||||
swWarn("setsockopt(TCP_NODELAY) failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* send buffer to client
|
||||
*/
|
||||
int swConnection_buffer_send(swConnection *conn)
|
||||
{
|
||||
int ret, sendn;
|
||||
|
||||
swBuffer *buffer = conn->out_buffer;
|
||||
swBuffer_trunk *trunk = swBuffer_get_trunk(buffer);
|
||||
sendn = trunk->length - trunk->offset;
|
||||
|
||||
if (sendn == 0)
|
||||
{
|
||||
swBuffer_pop_trunk(buffer, trunk);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
ret = swConnection_send(conn, trunk->store.ptr + trunk->offset, sendn, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
switch (swConnection_error(errno))
|
||||
{
|
||||
case SW_ERROR:
|
||||
swWarn("send to fd[%d] failed. Error: %s[%d]", conn->fd, strerror(errno), errno);
|
||||
break;
|
||||
case SW_CLOSE:
|
||||
conn->close_errno = errno;
|
||||
conn->close_wait = 1;
|
||||
return SW_ERR;
|
||||
case SW_WAIT:
|
||||
conn->send_wait = 1;
|
||||
return SW_ERR;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
//trunk full send
|
||||
else if (ret == sendn || sendn == 0)
|
||||
{
|
||||
swBuffer_pop_trunk(buffer, trunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
trunk->offset += ret;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
swString* swConnection_get_string_buffer(swConnection *conn)
|
||||
{
|
||||
swString *buffer = conn->object;
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return swString_new(SW_BUFFER_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static char tmp_address[INET6_ADDRSTRLEN];
|
||||
|
||||
char* swConnection_get_ip(swConnection *conn)
|
||||
{
|
||||
if (conn->socket_type == SW_SOCK_TCP)
|
||||
{
|
||||
return inet_ntoa(conn->info.addr.inet_v4.sin_addr);
|
||||
}
|
||||
else if (conn->socket_type == SW_SOCK_TCP6)
|
||||
{
|
||||
if (inet_ntop(AF_INET6, &conn->info.addr.inet_v6.sin6_addr, tmp_address, sizeof(tmp_address)) == NULL)
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
else
|
||||
{
|
||||
return tmp_address;
|
||||
}
|
||||
}
|
||||
else if (conn->socket_type == SW_SOCK_UNIX_STREAM)
|
||||
{
|
||||
return conn->info.addr.un.sun_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
int swConnection_get_port(swConnection *conn)
|
||||
{
|
||||
if (conn->socket_type == SW_SOCK_TCP)
|
||||
{
|
||||
return ntohs(conn->info.addr.inet_v4.sin_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ntohs(conn->info.addr.inet_v6.sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
void swConnection_sendfile_destructor(swBuffer_trunk *chunk)
|
||||
{
|
||||
swTask_sendfile *task = chunk->store.ptr;
|
||||
close(task->fd);
|
||||
sw_free(task->filename);
|
||||
sw_free(task);
|
||||
}
|
||||
|
||||
int swConnection_sendfile(swConnection *conn, char *filename, off_t offset, size_t length)
|
||||
{
|
||||
if (conn->out_buffer == NULL)
|
||||
{
|
||||
conn->out_buffer = swBuffer_new(SW_BUFFER_SIZE);
|
||||
if (conn->out_buffer == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
swBuffer_trunk error_chunk;
|
||||
swTask_sendfile *task = sw_malloc(sizeof(swTask_sendfile));
|
||||
if (task == NULL)
|
||||
{
|
||||
swWarn("malloc for swTask_sendfile failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
bzero(task, sizeof(swTask_sendfile));
|
||||
|
||||
task->filename = sw_strdup(filename);
|
||||
int file_fd = open(filename, O_RDONLY);
|
||||
if (file_fd < 0)
|
||||
{
|
||||
sw_free(task->filename);
|
||||
sw_free(task);
|
||||
swSysError("open(%s) failed.", filename);
|
||||
return SW_OK;
|
||||
}
|
||||
task->fd = file_fd;
|
||||
task->offset = offset;
|
||||
|
||||
struct stat file_stat;
|
||||
if (fstat(file_fd, &file_stat) < 0)
|
||||
{
|
||||
swSysError("fstat(%s) failed.", filename);
|
||||
error_chunk.store.ptr = task;
|
||||
swConnection_sendfile_destructor(&error_chunk);
|
||||
return SW_ERR;
|
||||
}
|
||||
if (offset < 0 || (length + offset > file_stat.st_size))
|
||||
{
|
||||
swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "length or offset is invalid.");
|
||||
error_chunk.store.ptr = task;
|
||||
swConnection_sendfile_destructor(&error_chunk);
|
||||
return SW_OK;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
task->length = file_stat.st_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
task->length = length + offset;
|
||||
}
|
||||
|
||||
swBuffer_trunk *chunk = swBuffer_new_trunk(conn->out_buffer, SW_CHUNK_SENDFILE, 0);
|
||||
if (chunk == NULL)
|
||||
{
|
||||
swWarn("get out_buffer trunk failed.");
|
||||
error_chunk.store.ptr = task;
|
||||
swConnection_sendfile_destructor(&error_chunk);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
chunk->store.ptr = (void *) task;
|
||||
chunk->destroy = swConnection_sendfile_destructor;
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
|
||||
void swConnection_clear_string_buffer(swConnection *conn)
|
||||
{
|
||||
swString *buffer = conn->object;
|
||||
if (buffer != NULL)
|
||||
{
|
||||
swString_free(buffer);
|
||||
conn->object = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
swBuffer_trunk* swConnection_get_in_buffer(swConnection *conn)
|
||||
{
|
||||
swBuffer_trunk *trunk = NULL;
|
||||
swBuffer *buffer;
|
||||
|
||||
if (conn->in_buffer == NULL)
|
||||
{
|
||||
buffer = swBuffer_new(SW_BUFFER_SIZE);
|
||||
//buffer create failed
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
//new trunk
|
||||
trunk = swBuffer_new_trunk(buffer, SW_CHUNK_DATA, buffer->trunk_size);
|
||||
if (trunk == NULL)
|
||||
{
|
||||
sw_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
conn->in_buffer = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = conn->in_buffer;
|
||||
trunk = buffer->tail;
|
||||
if (trunk == NULL || trunk->length == buffer->trunk_size)
|
||||
{
|
||||
trunk = swBuffer_new_trunk(buffer, SW_CHUNK_DATA, buffer->trunk_size);
|
||||
}
|
||||
}
|
||||
return trunk;
|
||||
}
|
||||
|
||||
swBuffer_trunk* swConnection_get_out_buffer(swConnection *conn, uint32_t type)
|
||||
{
|
||||
swBuffer_trunk *trunk;
|
||||
if (conn->out_buffer == NULL)
|
||||
{
|
||||
conn->out_buffer = swBuffer_new(SW_BUFFER_SIZE);
|
||||
if (conn->out_buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (type == SW_CHUNK_SENDFILE)
|
||||
{
|
||||
trunk = swBuffer_new_trunk(conn->out_buffer, SW_CHUNK_SENDFILE, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
trunk = swBuffer_get_trunk(conn->out_buffer);
|
||||
if (trunk == NULL)
|
||||
{
|
||||
trunk = swBuffer_new_trunk(conn->out_buffer, SW_CHUNK_DATA, conn->out_buffer->trunk_size);
|
||||
}
|
||||
}
|
||||
return trunk;
|
||||
}
|
485
vendor/swoole/src/network/DNS.c
vendored
Executable file
485
vendor/swoole/src/network/DNS.c
vendored
Executable file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Client.h"
|
||||
|
||||
#define SW_DNS_SERVER_CONF "/etc/resolv.conf"
|
||||
#define SW_DNS_SERVER_NUM 2
|
||||
|
||||
enum swDNS_type
|
||||
{
|
||||
SW_DNS_A_RECORD = 0x01, //Lookup IPv4 address
|
||||
SW_DNS_AAAA_RECORD = 0x1c, //Lookup IPv6 address
|
||||
SW_DNS_MX_RECORD = 0x0f //Lookup mail server for domain
|
||||
};
|
||||
|
||||
enum swDNS_error
|
||||
{
|
||||
SW_DNS_NOT_EXIST, //Error: adress does not exist
|
||||
SW_DNS_TIMEOUT, //Lookup time expired
|
||||
SW_DNS_ERROR //No memory or other error
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*callback)(char *domain, swDNSResolver_result *result, void *data);
|
||||
char *domain;
|
||||
void *data;
|
||||
} swDNS_lookup_request;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t num;
|
||||
|
||||
} swDNS_result;
|
||||
|
||||
/* Struct for the DNS Header */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t id;
|
||||
uchar rd :1;
|
||||
uchar tc :1;
|
||||
uchar aa :1;
|
||||
uchar opcode :4;
|
||||
uchar qr :1;
|
||||
uchar rcode :4;
|
||||
uchar z :3;
|
||||
uchar ra :1;
|
||||
uint16_t qdcount;
|
||||
uint16_t ancount;
|
||||
uint16_t nscount;
|
||||
uint16_t arcount;
|
||||
} swDNSResolver_header;
|
||||
|
||||
/* Struct for the flags for the DNS Question */
|
||||
typedef struct q_flags
|
||||
{
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
} Q_FLAGS;
|
||||
|
||||
/* Struct for the flags for the DNS RRs */
|
||||
typedef struct rr_flags
|
||||
{
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
uint32_t ttl;
|
||||
uint16_t rdlength;
|
||||
} RR_FLAGS;
|
||||
|
||||
static uint16_t swoole_dns_request_id = 1;
|
||||
static swClient *resolver_socket = NULL;
|
||||
static swHashMap *request_map = NULL;
|
||||
|
||||
static int domain_encode(char *src, int n, char *dest);
|
||||
static void domain_decode(char *str);
|
||||
static int swDNSResolver_get_server();
|
||||
static int swDNSResolver_onReceive(swReactor *reactor, swEvent *event);
|
||||
|
||||
static int swDNSResolver_get_server()
|
||||
{
|
||||
FILE *fp;
|
||||
char line[100];
|
||||
char buf[16] = {0};
|
||||
|
||||
if ((fp = fopen(SW_DNS_SERVER_CONF, "rt")) == NULL)
|
||||
{
|
||||
swWarn("fopen("SW_DNS_SERVER_CONF") failed. Error: %s[%d]", strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
while (fgets(line, 100, fp))
|
||||
{
|
||||
if (strncmp(line, "nameserver", 10) == 0)
|
||||
{
|
||||
strcpy(buf, strtok(line, " "));
|
||||
strcpy(buf, strtok(NULL, "\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (strlen(buf) == 0)
|
||||
{
|
||||
SwooleG.dns_server_v4 = sw_strdup(SW_DNS_DEFAULT_SERVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
SwooleG.dns_server_v4 = sw_strdup(buf);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swDNSResolver_onReceive(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swDNSResolver_header *header = NULL;
|
||||
Q_FLAGS *qflags = NULL;
|
||||
RR_FLAGS *rrflags = NULL;
|
||||
|
||||
char packet[SW_CLIENT_BUFFER_SIZE];
|
||||
uchar rdata[10][254];
|
||||
uint32_t type[10];
|
||||
|
||||
char *temp;
|
||||
uint16_t steps;
|
||||
|
||||
char *_domain_name;
|
||||
char name[10][254];
|
||||
int i, j;
|
||||
|
||||
int ret = recv(event->fd, packet, sizeof(packet) - 1, 0);
|
||||
if (ret <= 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
packet[ret] = 0;
|
||||
header = (swDNSResolver_header *) packet;
|
||||
steps = sizeof(swDNSResolver_header);
|
||||
|
||||
_domain_name = &packet[steps];
|
||||
domain_decode(_domain_name);
|
||||
steps = steps + (strlen(_domain_name) + 2);
|
||||
|
||||
qflags = (Q_FLAGS *) &packet[steps];
|
||||
(void) qflags;
|
||||
steps = steps + sizeof(Q_FLAGS);
|
||||
|
||||
int ancount = ntohs(header->ancount);
|
||||
if (ancount > 10)
|
||||
{
|
||||
ancount = 10;
|
||||
}
|
||||
/* Parsing the RRs from the reply packet */
|
||||
for (i = 0; i < ancount; ++i)
|
||||
{
|
||||
type[i] = 0;
|
||||
/* Parsing the NAME portion of the RR */
|
||||
temp = &packet[steps];
|
||||
j = 0;
|
||||
while (*temp != 0)
|
||||
{
|
||||
if ((uchar) (*temp) == 0xc0)
|
||||
{
|
||||
++temp;
|
||||
temp = &packet[(uint8_t) *temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
name[i][j] = *temp;
|
||||
++j;
|
||||
++temp;
|
||||
}
|
||||
}
|
||||
name[i][j] = '\0';
|
||||
|
||||
domain_decode(name[i]);
|
||||
steps = steps + 2;
|
||||
|
||||
/* Parsing the RR flags of the RR */
|
||||
rrflags = (RR_FLAGS *) &packet[steps];
|
||||
steps = steps + sizeof(RR_FLAGS) - 2;
|
||||
|
||||
/* Parsing the IPv4 address in the RR */
|
||||
if (ntohs(rrflags->type) == 1)
|
||||
{
|
||||
for (j = 0; j < ntohs(rrflags->rdlength); ++j)
|
||||
{
|
||||
rdata[i][j] = (uchar) packet[steps + j];
|
||||
}
|
||||
type[i] = ntohs(rrflags->type);
|
||||
}
|
||||
|
||||
/* Parsing the canonical name in the RR */
|
||||
if (ntohs(rrflags->type) == 5)
|
||||
{
|
||||
temp = &packet[steps];
|
||||
j = 0;
|
||||
while (*temp != 0)
|
||||
{
|
||||
if ((uchar)(*temp) == 0xc0)
|
||||
{
|
||||
++temp;
|
||||
temp = &packet[(uint8_t) *temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
rdata[i][j] = *temp;
|
||||
++j;
|
||||
++temp;
|
||||
}
|
||||
}
|
||||
rdata[i][j] = '\0';
|
||||
domain_decode((char *) rdata[i]);
|
||||
type[i] = ntohs(rrflags->type);
|
||||
}
|
||||
steps = steps + ntohs(rrflags->rdlength);
|
||||
}
|
||||
|
||||
char key[1024];
|
||||
int request_id = ntohs(header->id);
|
||||
int key_len = snprintf(key, sizeof(key), "%s-%d", _domain_name, request_id);
|
||||
swDNS_lookup_request *request = swHashMap_find(request_map, key, key_len);
|
||||
if (request == NULL)
|
||||
{
|
||||
swWarn("bad response, request_id=%d.", request_id);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
swDNSResolver_result result;
|
||||
bzero(&result, sizeof(result));
|
||||
|
||||
for (i = 0; i < ancount; ++i)
|
||||
{
|
||||
if (type[i] != SW_DNS_A_RECORD)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
j = result.num;
|
||||
result.num++;
|
||||
result.hosts[j].length = sprintf(result.hosts[j].address, "%d.%d.%d.%d", rdata[i][0], rdata[i][1], rdata[i][2], rdata[i][3]);
|
||||
if (result.num == SW_DNS_HOST_BUFFER_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
request->callback(request->domain, &result, request->data);
|
||||
swHashMap_del(request_map, key, key_len);
|
||||
sw_free(request->domain);
|
||||
sw_free(request);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swDNSResolver_request(char *domain, void (*callback)(char *, swDNSResolver_result *, void *), void *data)
|
||||
{
|
||||
char *_domain_name;
|
||||
Q_FLAGS *qflags = NULL;
|
||||
char packet[SW_BUFFER_SIZE_STD];
|
||||
char key[1024];
|
||||
swDNSResolver_header *header = NULL;
|
||||
int steps = 0;
|
||||
|
||||
if (SwooleG.dns_server_v4 == NULL)
|
||||
{
|
||||
if (swDNSResolver_get_server() < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
header = (swDNSResolver_header *) packet;
|
||||
header->id = htons(swoole_dns_request_id);
|
||||
header->qr = 0;
|
||||
header->opcode = 0;
|
||||
header->aa = 0;
|
||||
header->tc = 0;
|
||||
header->rd = 1;
|
||||
header->ra = 0;
|
||||
header->z = 0;
|
||||
header->rcode = 0;
|
||||
header->qdcount = htons(1);
|
||||
header->ancount = 0x0000;
|
||||
header->nscount = 0x0000;
|
||||
header->arcount = 0x0000;
|
||||
|
||||
steps = sizeof(swDNSResolver_header);
|
||||
|
||||
_domain_name = &packet[steps];
|
||||
|
||||
int len = strlen(domain);
|
||||
if (len >= sizeof(key))
|
||||
{
|
||||
swWarn("domain name is too long.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int key_len = snprintf(key, sizeof(key), "%s-%d", domain, swoole_dns_request_id);
|
||||
if (!request_map)
|
||||
{
|
||||
request_map = swHashMap_new(128, NULL);
|
||||
}
|
||||
else if (swHashMap_find(request_map, key, key_len))
|
||||
{
|
||||
swoole_error_log(SW_LOG_WARNING, SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST, "duplicate request.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swDNS_lookup_request *request = sw_malloc(sizeof(swDNS_lookup_request));
|
||||
if (request == NULL)
|
||||
{
|
||||
swWarn("malloc(%d) failed.", (int ) sizeof(swDNS_lookup_request));
|
||||
return SW_ERR;
|
||||
}
|
||||
request->domain = sw_strndup(domain, len + 1);
|
||||
if (request->domain == NULL)
|
||||
{
|
||||
swWarn("strdup(%d) failed.", len + 1);
|
||||
sw_free(request);
|
||||
return SW_ERR;
|
||||
}
|
||||
request->data = data;
|
||||
request->callback = callback;
|
||||
|
||||
if (domain_encode(request->domain, len, _domain_name) < 0)
|
||||
{
|
||||
swWarn("invalid domain[%s].", domain);
|
||||
sw_free(request->domain);
|
||||
sw_free(request);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
steps += (strlen((const char *) _domain_name) + 1);
|
||||
|
||||
qflags = (Q_FLAGS *) &packet[steps];
|
||||
qflags->qtype = htons(SW_DNS_A_RECORD);
|
||||
qflags->qclass = htons(0x0001);
|
||||
steps += sizeof(Q_FLAGS);
|
||||
|
||||
if (resolver_socket == NULL)
|
||||
{
|
||||
resolver_socket = sw_malloc(sizeof(swClient));
|
||||
if (resolver_socket == NULL)
|
||||
{
|
||||
sw_free(request->domain);
|
||||
sw_free(request);
|
||||
swWarn("malloc failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
if (swClient_create(resolver_socket, SW_SOCK_UDP, 0) < 0)
|
||||
{
|
||||
sw_free(resolver_socket);
|
||||
sw_free(request->domain);
|
||||
sw_free(request);
|
||||
return SW_ERR;
|
||||
}
|
||||
char *_port;
|
||||
int dns_server_port = SW_DNS_SERVER_PORT;
|
||||
char dns_server_host[32];
|
||||
strcpy(dns_server_host, SwooleG.dns_server_v4);
|
||||
if ((_port = strchr(SwooleG.dns_server_v4, ':')))
|
||||
{
|
||||
dns_server_port = atoi(_port + 1);
|
||||
dns_server_host[_port - SwooleG.dns_server_v4] = '\0';
|
||||
}
|
||||
if (resolver_socket->connect(resolver_socket, dns_server_host, dns_server_port, 1, 0) < 0)
|
||||
{
|
||||
do_close: resolver_socket->close(resolver_socket);
|
||||
swClient_free(resolver_socket);
|
||||
sw_free(resolver_socket);
|
||||
sw_free(request->domain);
|
||||
sw_free(request);
|
||||
resolver_socket = NULL;
|
||||
return SW_ERR;
|
||||
}
|
||||
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_DNS_RESOLVER, swDNSResolver_onReceive);
|
||||
if (SwooleG.main_reactor->add(SwooleG.main_reactor, resolver_socket->socket->fd, SW_FD_DNS_RESOLVER))
|
||||
{
|
||||
goto do_close;
|
||||
}
|
||||
}
|
||||
|
||||
if (resolver_socket->send(resolver_socket, (char *) packet, steps, 0) < 0)
|
||||
{
|
||||
goto do_close;
|
||||
}
|
||||
|
||||
swHashMap_add(request_map, key, key_len, request);
|
||||
swoole_dns_request_id++;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swDNSResolver_free()
|
||||
{
|
||||
if (resolver_socket == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (SwooleG.main_reactor == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (swHashMap_count(request_map) > 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
SwooleG.main_reactor->del(SwooleG.main_reactor, resolver_socket->socket->fd);
|
||||
resolver_socket->close(resolver_socket);
|
||||
swClient_free(resolver_socket);
|
||||
sw_free(resolver_socket);
|
||||
resolver_socket = NULL;
|
||||
swHashMap_free(request_map);
|
||||
request_map = NULL;
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function converts the dot-based hostname into the DNS format
|
||||
* (i.e. www.apple.com into 3www5apple3com0)
|
||||
*/
|
||||
static int domain_encode(char *src, int n, char *dest)
|
||||
{
|
||||
if (src[n] == '.')
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
int i;
|
||||
int len = 0;
|
||||
memcpy(dest + 1, src, n + 1);
|
||||
dest[n + 1] = '.';
|
||||
dest[n + 2] = 0;
|
||||
src = dest + 1;
|
||||
n++;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (src[i] == '.')
|
||||
{
|
||||
len = i - pos;
|
||||
dest[pos] = len;
|
||||
pos += len + 1;
|
||||
}
|
||||
}
|
||||
dest[pos] = 0;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function converts a DNS-based hostname into dot-based format
|
||||
* (i.e. 3www5apple3com0 into www.apple.com)
|
||||
*/
|
||||
static void domain_decode(char *str)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < strlen((const char*) str); i++)
|
||||
{
|
||||
unsigned int len = str[i];
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
str[i] = str[i + 1];
|
||||
i++;
|
||||
}
|
||||
str[i] = '.';
|
||||
}
|
||||
str[i - 1] = '\0';
|
||||
}
|
642
vendor/swoole/src/network/Manager.c
vendored
Executable file
642
vendor/swoole/src/network/Manager.c
vendored
Executable file
@ -0,0 +1,642 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Server.h"
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reloading;
|
||||
uint8_t reload_all_worker;
|
||||
uint8_t reload_task_worker;
|
||||
uint8_t read_message;
|
||||
uint8_t alarm;
|
||||
|
||||
} swManagerProcess;
|
||||
|
||||
static int swManager_loop(swFactory *factory);
|
||||
static void swManager_signal_handle(int sig);
|
||||
static pid_t swManager_spawn_worker(swFactory *factory, int worker_id);
|
||||
static void swManager_check_exit_status(swServer *serv, int worker_id, pid_t pid, int status);
|
||||
|
||||
static swManagerProcess ManagerProcess;
|
||||
|
||||
//create worker child proccess
|
||||
int swManager_start(swFactory *factory)
|
||||
{
|
||||
swFactoryProcess *object = factory->object;
|
||||
int i;
|
||||
pid_t pid;
|
||||
swServer *serv = factory->ptr;
|
||||
|
||||
object->pipes = sw_calloc(serv->worker_num, sizeof(swPipe));
|
||||
if (object->pipes == NULL)
|
||||
{
|
||||
swError("malloc[worker_pipes] failed. Error: %s [%d]", strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
//worker进程的pipes
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
if (swPipeUnsock_create(&object->pipes[i], 1, SOCK_DGRAM) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
serv->workers[i].pipe_master = object->pipes[i].getFd(&object->pipes[i], SW_PIPE_MASTER);
|
||||
serv->workers[i].pipe_worker = object->pipes[i].getFd(&object->pipes[i], SW_PIPE_WORKER);
|
||||
serv->workers[i].pipe_object = &object->pipes[i];
|
||||
swServer_store_pipe_fd(serv, serv->workers[i].pipe_object);
|
||||
}
|
||||
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
if (swServer_create_task_worker(serv) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swProcessPool *pool = &serv->gs->task_workers;
|
||||
swTaskWorker_init(pool);
|
||||
|
||||
swWorker *worker;
|
||||
for (i = 0; i < serv->task_worker_num; i++)
|
||||
{
|
||||
worker = &pool->workers[i];
|
||||
if (swWorker_create(worker) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (serv->task_ipc_mode == SW_TASK_IPC_UNIXSOCK)
|
||||
{
|
||||
swServer_store_pipe_fd(SwooleG.serv, worker->pipe_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//User Worker Process
|
||||
if (serv->user_worker_num > 0)
|
||||
{
|
||||
serv->user_workers = SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->user_worker_num * sizeof(swWorker));
|
||||
if (serv->user_workers == NULL)
|
||||
{
|
||||
swoole_error_log(SW_LOG_ERROR, SW_ERROR_SYSTEM_CALL_FAIL, "gmalloc[server->user_workers] failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
swUserWorker_node *user_worker;
|
||||
i = 0;
|
||||
LL_FOREACH(serv->user_worker_list, user_worker)
|
||||
{
|
||||
memcpy(&serv->user_workers[i], user_worker->worker, sizeof(swWorker));
|
||||
if (swWorker_create(&serv->user_workers[i]) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
serv->message_box = swChannel_new(65536, sizeof(swWorkerStopMessage), SW_CHAN_LOCK | SW_CHAN_SHM);
|
||||
if (serv->message_box == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
switch (pid)
|
||||
{
|
||||
//fork manager process
|
||||
case 0:
|
||||
//wait master process
|
||||
SW_START_SLEEP;
|
||||
if (serv->gs->start == 0)
|
||||
{
|
||||
return SW_OK;
|
||||
}
|
||||
swServer_close_listen_port(serv);
|
||||
|
||||
/**
|
||||
* create task worker process
|
||||
*/
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
swProcessPool_start(&serv->gs->task_workers);
|
||||
}
|
||||
/**
|
||||
* create worker process
|
||||
*/
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
//close(worker_pipes[i].pipes[0]);
|
||||
pid = swManager_spawn_worker(factory, i);
|
||||
if (pid < 0)
|
||||
{
|
||||
swError("fork() failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->workers[i].pid = pid;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* create user worker process
|
||||
*/
|
||||
if (serv->user_worker_list)
|
||||
{
|
||||
swUserWorker_node *user_worker;
|
||||
LL_FOREACH(serv->user_worker_list, user_worker)
|
||||
{
|
||||
/**
|
||||
* store the pipe object
|
||||
*/
|
||||
if (user_worker->worker->pipe_object)
|
||||
{
|
||||
swServer_store_pipe_fd(serv, user_worker->worker->pipe_object);
|
||||
}
|
||||
swManager_spawn_user_worker(serv, user_worker->worker);
|
||||
}
|
||||
}
|
||||
|
||||
SwooleG.process_type = SW_PROCESS_MANAGER;
|
||||
SwooleG.pid = getpid();
|
||||
exit(swManager_loop(factory));
|
||||
break;
|
||||
|
||||
//master process
|
||||
default:
|
||||
serv->gs->manager_pid = pid;
|
||||
break;
|
||||
case -1:
|
||||
swError("fork() failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static void swManager_check_exit_status(swServer *serv, int worker_id, pid_t pid, int status)
|
||||
{
|
||||
if (status != 0)
|
||||
{
|
||||
swWarn("worker#%d abnormal exit, status=%d, signal=%d", worker_id, WEXITSTATUS(status), WTERMSIG(status));
|
||||
if (serv->onWorkerError != NULL)
|
||||
{
|
||||
serv->onWorkerError(serv, worker_id, pid, WEXITSTATUS(status), WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int swManager_loop(swFactory *factory)
|
||||
{
|
||||
int pid, new_pid;
|
||||
int i;
|
||||
int reload_worker_i = 0;
|
||||
int reload_worker_num;
|
||||
int reload_init = 0;
|
||||
pid_t reload_worker_pid = 0;
|
||||
|
||||
int status;
|
||||
|
||||
SwooleG.use_signalfd = 0;
|
||||
SwooleG.use_timerfd = 0;
|
||||
|
||||
memset(&ManagerProcess, 0, sizeof(ManagerProcess));
|
||||
|
||||
swServer *serv = factory->ptr;
|
||||
swWorker *reload_workers;
|
||||
|
||||
if (serv->hooks[SW_SERVER_HOOK_MANAGER_START])
|
||||
{
|
||||
swServer_call_hook(serv, SW_SERVER_HOOK_MANAGER_START, serv);
|
||||
}
|
||||
|
||||
if (serv->onManagerStart)
|
||||
{
|
||||
serv->onManagerStart(serv);
|
||||
}
|
||||
|
||||
reload_worker_num = serv->worker_num + serv->task_worker_num;
|
||||
reload_workers = sw_calloc(reload_worker_num, sizeof(swWorker));
|
||||
if (reload_workers == NULL)
|
||||
{
|
||||
swError("malloc[reload_workers] failed");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
//for reload
|
||||
swSignal_add(SIGHUP, NULL);
|
||||
swSignal_add(SIGTERM, swManager_signal_handle);
|
||||
swSignal_add(SIGUSR1, swManager_signal_handle);
|
||||
swSignal_add(SIGUSR2, swManager_signal_handle);
|
||||
swSignal_add(SIGIO, swManager_signal_handle);
|
||||
#ifdef SIGRTMIN
|
||||
swSignal_add(SIGRTMIN, swManager_signal_handle);
|
||||
#endif
|
||||
//swSignal_add(SIGINT, swManager_signal_handle);
|
||||
|
||||
if (serv->manager_alarm > 0)
|
||||
{
|
||||
alarm(serv->manager_alarm);
|
||||
swSignal_add(SIGALRM, swManager_signal_handle);
|
||||
}
|
||||
|
||||
SwooleG.main_reactor = NULL;
|
||||
|
||||
while (SwooleG.running > 0)
|
||||
{
|
||||
_wait: pid = wait(&status);
|
||||
|
||||
if (ManagerProcess.read_message)
|
||||
{
|
||||
swWorkerStopMessage msg;
|
||||
while (swChannel_pop(serv->message_box, &msg, sizeof(msg)) > 0)
|
||||
{
|
||||
if (SwooleG.running == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
pid_t new_pid = swManager_spawn_worker(factory, msg.worker_id);
|
||||
if (new_pid > 0)
|
||||
{
|
||||
serv->workers[msg.worker_id].pid = new_pid;
|
||||
}
|
||||
}
|
||||
ManagerProcess.read_message = 0;
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
if (ManagerProcess.alarm == 1)
|
||||
{
|
||||
ManagerProcess.alarm = 0;
|
||||
alarm(serv->manager_alarm);
|
||||
|
||||
if (serv->hooks[SW_SERVER_HOOK_MANAGER_TIMER])
|
||||
{
|
||||
swServer_call_hook(serv, SW_SERVER_HOOK_MANAGER_TIMER, serv);
|
||||
}
|
||||
}
|
||||
|
||||
if (ManagerProcess.reloading == 0)
|
||||
{
|
||||
error: if (errno != EINTR)
|
||||
{
|
||||
swSysError("wait() failed.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//reload task & event workers
|
||||
else if (ManagerProcess.reload_all_worker == 1)
|
||||
{
|
||||
swNotice("Server is reloading now.");
|
||||
if (reload_init == 0)
|
||||
{
|
||||
reload_init = 1;
|
||||
memcpy(reload_workers, serv->workers, sizeof(swWorker) * serv->worker_num);
|
||||
reload_worker_num = serv->worker_num;
|
||||
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
memcpy(reload_workers + serv->worker_num, serv->gs->task_workers.workers,
|
||||
sizeof(swWorker) * serv->task_worker_num);
|
||||
reload_worker_num += serv->task_worker_num;
|
||||
}
|
||||
|
||||
ManagerProcess.reload_all_worker = 0;
|
||||
if (serv->reload_async)
|
||||
{
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
if (kill(reload_workers[i].pid, SIGTERM) < 0)
|
||||
{
|
||||
swSysError("kill(%d, SIGTERM) [%d] failed.", reload_workers[i].pid, i);
|
||||
}
|
||||
}
|
||||
reload_worker_i = serv->worker_num;
|
||||
}
|
||||
else
|
||||
{
|
||||
reload_worker_i = 0;
|
||||
}
|
||||
}
|
||||
goto kill_worker;
|
||||
}
|
||||
//only reload task workers
|
||||
else if (ManagerProcess.reload_task_worker == 1)
|
||||
{
|
||||
if (serv->task_worker_num == 0)
|
||||
{
|
||||
swWarn("cannot reload task workers, task workers is not started.");
|
||||
continue;
|
||||
}
|
||||
swNotice("Server is reloading now.");
|
||||
if (reload_init == 0)
|
||||
{
|
||||
memcpy(reload_workers, serv->gs->task_workers.workers, sizeof(swWorker) * serv->task_worker_num);
|
||||
reload_worker_num = serv->task_worker_num;
|
||||
reload_worker_i = 0;
|
||||
reload_init = 1;
|
||||
ManagerProcess.reload_task_worker = 0;
|
||||
}
|
||||
goto kill_worker;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (SwooleG.running == 1)
|
||||
{
|
||||
//event workers
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
//compare PID
|
||||
if (pid != serv->workers[i].pid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(status) && serv->workers[i].tracer)
|
||||
{
|
||||
serv->workers[i].tracer(&serv->workers[i]);
|
||||
serv->workers[i].tracer = NULL;
|
||||
goto _wait;
|
||||
}
|
||||
|
||||
//Check the process return code and signal
|
||||
swManager_check_exit_status(serv, i, pid, status);
|
||||
|
||||
while (1)
|
||||
{
|
||||
new_pid = swManager_spawn_worker(factory, i);
|
||||
if (new_pid < 0)
|
||||
{
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->workers[i].pid = new_pid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
swWorker *exit_worker;
|
||||
//task worker
|
||||
if (serv->gs->task_workers.map)
|
||||
{
|
||||
exit_worker = swHashMap_find_int(serv->gs->task_workers.map, pid);
|
||||
if (exit_worker != NULL)
|
||||
{
|
||||
if (WIFSTOPPED(status) && exit_worker->tracer)
|
||||
{
|
||||
exit_worker->tracer(exit_worker);
|
||||
exit_worker->tracer = NULL;
|
||||
goto _wait;
|
||||
}
|
||||
swManager_check_exit_status(serv, exit_worker->id, pid, status);
|
||||
swProcessPool_spawn(&serv->gs->task_workers, exit_worker);
|
||||
}
|
||||
}
|
||||
//user process
|
||||
if (serv->user_worker_map != NULL)
|
||||
{
|
||||
swManager_wait_user_worker(&serv->gs->event_workers, pid, status);
|
||||
}
|
||||
if (pid == reload_worker_pid)
|
||||
{
|
||||
reload_worker_i++;
|
||||
}
|
||||
}
|
||||
//reload worker
|
||||
kill_worker: if (ManagerProcess.reloading == 1)
|
||||
{
|
||||
//reload finish
|
||||
if (reload_worker_i >= reload_worker_num)
|
||||
{
|
||||
reload_worker_pid = reload_worker_i = reload_init = ManagerProcess.reloading = 0;
|
||||
continue;
|
||||
}
|
||||
reload_worker_pid = reload_workers[reload_worker_i].pid;
|
||||
if (kill(reload_worker_pid, SIGTERM) < 0)
|
||||
{
|
||||
if (errno == ECHILD)
|
||||
{
|
||||
reload_worker_i++;
|
||||
goto kill_worker;
|
||||
}
|
||||
swSysError("kill(%d, SIGTERM) [%d] failed.", reload_workers[reload_worker_i].pid, reload_worker_i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sw_free(reload_workers);
|
||||
swSignal_none();
|
||||
//kill all child process
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
swTrace("[Manager]kill worker processor");
|
||||
kill(serv->workers[i].pid, SIGTERM);
|
||||
}
|
||||
//kill and wait task process
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
swProcessPool_shutdown(&serv->gs->task_workers);
|
||||
}
|
||||
//wait child process
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
if (swWaitpid(serv->workers[i].pid, &status, 0) < 0)
|
||||
{
|
||||
swSysError("waitpid(%d) failed.", serv->workers[i].pid);
|
||||
}
|
||||
}
|
||||
//kill all user process
|
||||
if (serv->user_worker_map)
|
||||
{
|
||||
swManager_kill_user_worker(serv);
|
||||
}
|
||||
|
||||
if (serv->onManagerStop)
|
||||
{
|
||||
serv->onManagerStop(serv);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static pid_t swManager_spawn_worker(swFactory *factory, int worker_id)
|
||||
{
|
||||
pid_t pid;
|
||||
int ret;
|
||||
|
||||
pid = fork();
|
||||
|
||||
//fork() failed
|
||||
if (pid < 0)
|
||||
{
|
||||
swWarn("Fork Worker failed. Error: %s [%d]", strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
//worker child processor
|
||||
else if (pid == 0)
|
||||
{
|
||||
ret = swWorker_loop(factory, worker_id);
|
||||
exit(ret);
|
||||
}
|
||||
//parent,add to writer
|
||||
else
|
||||
{
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
static void swManager_signal_handle(int sig)
|
||||
{
|
||||
switch (sig)
|
||||
{
|
||||
case SIGTERM:
|
||||
SwooleG.running = 0;
|
||||
break;
|
||||
/**
|
||||
* reload all workers
|
||||
*/
|
||||
case SIGUSR1:
|
||||
if (ManagerProcess.reloading == 0)
|
||||
{
|
||||
ManagerProcess.reloading = 1;
|
||||
ManagerProcess.reload_all_worker = 1;
|
||||
}
|
||||
break;
|
||||
/**
|
||||
* only reload task workers
|
||||
*/
|
||||
case SIGUSR2:
|
||||
if (ManagerProcess.reloading == 0)
|
||||
{
|
||||
ManagerProcess.reloading = 1;
|
||||
ManagerProcess.reload_task_worker = 1;
|
||||
}
|
||||
break;
|
||||
case SIGIO:
|
||||
ManagerProcess.read_message = 1;
|
||||
break;
|
||||
case SIGALRM:
|
||||
ManagerProcess.alarm = 1;
|
||||
break;
|
||||
default:
|
||||
#ifdef SIGRTMIN
|
||||
if (sig == SIGRTMIN)
|
||||
{
|
||||
swServer_reopen_log_file(SwooleG.serv);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int swManager_wait_user_worker(swProcessPool *pool, pid_t pid, int status)
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
swWorker *exit_worker = swHashMap_find_int(serv->user_worker_map, pid);
|
||||
if (exit_worker != NULL)
|
||||
{
|
||||
swManager_check_exit_status(serv, exit_worker->id, pid, status);
|
||||
return swManager_spawn_user_worker(serv, exit_worker);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
void swManager_kill_user_worker(swServer *serv)
|
||||
{
|
||||
if (!serv->user_worker_map)
|
||||
{
|
||||
return;
|
||||
}
|
||||
swWorker* user_worker;
|
||||
uint64_t key;
|
||||
int __stat_loc;
|
||||
|
||||
//kill user process
|
||||
while (1)
|
||||
{
|
||||
user_worker = swHashMap_each_int(serv->user_worker_map, &key);
|
||||
//hashmap empty
|
||||
if (user_worker == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
kill(user_worker->pid, SIGTERM);
|
||||
}
|
||||
|
||||
//wait user process
|
||||
while (1)
|
||||
{
|
||||
user_worker = swHashMap_each_int(serv->user_worker_map, &key);
|
||||
//hashmap empty
|
||||
if (user_worker == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (swWaitpid(user_worker->pid, &__stat_loc, 0) < 0)
|
||||
{
|
||||
swSysError("waitpid(%d) failed.", user_worker->pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pid_t swManager_spawn_user_worker(swServer *serv, swWorker* worker)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
swWarn("Fork Worker failed. Error: %s [%d]", strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
//child
|
||||
else if (pid == 0)
|
||||
{
|
||||
SwooleG.process_type = SW_PROCESS_USERWORKER;
|
||||
SwooleWG.worker = worker;
|
||||
SwooleWG.id = worker->id;
|
||||
worker->pid = getpid();
|
||||
//close tcp listen socket
|
||||
if (serv->factory_mode == SW_MODE_SINGLE)
|
||||
{
|
||||
swServer_close_port(serv, SW_TRUE);
|
||||
}
|
||||
serv->onUserWorkerStart(serv, worker);
|
||||
exit(0);
|
||||
}
|
||||
//parent
|
||||
else
|
||||
{
|
||||
if (worker->pid)
|
||||
{
|
||||
swHashMap_del_int(serv->user_worker_map, worker->pid);
|
||||
}
|
||||
worker->pid = pid;
|
||||
swHashMap_add_int(serv->user_worker_map, pid, worker);
|
||||
return pid;
|
||||
}
|
||||
}
|
757
vendor/swoole/src/network/Port.c
vendored
Executable file
757
vendor/swoole/src/network/Port.c
vendored
Executable file
@ -0,0 +1,757 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Server.h"
|
||||
#include "http.h"
|
||||
#include "http2.h"
|
||||
#include "websocket.h"
|
||||
#include "mqtt.h"
|
||||
#include "redis.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int swPort_onRead_raw(swReactor *reactor, swListenPort *lp, swEvent *event);
|
||||
static int swPort_onRead_check_length(swReactor *reactor, swListenPort *lp, swEvent *event);
|
||||
static int swPort_onRead_check_eof(swReactor *reactor, swListenPort *lp, swEvent *event);
|
||||
static int swPort_onRead_http(swReactor *reactor, swListenPort *lp, swEvent *event);
|
||||
static int swPort_onRead_redis(swReactor *reactor, swListenPort *lp, swEvent *event);
|
||||
static int swPort_http_static_handler(swHttpRequest *request, swConnection *conn);
|
||||
|
||||
void swPort_init(swListenPort *port)
|
||||
{
|
||||
port->sock = 0;
|
||||
port->ssl = 0;
|
||||
|
||||
//listen backlog
|
||||
port->backlog = SW_BACKLOG;
|
||||
//tcp keepalive
|
||||
port->tcp_keepcount = SW_TCP_KEEPCOUNT;
|
||||
port->tcp_keepinterval = SW_TCP_KEEPINTERVAL;
|
||||
port->tcp_keepidle = SW_TCP_KEEPIDLE;
|
||||
port->open_tcp_nopush = 1;
|
||||
|
||||
port->protocol.package_length_type = 'N';
|
||||
port->protocol.package_length_size = 4;
|
||||
port->protocol.package_body_offset = 4;
|
||||
port->protocol.package_max_length = SW_BUFFER_INPUT_SIZE;
|
||||
|
||||
port->socket_buffer_size = SwooleG.socket_buffer_size;
|
||||
|
||||
char eof[] = SW_DATA_EOF;
|
||||
port->protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1;
|
||||
memcpy(port->protocol.package_eof, eof, port->protocol.package_eof_len);
|
||||
}
|
||||
|
||||
#ifdef SW_USE_OPENSSL
|
||||
int swPort_enable_ssl_encrypt(swListenPort *ls)
|
||||
{
|
||||
if (ls->ssl_option.cert_file == NULL || ls->ssl_option.key_file == NULL)
|
||||
{
|
||||
swWarn("SSL error, require ssl_cert_file and ssl_key_file.");
|
||||
return SW_ERR;
|
||||
}
|
||||
ls->ssl_context = swSSL_get_context(&ls->ssl_option);
|
||||
if (ls->ssl_context == NULL)
|
||||
{
|
||||
swWarn("swSSL_get_context() error.");
|
||||
return SW_ERR;
|
||||
}
|
||||
/**
|
||||
* OpenSSL thread-safe
|
||||
*/
|
||||
swSSL_init_thread_safety();
|
||||
|
||||
if (ls->ssl_option.client_cert_file
|
||||
&& swSSL_set_client_certificate(ls->ssl_context, ls->ssl_option.client_cert_file,
|
||||
ls->ssl_option.verify_depth) == SW_ERR)
|
||||
{
|
||||
swWarn("swSSL_set_client_certificate() error.");
|
||||
return SW_ERR;
|
||||
}
|
||||
if (ls->open_http_protocol)
|
||||
{
|
||||
ls->ssl_config.http = 1;
|
||||
}
|
||||
if (ls->open_http2_protocol)
|
||||
{
|
||||
ls->ssl_config.http_v2 = 1;
|
||||
swSSL_server_http_advise(ls->ssl_context, &ls->ssl_config);
|
||||
}
|
||||
if (swSSL_server_set_cipher(ls->ssl_context, &ls->ssl_config) < 0)
|
||||
{
|
||||
swWarn("swSSL_server_set_cipher() error.");
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int swPort_listen(swListenPort *ls)
|
||||
{
|
||||
int sock = ls->sock;
|
||||
int option = 1;
|
||||
|
||||
//listen stream socket
|
||||
if (listen(sock, ls->backlog) < 0)
|
||||
{
|
||||
swWarn("listen(%s:%d, %d) failed. Error: %s[%d]", ls->host, ls->port, ls->backlog, strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
#ifdef TCP_DEFER_ACCEPT
|
||||
if (ls->tcp_defer_accept)
|
||||
{
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (const void*) &ls->tcp_defer_accept, sizeof(int)) < 0)
|
||||
{
|
||||
swSysError("setsockopt(TCP_DEFER_ACCEPT) failed.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TCP_FASTOPEN
|
||||
if (ls->tcp_fastopen)
|
||||
{
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, (const void*) &ls->tcp_fastopen, sizeof(int)) < 0)
|
||||
{
|
||||
swSysError("setsockopt(TCP_FASTOPEN) failed.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SO_KEEPALIVE
|
||||
if (ls->open_tcp_keepalive == 1)
|
||||
{
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &option, sizeof(option)) < 0)
|
||||
{
|
||||
swSysError("setsockopt(SO_KEEPALIVE) failed.");
|
||||
}
|
||||
#ifdef TCP_KEEPIDLE
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &ls->tcp_keepidle, sizeof(int));
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void *) &ls->tcp_keepinterval, sizeof(int));
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void *) &ls->tcp_keepcount, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
ls->buffer_high_watermark = ls->socket_buffer_size * 0.8;
|
||||
ls->buffer_low_watermark = 0;
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
|
||||
void swPort_set_protocol(swListenPort *ls)
|
||||
{
|
||||
//Thread mode must copy the data.
|
||||
//will free after onFinish
|
||||
if (ls->open_eof_check)
|
||||
{
|
||||
if (ls->protocol.package_eof_len > sizeof(ls->protocol.package_eof))
|
||||
{
|
||||
ls->protocol.package_eof_len = sizeof(ls->protocol.package_eof);
|
||||
}
|
||||
ls->protocol.onPackage = swReactorThread_dispatch;
|
||||
ls->onRead = swPort_onRead_check_eof;
|
||||
}
|
||||
else if (ls->open_length_check)
|
||||
{
|
||||
if (ls->protocol.package_length_type != '\0')
|
||||
{
|
||||
ls->protocol.get_package_length = swProtocol_get_package_length;
|
||||
}
|
||||
ls->protocol.onPackage = swReactorThread_dispatch;
|
||||
ls->onRead = swPort_onRead_check_length;
|
||||
}
|
||||
else if (ls->open_http_protocol)
|
||||
{
|
||||
if (ls->open_websocket_protocol)
|
||||
{
|
||||
ls->protocol.get_package_length = swWebSocket_get_package_length;
|
||||
ls->protocol.onPackage = swWebSocket_dispatch_frame;
|
||||
ls->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t);
|
||||
}
|
||||
#ifdef SW_USE_HTTP2
|
||||
else if (ls->open_http2_protocol)
|
||||
{
|
||||
ls->protocol.get_package_length = swHttp2_get_frame_length;
|
||||
ls->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;
|
||||
ls->protocol.onPackage = swReactorThread_dispatch;
|
||||
}
|
||||
#endif
|
||||
ls->onRead = swPort_onRead_http;
|
||||
}
|
||||
else if (ls->open_mqtt_protocol)
|
||||
{
|
||||
ls->protocol.get_package_length = swMqtt_get_package_length;
|
||||
ls->protocol.onPackage = swReactorThread_dispatch;
|
||||
ls->onRead = swPort_onRead_check_length;
|
||||
}
|
||||
else if (ls->open_redis_protocol)
|
||||
{
|
||||
ls->protocol.onPackage = swReactorThread_dispatch;
|
||||
ls->onRead = swPort_onRead_redis;
|
||||
}
|
||||
else
|
||||
{
|
||||
ls->onRead = swPort_onRead_raw;
|
||||
}
|
||||
}
|
||||
|
||||
void swPort_clear_protocol(swListenPort *ls)
|
||||
{
|
||||
ls->open_eof_check = 0;
|
||||
ls->open_length_check = 0;
|
||||
ls->open_http_protocol = 0;
|
||||
ls->open_websocket_protocol = 0;
|
||||
#ifdef SW_USE_HTTP2
|
||||
ls->open_http2_protocol = 0;
|
||||
#endif
|
||||
ls->open_mqtt_protocol = 0;
|
||||
ls->open_redis_protocol = 0;
|
||||
}
|
||||
|
||||
static int swPort_onRead_raw(swReactor *reactor, swListenPort *port, swEvent *event)
|
||||
{
|
||||
int n;
|
||||
swDispatchData task;
|
||||
swConnection *conn = event->socket;
|
||||
|
||||
n = swConnection_recv(conn, task.data.data, SW_BUFFER_SIZE, 0);
|
||||
if (n < 0)
|
||||
{
|
||||
switch (swConnection_error(errno))
|
||||
{
|
||||
case SW_ERROR:
|
||||
swSysError("recv from connection#%d failed.", event->fd);
|
||||
return SW_OK;
|
||||
case SW_CLOSE:
|
||||
conn->close_errno = errno;
|
||||
goto close_fd;
|
||||
default:
|
||||
return SW_OK;
|
||||
}
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
close_fd: swReactorThread_onClose(reactor, event);
|
||||
return SW_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
task.data.info.fd = event->fd;
|
||||
task.data.info.from_id = event->from_id;
|
||||
task.data.info.len = n;
|
||||
task.data.info.type = SW_EVENT_TCP;
|
||||
task.target_worker_id = -1;
|
||||
return swReactorThread_dispatch(conn, task.data.data, task.data.info.len);
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
|
||||
static int swPort_onRead_check_length(swReactor *reactor, swListenPort *port, swEvent *event)
|
||||
{
|
||||
swServer *serv = reactor->ptr;
|
||||
swConnection *conn = event->socket;
|
||||
swProtocol *protocol = &port->protocol;
|
||||
|
||||
swString *buffer = swServer_get_buffer(serv, event->fd);
|
||||
if (!buffer)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (swProtocol_recv_check_length(protocol, conn, buffer) < 0)
|
||||
{
|
||||
swTrace("Close Event.FD=%d|From=%d", event->fd, event->from_id);
|
||||
swReactorThread_onClose(reactor, event);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* For Http Protocol
|
||||
*/
|
||||
static int swPort_onRead_http(swReactor *reactor, swListenPort *port, swEvent *event)
|
||||
{
|
||||
swConnection *conn = event->socket;
|
||||
swServer *serv = reactor->ptr;
|
||||
|
||||
if (conn->websocket_status >= WEBSOCKET_STATUS_HANDSHAKE)
|
||||
{
|
||||
if (conn->http_upgrade == 0)
|
||||
{
|
||||
swHttpRequest_free(conn);
|
||||
conn->websocket_status = WEBSOCKET_STATUS_ACTIVE;
|
||||
conn->http_upgrade = 1;
|
||||
}
|
||||
return swPort_onRead_check_length(reactor, port, event);
|
||||
}
|
||||
|
||||
#ifdef SW_USE_HTTP2
|
||||
if (conn->http2_stream)
|
||||
{
|
||||
return swPort_onRead_check_length(reactor, port, event);
|
||||
}
|
||||
#endif
|
||||
|
||||
int n = 0;
|
||||
char *buf;
|
||||
int buf_len;
|
||||
|
||||
swHttpRequest *request = NULL;
|
||||
swProtocol *protocol = &port->protocol;
|
||||
|
||||
//new http request
|
||||
if (conn->object == NULL)
|
||||
{
|
||||
request = sw_malloc(sizeof(swHttpRequest));
|
||||
bzero(request, sizeof(swHttpRequest));
|
||||
conn->object = request;
|
||||
}
|
||||
else
|
||||
{
|
||||
request = (swHttpRequest *) conn->object;
|
||||
}
|
||||
|
||||
if (!request->buffer)
|
||||
{
|
||||
request->buffer = swString_new(SW_HTTP_HEADER_MAX_SIZE);
|
||||
//alloc memory failed.
|
||||
if (!request->buffer)
|
||||
{
|
||||
swReactorThread_onClose(reactor, event);
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
swString *buffer = request->buffer;
|
||||
|
||||
recv_data:
|
||||
buf = buffer->str + buffer->length;
|
||||
buf_len = buffer->size - buffer->length;
|
||||
|
||||
n = swConnection_recv(conn, buf, buf_len, 0);
|
||||
if (n < 0)
|
||||
{
|
||||
switch (swConnection_error(errno))
|
||||
{
|
||||
case SW_ERROR:
|
||||
swSysError("recv from connection#%d failed.", event->fd);
|
||||
return SW_OK;
|
||||
case SW_CLOSE:
|
||||
conn->close_errno = errno;
|
||||
goto close_fd;
|
||||
default:
|
||||
return SW_OK;
|
||||
}
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
close_fd:
|
||||
swHttpRequest_free(conn);
|
||||
swReactorThread_onClose(reactor, event);
|
||||
return SW_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->length += n;
|
||||
|
||||
if (request->method == 0 && swHttpRequest_get_protocol(request) < 0)
|
||||
{
|
||||
if (request->excepted == 0 && request->buffer->length < SW_HTTP_HEADER_MAX_SIZE)
|
||||
{
|
||||
return SW_OK;
|
||||
}
|
||||
swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "get protocol failed.");
|
||||
#ifdef SW_HTTP_BAD_REQUEST
|
||||
if (swConnection_send(conn, SW_STRL(SW_HTTP_BAD_REQUEST) - 1, 0) < 0)
|
||||
{
|
||||
swSysError("send() failed.");
|
||||
}
|
||||
#endif
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
if (request->method > HTTP_PRI)
|
||||
{
|
||||
swWarn("method no support");
|
||||
goto close_fd;
|
||||
}
|
||||
#ifdef SW_USE_HTTP2
|
||||
else if (request->method == HTTP_PRI)
|
||||
{
|
||||
conn->http2_stream = 1;
|
||||
swHttp2_send_setting_frame(protocol, conn);
|
||||
if (n == sizeof(SW_HTTP2_PRI_STRING) - 1)
|
||||
{
|
||||
swHttpRequest_free(conn);
|
||||
return SW_OK;
|
||||
}
|
||||
swHttp2_parse_frame(protocol, conn, buf + (sizeof(SW_HTTP2_PRI_STRING) - 1), n - (sizeof(SW_HTTP2_PRI_STRING) - 1));
|
||||
swHttpRequest_free(conn);
|
||||
return SW_OK;
|
||||
}
|
||||
#endif
|
||||
//http header is not the end
|
||||
if (request->header_length == 0)
|
||||
{
|
||||
if (swHttpRequest_get_header_length(request) < 0)
|
||||
{
|
||||
if (buffer->size == buffer->length)
|
||||
{
|
||||
swWarn("[2]http header is too long.");
|
||||
goto close_fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto recv_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//http body
|
||||
if (request->content_length == 0)
|
||||
{
|
||||
if (swHttpRequest_get_content_length(request) < 0)
|
||||
{
|
||||
if (memcmp(buffer->str + buffer->length - 4, "\r\n\r\n", 4) == 0)
|
||||
{
|
||||
/**
|
||||
* send static file content directly in the reactor thread
|
||||
*/
|
||||
if (!(serv->enable_static_handler && swPort_http_static_handler(request, conn)))
|
||||
{
|
||||
/**
|
||||
* dynamic request, dispatch to worker
|
||||
*/
|
||||
swReactorThread_dispatch(conn, buffer->str, buffer->length);
|
||||
}
|
||||
swHttpRequest_free(conn);
|
||||
return SW_OK;
|
||||
}
|
||||
else if (buffer->size == buffer->length)
|
||||
{
|
||||
swWarn("[0]http header is too long.");
|
||||
goto close_fd;
|
||||
}
|
||||
//wait more data
|
||||
else
|
||||
{
|
||||
goto recv_data;
|
||||
}
|
||||
}
|
||||
else if (request->content_length > (protocol->package_max_length - SW_HTTP_HEADER_MAX_SIZE))
|
||||
{
|
||||
swWarn("Content-Length is too big, MaxSize=[%d].", protocol->package_max_length - SW_HTTP_HEADER_MAX_SIZE);
|
||||
goto close_fd;
|
||||
}
|
||||
}
|
||||
|
||||
//total length
|
||||
uint32_t request_size = request->header_length + request->content_length;
|
||||
if (request_size > buffer->size && swString_extend(buffer, request_size) < 0)
|
||||
{
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
//discard the redundant data
|
||||
if (buffer->length > request_size)
|
||||
{
|
||||
buffer->length = request_size;
|
||||
}
|
||||
|
||||
if (buffer->length == request_size)
|
||||
{
|
||||
swReactorThread_dispatch(conn, buffer->str, buffer->length);
|
||||
swHttpRequest_free(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef SW_HTTP_100_CONTINUE
|
||||
//Expect: 100-continue
|
||||
if (swHttpRequest_has_expect_header(request))
|
||||
{
|
||||
swSendData _send;
|
||||
_send.data = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
_send.length = strlen(_send.data);
|
||||
|
||||
int send_times = 0;
|
||||
direct_send:
|
||||
n = swConnection_send(conn, _send.data, _send.length, 0);
|
||||
if (n < _send.length)
|
||||
{
|
||||
_send.data += n;
|
||||
_send.length -= n;
|
||||
send_times++;
|
||||
if (send_times < 10)
|
||||
{
|
||||
goto direct_send;
|
||||
}
|
||||
else
|
||||
{
|
||||
swWarn("send http header failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
swTrace("PostWait: request->content_length=%d, buffer->length=%zd, request->header_length=%d\n",
|
||||
request->content_length, buffer->length, request->header_length);
|
||||
}
|
||||
#endif
|
||||
goto recv_data;
|
||||
}
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swPort_onRead_redis(swReactor *reactor, swListenPort *port, swEvent *event)
|
||||
{
|
||||
swConnection *conn = event->socket;
|
||||
swProtocol *protocol = &port->protocol;
|
||||
swServer *serv = reactor->ptr;
|
||||
|
||||
swString *buffer = swServer_get_buffer(serv, event->fd);
|
||||
if (!buffer)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (swRedis_recv(protocol, conn, buffer) < 0)
|
||||
{
|
||||
swReactorThread_onClose(reactor, event);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swPort_onRead_check_eof(swReactor *reactor, swListenPort *port, swEvent *event)
|
||||
{
|
||||
swConnection *conn = event->socket;
|
||||
swProtocol *protocol = &port->protocol;
|
||||
swServer *serv = reactor->ptr;
|
||||
|
||||
swString *buffer = swServer_get_buffer(serv, event->fd);
|
||||
if (!buffer)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (swProtocol_recv_check_eof(protocol, conn, buffer) < 0)
|
||||
{
|
||||
swReactorThread_onClose(reactor, event);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
void swPort_free(swListenPort *port)
|
||||
{
|
||||
#ifdef SW_USE_OPENSSL
|
||||
if (port->ssl)
|
||||
{
|
||||
swSSL_free_context(port->ssl_context);
|
||||
sw_free(port->ssl_option.cert_file);
|
||||
sw_free(port->ssl_option.key_file);
|
||||
if (port->ssl_option.client_cert_file)
|
||||
{
|
||||
sw_free(port->ssl_option.client_cert_file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
close(port->sock);
|
||||
|
||||
//remove unix socket file
|
||||
if (port->type == SW_SOCK_UNIX_STREAM || port->type == SW_SOCK_UNIX_DGRAM)
|
||||
{
|
||||
unlink(port->host);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
int swPort_http_static_handler(swHttpRequest *request, swConnection *conn)
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
char *url = request->buffer->str + request->url_offset;
|
||||
char *params = memchr(url, '?', request->url_length);
|
||||
|
||||
struct
|
||||
{
|
||||
off_t offset;
|
||||
size_t length;
|
||||
char filename[PATH_MAX];
|
||||
} buffer;
|
||||
|
||||
char *p = buffer.filename;
|
||||
|
||||
memcpy(p, serv->document_root, serv->document_root_len);
|
||||
p += serv->document_root_len;
|
||||
uint32_t n = params ? params - url : request->url_length;
|
||||
memcpy(p, url, n);
|
||||
p += n;
|
||||
*p = 0;
|
||||
|
||||
struct stat file_stat;
|
||||
if (lstat(buffer.filename, &file_stat) < 0)
|
||||
{
|
||||
return SW_FALSE;
|
||||
}
|
||||
if ((file_stat.st_mode & S_IFMT) != S_IFREG)
|
||||
{
|
||||
return SW_FALSE;
|
||||
}
|
||||
|
||||
char header_buffer[1024];
|
||||
swSendData response;
|
||||
response.info.fd = conn->session_id;
|
||||
|
||||
response.info.type = SW_EVENT_TCP;
|
||||
|
||||
p = request->buffer->str + request->url_offset + request->url_length + 10;
|
||||
char *pe = request->buffer->str + request->header_length;
|
||||
|
||||
char *date_if_modified_since = NULL;
|
||||
int length_if_modified_since = 0;
|
||||
|
||||
int state = 0;
|
||||
for (; p < pe; p++)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case 0:
|
||||
if (strncasecmp(p, SW_STRL("If-Modified-Since") - 1) == 0)
|
||||
{
|
||||
p += sizeof("If-Modified-Since");
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!isspace(*p))
|
||||
{
|
||||
date_if_modified_since = p;
|
||||
state = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strncasecmp(p, SW_STRL("\r\n") - 1) == 0)
|
||||
{
|
||||
length_if_modified_since = p - date_if_modified_since;
|
||||
goto check_modify_date;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char date_[64];
|
||||
struct tm *tm1;
|
||||
|
||||
check_modify_date: tm1 = gmtime(&serv->gs->now);
|
||||
strftime(date_, sizeof(date_), "%a, %d %b %Y %H:%M:%S %Z", tm1);
|
||||
|
||||
char date_last_modified[64];
|
||||
#ifdef __MACH__
|
||||
time_t file_mtime = file_stat.st_mtimespec.tv_sec;
|
||||
#else
|
||||
time_t file_mtime = file_stat.st_mtim.tv_sec;
|
||||
#endif
|
||||
|
||||
struct tm *tm2 = gmtime(&file_mtime);
|
||||
strftime(date_last_modified, sizeof(date_last_modified), "%a, %d %b %Y %H:%M:%S %Z", tm2);
|
||||
|
||||
if (state == 2)
|
||||
{
|
||||
struct tm tm3;
|
||||
char date_tmp[64];
|
||||
memcpy(date_tmp, date_if_modified_since, length_if_modified_since);
|
||||
date_tmp[length_if_modified_since] = 0;
|
||||
|
||||
char *date_format = NULL;
|
||||
|
||||
if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_GMT, &tm3) != NULL)
|
||||
{
|
||||
date_format = SW_HTTP_RFC1123_DATE_GMT;
|
||||
}
|
||||
else if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_UTC, &tm3) != NULL)
|
||||
{
|
||||
date_format = SW_HTTP_RFC1123_DATE_UTC;
|
||||
}
|
||||
else if (strptime(date_tmp, SW_HTTP_RFC850_DATE, &tm3) != NULL)
|
||||
{
|
||||
date_format = SW_HTTP_RFC850_DATE;
|
||||
}
|
||||
else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != NULL)
|
||||
{
|
||||
date_format = SW_HTTP_ASCTIME_DATE;
|
||||
}
|
||||
if (date_format && mktime(&tm3) - (int) timezone >= file_mtime)
|
||||
{
|
||||
response.length = response.info.len = snprintf(header_buffer, sizeof(header_buffer),
|
||||
"HTTP/1.1 304 Not Modified\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Last-Modified: %s\r\n"
|
||||
"Server: %s\r\n\r\n", date_, date_last_modified,
|
||||
SW_HTTP_SERVER_SOFTWARE);
|
||||
response.data = header_buffer;
|
||||
swReactorThread_send(&response);
|
||||
return SW_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
response.length = response.info.len = snprintf(header_buffer, sizeof(header_buffer),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Last-Modified: %s\r\n"
|
||||
"Server: %s\r\n\r\n", (long) file_stat.st_size, swoole_get_mimetype(buffer.filename),
|
||||
date_,
|
||||
date_last_modified,
|
||||
SW_HTTP_SERVER_SOFTWARE);
|
||||
|
||||
response.data = header_buffer;
|
||||
|
||||
#ifdef HAVE_TCP_NOPUSH
|
||||
if (conn->tcp_nopush == 0)
|
||||
{
|
||||
if (swSocket_tcp_nopush(conn->fd, 1) == -1)
|
||||
{
|
||||
swWarn("swSocket_tcp_nopush() failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
conn->tcp_nopush = 1;
|
||||
}
|
||||
#endif
|
||||
swReactorThread_send(&response);
|
||||
|
||||
buffer.offset = 0;
|
||||
buffer.length = file_stat.st_size;
|
||||
|
||||
response.info.type = SW_EVENT_SENDFILE;
|
||||
response.length = response.info.len = sizeof(swSendFile_request) + buffer.length + 1;
|
||||
response.data = (void*) &buffer;
|
||||
|
||||
swReactorThread_send(&response);
|
||||
return SW_TRUE;
|
||||
}
|
828
vendor/swoole/src/network/ProcessPool.c
vendored
Executable file
828
vendor/swoole/src/network/ProcessPool.c
vendored
Executable file
@ -0,0 +1,828 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Server.h"
|
||||
#include "Client.h"
|
||||
|
||||
/**
|
||||
* call onTask
|
||||
*/
|
||||
static int swProcessPool_worker_loop(swProcessPool *pool, swWorker *worker);
|
||||
/**
|
||||
* call onMessage
|
||||
*/
|
||||
static int swProcessPool_worker_loop_ex(swProcessPool *pool, swWorker *worker);
|
||||
|
||||
static void swProcessPool_free(swProcessPool *pool);
|
||||
|
||||
/**
|
||||
* Process manager
|
||||
*/
|
||||
int swProcessPool_create(swProcessPool *pool, int worker_num, int max_request, key_t msgqueue_key, int ipc_mode)
|
||||
{
|
||||
bzero(pool, sizeof(swProcessPool));
|
||||
|
||||
pool->worker_num = worker_num;
|
||||
pool->max_request = max_request;
|
||||
|
||||
pool->workers = SwooleG.memory_pool->alloc(SwooleG.memory_pool, worker_num * sizeof(swWorker));
|
||||
if (pool->workers == NULL)
|
||||
{
|
||||
swSysError("malloc[1] failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (ipc_mode == SW_IPC_MSGQUEUE)
|
||||
{
|
||||
pool->use_msgqueue = 1;
|
||||
pool->msgqueue_key = msgqueue_key;
|
||||
|
||||
pool->queue = sw_malloc(sizeof(swMsgQueue));
|
||||
if (pool->queue == NULL)
|
||||
{
|
||||
swSysError("malloc[2] failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (swMsgQueue_create(pool->queue, 1, pool->msgqueue_key, 0) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
else if (ipc_mode == SW_IPC_SOCKET)
|
||||
{
|
||||
pool->use_socket = 1;
|
||||
pool->stream = sw_malloc(sizeof(swStreamInfo));
|
||||
if (pool->stream == NULL)
|
||||
{
|
||||
swWarn("malloc[2] failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
bzero(pool->stream, sizeof(swStreamInfo));
|
||||
}
|
||||
else if (ipc_mode == SW_IPC_UNIXSOCK)
|
||||
{
|
||||
pool->pipes = sw_calloc(worker_num, sizeof(swPipe));
|
||||
if (pool->pipes == NULL)
|
||||
{
|
||||
swWarn("malloc[2] failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swPipe *pipe;
|
||||
int i;
|
||||
for (i = 0; i < worker_num; i++)
|
||||
{
|
||||
pipe = &pool->pipes[i];
|
||||
if (swPipeUnsock_create(pipe, 1, SOCK_DGRAM) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->workers[i].pipe_master = pipe->getFd(pipe, SW_PIPE_MASTER);
|
||||
pool->workers[i].pipe_worker = pipe->getFd(pipe, SW_PIPE_WORKER);
|
||||
pool->workers[i].pipe_object = pipe;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ipc_mode = SW_IPC_NONE;
|
||||
}
|
||||
|
||||
pool->map = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, NULL);
|
||||
if (pool->map == NULL)
|
||||
{
|
||||
swProcessPool_free(pool);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
pool->ipc_mode = ipc_mode;
|
||||
if (ipc_mode > SW_IPC_NONE)
|
||||
{
|
||||
pool->main_loop = swProcessPool_worker_loop;
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swProcessPool_create_unix_socket(swProcessPool *pool, char *socket_file, int blacklog)
|
||||
{
|
||||
if (pool->ipc_mode != SW_IPC_SOCKET)
|
||||
{
|
||||
swWarn("ipc_mode is not SW_IPC_SOCKET.");
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->stream->socket_file = sw_strdup(socket_file);
|
||||
if (pool->stream->socket_file == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->stream->socket = swSocket_create_server(SW_SOCK_UNIX_STREAM, pool->stream->socket_file, 0, blacklog);
|
||||
if (pool->stream->socket < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swProcessPool_create_tcp_socket(swProcessPool *pool, char *host, int port, int blacklog)
|
||||
{
|
||||
if (pool->ipc_mode != SW_IPC_SOCKET)
|
||||
{
|
||||
swWarn("ipc_mode is not SW_IPC_SOCKET.");
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->stream->socket_file = sw_strdup(host);
|
||||
if (pool->stream->socket_file == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->stream->socket = swSocket_create_server(SW_SOCK_TCP, host, port, blacklog);
|
||||
if (pool->stream->socket < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* start workers
|
||||
*/
|
||||
int swProcessPool_start(swProcessPool *pool)
|
||||
{
|
||||
if (pool->ipc_mode == SW_IPC_SOCKET && (pool->stream == NULL || pool->stream->socket == 0))
|
||||
{
|
||||
swWarn("must first listen to an tcp port.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int i;
|
||||
pool->started = 1;
|
||||
pool->run_worker_num = pool->worker_num;
|
||||
|
||||
for (i = 0; i < pool->worker_num; i++)
|
||||
{
|
||||
pool->workers[i].pool = pool;
|
||||
pool->workers[i].id = pool->start_id + i;
|
||||
pool->workers[i].type = pool->type;
|
||||
|
||||
if (swProcessPool_spawn(pool, &(pool->workers[i])) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static sw_inline int swProcessPool_schedule(swProcessPool *pool)
|
||||
{
|
||||
if (pool->dispatch_mode == SW_DISPATCH_QUEUE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i, target_worker_id = 0;
|
||||
int run_worker_num = pool->run_worker_num;
|
||||
|
||||
for (i = 0; i < run_worker_num + 1; i++)
|
||||
{
|
||||
target_worker_id = sw_atomic_fetch_add(&pool->round_id, 1) % run_worker_num;
|
||||
if (pool->workers[target_worker_id].status == SW_WORKER_IDLE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return target_worker_id;
|
||||
}
|
||||
|
||||
int swProcessPool_response(swProcessPool *pool, char *data, int length)
|
||||
{
|
||||
if (pool->stream == NULL || pool->stream->last_connection == 0 || pool->stream->response_buffer == NULL)
|
||||
{
|
||||
SwooleG.error = SW_ERROR_INVALID_PARAMS;
|
||||
return SW_ERR;
|
||||
}
|
||||
return swString_append_ptr(pool->stream->response_buffer, data, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatch data to worker
|
||||
*/
|
||||
int swProcessPool_dispatch(swProcessPool *pool, swEventData *data, int *dst_worker_id)
|
||||
{
|
||||
int ret = 0;
|
||||
swWorker *worker;
|
||||
|
||||
if (pool->use_socket)
|
||||
{
|
||||
swStream *stream = swStream_new(pool->stream->socket_file, 0, SW_SOCK_UNIX_STREAM);
|
||||
if (stream == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
stream->response = NULL;
|
||||
stream->session_id = 0;
|
||||
if (swStream_send(stream, (char*) data, sizeof(data->info) + data->info.len) < 0)
|
||||
{
|
||||
stream->cancel = 1;
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
if (*dst_worker_id < 0)
|
||||
{
|
||||
*dst_worker_id = swProcessPool_schedule(pool);
|
||||
}
|
||||
|
||||
*dst_worker_id += pool->start_id;
|
||||
worker = swProcessPool_get_worker(pool, *dst_worker_id);
|
||||
|
||||
int sendn = sizeof(data->info) + data->info.len;
|
||||
ret = swWorker_send2worker(worker, data, sendn, SW_PIPE_MASTER | SW_PIPE_NONBLOCK);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
sw_atomic_fetch_add(&worker->tasking_num, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
swWarn("send %d bytes to worker#%d failed.", sendn, *dst_worker_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatch data to worker
|
||||
*/
|
||||
int swProcessPool_dispatch_blocking(swProcessPool *pool, swEventData *data, int *dst_worker_id)
|
||||
{
|
||||
int ret = 0;
|
||||
int sendn = sizeof(data->info) + data->info.len;
|
||||
|
||||
if (pool->use_socket)
|
||||
{
|
||||
swClient _socket;
|
||||
if (swClient_create(&_socket, SW_SOCK_UNIX_STREAM, SW_SOCK_SYNC) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (_socket.connect(&_socket, pool->stream->socket_file, 0, -1, 0) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (_socket.send(&_socket, (void*) data, sendn, 0) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
_socket.close(&_socket);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
if (*dst_worker_id < 0)
|
||||
{
|
||||
*dst_worker_id = swProcessPool_schedule(pool);
|
||||
}
|
||||
|
||||
*dst_worker_id += pool->start_id;
|
||||
swWorker *worker = swProcessPool_get_worker(pool, *dst_worker_id);
|
||||
|
||||
ret = swWorker_send2worker(worker, data, sendn, SW_PIPE_MASTER);
|
||||
if (ret < 0)
|
||||
{
|
||||
swWarn("send %d bytes to worker#%d failed.", sendn, *dst_worker_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw_atomic_fetch_add(&worker->tasking_num, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void swProcessPool_shutdown(swProcessPool *pool)
|
||||
{
|
||||
int i, status;
|
||||
swWorker *worker;
|
||||
SwooleG.running = 0;
|
||||
|
||||
swSignal_none();
|
||||
//concurrent kill
|
||||
for (i = 0; i < pool->run_worker_num; i++)
|
||||
{
|
||||
worker = &pool->workers[i];
|
||||
if (swKill(worker->pid, SIGTERM) < 0)
|
||||
{
|
||||
swSysError("kill(%d) failed.", worker->pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < pool->run_worker_num; i++)
|
||||
{
|
||||
worker = &pool->workers[i];
|
||||
if (swWaitpid(worker->pid, &status, 0) < 0)
|
||||
{
|
||||
swSysError("waitpid(%d) failed.", worker->pid);
|
||||
}
|
||||
}
|
||||
swProcessPool_free(pool);
|
||||
pool->started = 0;
|
||||
}
|
||||
|
||||
pid_t swProcessPool_spawn(swProcessPool *pool, swWorker *worker)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
int ret_code = 0;
|
||||
|
||||
switch (pid)
|
||||
{
|
||||
//child
|
||||
case 0:
|
||||
/**
|
||||
* Process start
|
||||
*/
|
||||
if (pool->onWorkerStart != NULL)
|
||||
{
|
||||
pool->onWorkerStart(pool, worker->id);
|
||||
}
|
||||
/**
|
||||
* Process main loop
|
||||
*/
|
||||
if (pool->main_loop)
|
||||
{
|
||||
ret_code = pool->main_loop(pool, worker);
|
||||
}
|
||||
/**
|
||||
* Process stop
|
||||
*/
|
||||
if (pool->onWorkerStop != NULL)
|
||||
{
|
||||
pool->onWorkerStop(pool, worker->id);
|
||||
}
|
||||
exit(ret_code);
|
||||
break;
|
||||
case -1:
|
||||
swWarn("fork() failed. Error: %s [%d]", strerror(errno), errno);
|
||||
break;
|
||||
//parent
|
||||
default:
|
||||
//remove old process
|
||||
if (worker->pid)
|
||||
{
|
||||
swHashMap_del_int(pool->map, worker->pid);
|
||||
}
|
||||
worker->pid = pid;
|
||||
//insert new process
|
||||
swHashMap_add_int(pool->map, pid, worker);
|
||||
break;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int swProcessPool_worker_loop(swProcessPool *pool, swWorker *worker)
|
||||
{
|
||||
struct
|
||||
{
|
||||
long mtype;
|
||||
swEventData buf;
|
||||
} out;
|
||||
|
||||
int n = 0, ret;
|
||||
int task_n, worker_task_always = 0;
|
||||
|
||||
if (pool->max_request < 1)
|
||||
{
|
||||
task_n = 1;
|
||||
worker_task_always = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
task_n = pool->max_request;
|
||||
if (pool->max_request > 10)
|
||||
{
|
||||
n = swoole_system_random(1, pool->max_request / 2);
|
||||
if (n > 0)
|
||||
{
|
||||
task_n += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use from_fd save the task_worker->id
|
||||
*/
|
||||
out.buf.info.from_fd = worker->id;
|
||||
|
||||
if (pool->dispatch_mode == SW_DISPATCH_QUEUE)
|
||||
{
|
||||
out.mtype = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.mtype = worker->id + 1;
|
||||
}
|
||||
|
||||
while (SwooleG.running > 0 && task_n > 0)
|
||||
{
|
||||
/**
|
||||
* fetch task
|
||||
*/
|
||||
if (pool->use_msgqueue)
|
||||
{
|
||||
n = swMsgQueue_pop(pool->queue, (swQueue_data *) &out, sizeof(out.buf));
|
||||
if (n < 0 && errno != EINTR)
|
||||
{
|
||||
swSysError("[Worker#%d] msgrcv() failed.", worker->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (pool->use_socket)
|
||||
{
|
||||
int fd = accept(pool->stream->socket, NULL, NULL);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
swSysError("accept(%d) failed.", pool->stream->socket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
n = swStream_recv_blocking(fd, (void*) &out.buf, sizeof(out.buf));
|
||||
if (n == SW_CLOSE)
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
pool->stream->last_connection = fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = read(worker->pipe_worker, &out.buf, sizeof(out.buf));
|
||||
if (n < 0 && errno != EINTR)
|
||||
{
|
||||
swSysError("[Worker#%d] read(%d) failed.", worker->id, worker->pipe_worker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* timer
|
||||
*/
|
||||
if (n < 0)
|
||||
{
|
||||
if (errno == EINTR && SwooleG.signal_alarm)
|
||||
{
|
||||
alarm_handler: SwooleG.signal_alarm = 0;
|
||||
swTimer_select(&SwooleG.timer);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* do task
|
||||
*/
|
||||
worker->status = SW_WORKER_BUSY;
|
||||
worker->request_time = time(NULL);
|
||||
ret = pool->onTask(pool, &out.buf);
|
||||
worker->status = SW_WORKER_IDLE;
|
||||
worker->request_time = 0;
|
||||
worker->traced = 0;
|
||||
|
||||
if (pool->use_socket && pool->stream->last_connection > 0)
|
||||
{
|
||||
int _end = 0;
|
||||
swSocket_write_blocking(pool->stream->last_connection, (void *) &_end, sizeof(_end));
|
||||
close(pool->stream->last_connection);
|
||||
pool->stream->last_connection = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* timer
|
||||
*/
|
||||
if (SwooleG.signal_alarm)
|
||||
{
|
||||
goto alarm_handler;
|
||||
}
|
||||
|
||||
if (ret >= 0 && !worker_task_always)
|
||||
{
|
||||
task_n--;
|
||||
}
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swProcessPool_set_protocol(swProcessPool *pool, int task_protocol, uint32_t max_packet_size)
|
||||
{
|
||||
if (task_protocol)
|
||||
{
|
||||
pool->main_loop = swProcessPool_worker_loop;
|
||||
}
|
||||
else
|
||||
{
|
||||
pool->packet_buffer = sw_malloc(max_packet_size);
|
||||
if (pool->packet_buffer == NULL)
|
||||
{
|
||||
swSysError("malloc(%d) failed.", max_packet_size);
|
||||
return SW_ERR;
|
||||
}
|
||||
if (pool->stream)
|
||||
{
|
||||
pool->stream->response_buffer = swString_new(SW_BUFFER_SIZE_STD);
|
||||
if (pool->stream->response_buffer == NULL)
|
||||
{
|
||||
sw_free(pool->packet_buffer);
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
pool->max_packet_size = max_packet_size;
|
||||
pool->main_loop = swProcessPool_worker_loop_ex;
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swProcessPool_worker_loop_ex(swProcessPool *pool, swWorker *worker)
|
||||
{
|
||||
int n;
|
||||
char *data;
|
||||
|
||||
swQueue_data *outbuf = (swQueue_data *) pool->packet_buffer;
|
||||
outbuf->mtype = 0;
|
||||
|
||||
while (SwooleG.running > 0)
|
||||
{
|
||||
/**
|
||||
* fetch task
|
||||
*/
|
||||
if (pool->use_msgqueue)
|
||||
{
|
||||
n = swMsgQueue_pop(pool->queue, outbuf, sizeof(outbuf->mdata));
|
||||
if (n < 0 && errno != EINTR)
|
||||
{
|
||||
swSysError("[Worker#%d] msgrcv() failed.", worker->id);
|
||||
break;
|
||||
}
|
||||
data = outbuf->mdata;
|
||||
}
|
||||
else if (pool->use_socket)
|
||||
{
|
||||
int fd = accept(pool->stream->socket, NULL, NULL);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
swSysError("accept(%d) failed.", pool->stream->socket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
int tmp = 0;
|
||||
if (swSocket_recv_blocking(fd, &tmp, sizeof(tmp), MSG_WAITALL) <= 0)
|
||||
{
|
||||
goto _close;
|
||||
}
|
||||
n = ntohl(tmp);
|
||||
if (n <= 0)
|
||||
{
|
||||
goto _close;
|
||||
}
|
||||
else if (n > pool->max_packet_size)
|
||||
{
|
||||
goto _close;
|
||||
}
|
||||
if (swSocket_recv_blocking(fd, pool->packet_buffer, n, MSG_WAITALL) <= 0)
|
||||
{
|
||||
_close: close(fd);
|
||||
continue;
|
||||
}
|
||||
data = pool->packet_buffer;
|
||||
pool->stream->last_connection = fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = read(worker->pipe_worker, pool->packet_buffer, pool->max_packet_size);
|
||||
if (n < 0 && errno != EINTR)
|
||||
{
|
||||
swSysError("[Worker#%d] read(%d) failed.", worker->id, worker->pipe_worker);
|
||||
}
|
||||
data = pool->packet_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* timer
|
||||
*/
|
||||
if (n < 0)
|
||||
{
|
||||
if (errno == EINTR && SwooleG.signal_alarm)
|
||||
{
|
||||
alarm_handler: SwooleG.signal_alarm = 0;
|
||||
swTimer_select(&SwooleG.timer);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
pool->onMessage(pool, data, n);
|
||||
|
||||
if (pool->use_socket && pool->stream->last_connection > 0)
|
||||
{
|
||||
swString *resp_buf = pool->stream->response_buffer;
|
||||
if (resp_buf && resp_buf->length > 0)
|
||||
{
|
||||
int _l = htonl(resp_buf->length);
|
||||
swSocket_write_blocking(pool->stream->last_connection, &_l, sizeof(_l));
|
||||
swSocket_write_blocking(pool->stream->last_connection, resp_buf->str, resp_buf->length);
|
||||
swString_clear(resp_buf);
|
||||
}
|
||||
close(pool->stream->last_connection);
|
||||
pool->stream->last_connection = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* timer
|
||||
*/
|
||||
if (SwooleG.signal_alarm)
|
||||
{
|
||||
goto alarm_handler;
|
||||
}
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a worker to pool
|
||||
*/
|
||||
int swProcessPool_add_worker(swProcessPool *pool, swWorker *worker)
|
||||
{
|
||||
swHashMap_add_int(pool->map, worker->pid, worker);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swProcessPool_wait(swProcessPool *pool)
|
||||
{
|
||||
int pid, new_pid;
|
||||
int reload_worker_i = 0;
|
||||
pid_t reload_worker_pid = 0;
|
||||
int ret;
|
||||
int status;
|
||||
|
||||
swWorker *reload_workers = sw_calloc(pool->worker_num, sizeof(swWorker));
|
||||
if (reload_workers == NULL)
|
||||
{
|
||||
swError("malloc[reload_workers] failed");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
while (SwooleG.running)
|
||||
{
|
||||
pid = wait(&status);
|
||||
if (pid < 0)
|
||||
{
|
||||
if (SwooleG.running == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (pool->reloading == 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
swWarn("[Manager] wait failed. Error: %s [%d]", strerror(errno), errno);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
swNotice("reload workers.");
|
||||
|
||||
if (pool->reload_init == 0)
|
||||
{
|
||||
pool->reload_init = 1;
|
||||
memcpy(reload_workers, pool->workers, sizeof(swWorker) * pool->worker_num);
|
||||
}
|
||||
|
||||
goto kill_worker;
|
||||
}
|
||||
|
||||
if (SwooleG.running == 1)
|
||||
{
|
||||
swWorker *exit_worker = swHashMap_find_int(pool->map, pid);
|
||||
if (exit_worker == NULL)
|
||||
{
|
||||
if (pool->onWorkerNotFound)
|
||||
{
|
||||
pool->onWorkerNotFound(pool, pid, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
swWarn("[Manager]unknow worker[pid=%d]", pid);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!WIFEXITED(status))
|
||||
{
|
||||
swWarn("worker#%d abnormal exit, status=%d, signal=%d", exit_worker->id, WEXITSTATUS(status), WTERMSIG(status));
|
||||
}
|
||||
new_pid = swProcessPool_spawn(pool, exit_worker);
|
||||
if (new_pid < 0)
|
||||
{
|
||||
swWarn("Fork worker process failed. Error: %s [%d]", strerror(errno), errno);
|
||||
sw_free(reload_workers);
|
||||
return SW_ERR;
|
||||
}
|
||||
swHashMap_del_int(pool->map, pid);
|
||||
if (pid == reload_worker_pid)
|
||||
{
|
||||
reload_worker_i++;
|
||||
}
|
||||
}
|
||||
//reload worker
|
||||
kill_worker: if (pool->reloading == 1)
|
||||
{
|
||||
//reload finish
|
||||
if (reload_worker_i >= pool->worker_num)
|
||||
{
|
||||
pool->reloading = pool->reload_init = reload_worker_pid = reload_worker_i = 0;
|
||||
continue;
|
||||
}
|
||||
reload_worker_pid = reload_workers[reload_worker_i].pid;
|
||||
ret = kill(reload_worker_pid, SIGTERM);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (errno == ECHILD)
|
||||
{
|
||||
reload_worker_i++;
|
||||
goto kill_worker;
|
||||
}
|
||||
swSysError("[Manager]kill(%d) failed.", reload_workers[reload_worker_i].pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
sw_free(reload_workers);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static void swProcessPool_free(swProcessPool *pool)
|
||||
{
|
||||
int i;
|
||||
swPipe *_pipe;
|
||||
|
||||
if (pool->pipes)
|
||||
{
|
||||
for (i = 0; i < pool->worker_num; i++)
|
||||
{
|
||||
_pipe = &pool->pipes[i];
|
||||
_pipe->close(_pipe);
|
||||
}
|
||||
sw_free(pool->pipes);
|
||||
}
|
||||
|
||||
if (pool->use_msgqueue == 1 && pool->msgqueue_key == 0)
|
||||
{
|
||||
swMsgQueue_free(pool->queue);
|
||||
}
|
||||
|
||||
if (pool->stream)
|
||||
{
|
||||
if (pool->stream->socket)
|
||||
{
|
||||
unlink(pool->stream->socket_file);
|
||||
sw_free((void*) pool->stream->socket_file);
|
||||
}
|
||||
if (pool->stream->socket)
|
||||
{
|
||||
close(pool->stream->socket);
|
||||
}
|
||||
if (pool->stream->response_buffer)
|
||||
{
|
||||
swString_free(pool->stream->response_buffer);
|
||||
}
|
||||
sw_free(pool->stream);
|
||||
}
|
||||
|
||||
if (pool->map)
|
||||
{
|
||||
swHashMap_free(pool->map);
|
||||
}
|
||||
}
|
||||
|
582
vendor/swoole/src/network/ReactorProcess.c
vendored
Executable file
582
vendor/swoole/src/network/ReactorProcess.c
vendored
Executable file
@ -0,0 +1,582 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Server.h"
|
||||
|
||||
static int swReactorProcess_loop(swProcessPool *pool, swWorker *worker);
|
||||
static int swReactorProcess_onPipeRead(swReactor *reactor, swEvent *event);
|
||||
static int swReactorProcess_send2client(swFactory *, swSendData *);
|
||||
static int swReactorProcess_send2worker(int, void *, int);
|
||||
static void swReactorProcess_onTimeout(swTimer *timer, swTimer_node *tnode);
|
||||
|
||||
#ifdef HAVE_REUSEPORT
|
||||
static int swReactorProcess_reuse_port(swListenPort *ls);
|
||||
#endif
|
||||
|
||||
static uint32_t heartbeat_check_lasttime = 0;
|
||||
|
||||
int swReactorProcess_create(swServer *serv)
|
||||
{
|
||||
serv->reactor_num = serv->worker_num;
|
||||
serv->reactor_threads = sw_calloc(1, sizeof(swReactorThread));
|
||||
if (serv->reactor_threads == NULL)
|
||||
{
|
||||
swSysError("calloc[1](%d) failed.", (int )(serv->reactor_num * sizeof(swReactorThread)));
|
||||
return SW_ERR;
|
||||
}
|
||||
serv->connection_list = sw_calloc(serv->max_connection, sizeof(swConnection));
|
||||
if (serv->connection_list == NULL)
|
||||
{
|
||||
swSysError("calloc[2](%d) failed.", (int )(serv->max_connection * sizeof(swConnection)));
|
||||
return SW_ERR;
|
||||
}
|
||||
//create factry object
|
||||
if (swFactory_create(&(serv->factory)) < 0)
|
||||
{
|
||||
swError("create factory failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
serv->factory.finish = swReactorProcess_send2client;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* base模式
|
||||
* 在worker进程中直接accept连接
|
||||
*/
|
||||
int swReactorProcess_start(swServer *serv)
|
||||
{
|
||||
swListenPort *ls;
|
||||
if (serv->onStart != NULL)
|
||||
{
|
||||
serv->onStart(serv);
|
||||
}
|
||||
|
||||
//listen TCP
|
||||
if (serv->have_tcp_sock == 1)
|
||||
{
|
||||
LL_FOREACH(serv->listen_list, ls)
|
||||
{
|
||||
if (swSocket_is_dgram(ls->type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (SwooleG.reuse_port)
|
||||
{
|
||||
if (close(ls->sock) < 0)
|
||||
{
|
||||
swSysError("close(%d) failed.", ls->sock);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//listen server socket
|
||||
if (swPort_listen(ls) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (swProcessPool_create(&serv->gs->event_workers, serv->worker_num, serv->max_request, 0, SW_IPC_UNIXSOCK) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
serv->gs->event_workers.ptr = serv;
|
||||
serv->gs->event_workers.main_loop = swReactorProcess_loop;
|
||||
serv->gs->event_workers.type = SW_PROCESS_WORKER;
|
||||
serv->gs->event_workers.run_worker_num = serv->worker_num;
|
||||
|
||||
//no worker
|
||||
if (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && serv->user_worker_list == NULL)
|
||||
{
|
||||
swWorker single_worker;
|
||||
bzero(&single_worker, sizeof(single_worker));
|
||||
return swReactorProcess_loop(&serv->gs->event_workers, &single_worker);
|
||||
}
|
||||
|
||||
swWorker *worker;
|
||||
int i;
|
||||
for (i = 0; i < serv->worker_num; i++)
|
||||
{
|
||||
worker = &serv->gs->event_workers.workers[i];
|
||||
if (swWorker_create(worker) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
//task workers
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
if (swServer_create_task_worker(serv) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swTaskWorker_init(&serv->gs->task_workers);
|
||||
swProcessPool_start(&serv->gs->task_workers);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < serv->gs->task_workers.worker_num; i++)
|
||||
{
|
||||
swProcessPool_add_worker(&serv->gs->event_workers, &serv->gs->task_workers.workers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create user worker process
|
||||
*/
|
||||
if (serv->user_worker_list)
|
||||
{
|
||||
swUserWorker_node *user_worker;
|
||||
LL_FOREACH(serv->user_worker_list, user_worker)
|
||||
{
|
||||
/**
|
||||
* store the pipe object
|
||||
*/
|
||||
if (user_worker->worker->pipe_object)
|
||||
{
|
||||
swServer_store_pipe_fd(serv, user_worker->worker->pipe_object);
|
||||
}
|
||||
swManager_spawn_user_worker(serv, user_worker->worker);
|
||||
}
|
||||
serv->gs->event_workers.onWorkerNotFound = swManager_wait_user_worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* manager process is the same as the master process
|
||||
*/
|
||||
SwooleG.pid = serv->gs->manager_pid = getpid();
|
||||
SwooleG.process_type = SW_PROCESS_MASTER;
|
||||
|
||||
/**
|
||||
* manager process can not use signalfd
|
||||
*/
|
||||
SwooleG.use_timerfd = 0;
|
||||
SwooleG.use_signalfd = 0;
|
||||
SwooleG.use_timer_pipe = 0;
|
||||
swServer_signal_init(serv);
|
||||
|
||||
swProcessPool_start(&serv->gs->event_workers);
|
||||
swProcessPool_wait(&serv->gs->event_workers);
|
||||
swProcessPool_shutdown(&serv->gs->event_workers);
|
||||
|
||||
swManager_kill_user_worker(serv);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swReactorProcess_onPipeRead(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swEventData task;
|
||||
swSendData _send;
|
||||
swServer *serv = reactor->ptr;
|
||||
swFactory *factory = &serv->factory;
|
||||
swString *buffer_output;
|
||||
|
||||
if (read(event->fd, &task, sizeof(task)) <= 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
switch (task.info.type)
|
||||
{
|
||||
case SW_EVENT_PIPE_MESSAGE:
|
||||
serv->onPipeMessage(serv, &task);
|
||||
break;
|
||||
case SW_EVENT_FINISH:
|
||||
serv->onFinish(serv, &task);
|
||||
break;
|
||||
case SW_EVENT_SENDFILE:
|
||||
memcpy(&_send.info, &task.info, sizeof(_send.info));
|
||||
_send.data = task.data;
|
||||
factory->finish(factory, &_send);
|
||||
break;
|
||||
case SW_EVENT_PROXY_START:
|
||||
case SW_EVENT_PROXY_END:
|
||||
buffer_output = SwooleWG.buffer_output[task.info.from_id];
|
||||
swString_append_ptr(buffer_output, task.data, task.info.len);
|
||||
if (task.info.type == SW_EVENT_PROXY_END)
|
||||
{
|
||||
memcpy(&_send.info, &task.info, sizeof(_send.info));
|
||||
_send.info.type = SW_EVENT_TCP;
|
||||
_send.data = buffer_output->str;
|
||||
_send.length = buffer_output->length;
|
||||
factory->finish(factory, &_send);
|
||||
swString_clear(buffer_output);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swReactorProcess_loop(swProcessPool *pool, swWorker *worker)
|
||||
{
|
||||
swServer *serv = pool->ptr;
|
||||
swReactor *reactor = &(serv->reactor_threads[0].reactor);
|
||||
|
||||
SwooleG.process_type = SW_PROCESS_WORKER;
|
||||
SwooleG.pid = getpid();
|
||||
|
||||
SwooleWG.id = worker->id;
|
||||
if (serv->max_request > 0)
|
||||
{
|
||||
SwooleWG.run_always = 0;
|
||||
}
|
||||
SwooleWG.max_request = serv->max_request;
|
||||
SwooleWG.worker = worker;
|
||||
|
||||
SwooleTG.id = 0;
|
||||
if (worker->id == 0)
|
||||
{
|
||||
SwooleTG.update_time = 1;
|
||||
}
|
||||
|
||||
swServer_worker_init(serv, worker);
|
||||
|
||||
int n_buffer = serv->worker_num + serv->task_worker_num;
|
||||
SwooleWG.buffer_output = sw_malloc(sizeof(swString*) * n_buffer);
|
||||
if (SwooleWG.buffer_output == NULL)
|
||||
{
|
||||
swError("malloc for SwooleWG.buffer_output failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < n_buffer; i++)
|
||||
{
|
||||
SwooleWG.buffer_output[i] = swString_new(SW_BUFFER_SIZE_BIG);
|
||||
if (SwooleWG.buffer_output[i] == NULL)
|
||||
{
|
||||
swError("buffer_output init failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
//create reactor
|
||||
if (swReactor_create(reactor, SW_REACTOR_MAXEVENTS) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swListenPort *ls;
|
||||
int fdtype;
|
||||
|
||||
LL_FOREACH(serv->listen_list, ls)
|
||||
{
|
||||
fdtype = swSocket_is_dgram(ls->type) ? SW_FD_UDP : SW_FD_LISTEN;
|
||||
#ifdef HAVE_REUSEPORT
|
||||
if (fdtype == SW_FD_LISTEN && SwooleG.reuse_port)
|
||||
{
|
||||
if (swReactorProcess_reuse_port(ls) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
reactor->add(reactor, ls->sock, fdtype);
|
||||
}
|
||||
|
||||
SwooleG.main_reactor = reactor;
|
||||
|
||||
reactor->id = worker->id;
|
||||
reactor->ptr = serv;
|
||||
|
||||
#ifdef SW_USE_RINGBUFFER
|
||||
serv->reactor_threads[0].buffer_input = swMalloc_new();
|
||||
if (serv->reactor_threads[0].buffer_input == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNALFD
|
||||
if (SwooleG.use_signalfd)
|
||||
{
|
||||
swSignalfd_setup(SwooleG.main_reactor);
|
||||
}
|
||||
#endif
|
||||
|
||||
reactor->thread = 1;
|
||||
reactor->socket_list = serv->connection_list;
|
||||
reactor->max_socket = serv->max_connection;
|
||||
|
||||
reactor->disable_accept = 0;
|
||||
reactor->enable_accept = swServer_enable_accept;
|
||||
reactor->close = swReactorThread_close;
|
||||
|
||||
//set event handler
|
||||
//connect
|
||||
reactor->setHandle(reactor, SW_FD_LISTEN, swServer_master_onAccept);
|
||||
//close
|
||||
reactor->setHandle(reactor, SW_FD_CLOSE, swReactorProcess_onClose);
|
||||
//pipe
|
||||
reactor->setHandle(reactor, SW_FD_WRITE, swReactor_onWrite);
|
||||
reactor->setHandle(reactor, SW_FD_PIPE | SW_EVENT_READ, swReactorProcess_onPipeRead);
|
||||
|
||||
swServer_store_listen_socket(serv);
|
||||
|
||||
if (worker->pipe_worker)
|
||||
{
|
||||
swSetNonBlock(worker->pipe_worker);
|
||||
swSetNonBlock(worker->pipe_master);
|
||||
reactor->add(reactor, worker->pipe_worker, SW_FD_PIPE);
|
||||
reactor->add(reactor, worker->pipe_master, SW_FD_PIPE);
|
||||
}
|
||||
|
||||
//task workers
|
||||
if (serv->task_worker_num > 0)
|
||||
{
|
||||
swPipe *p;
|
||||
swConnection *psock;
|
||||
int pfd;
|
||||
|
||||
if (serv->task_ipc_mode == SW_TASK_IPC_UNIXSOCK)
|
||||
{
|
||||
for (i = 0; i < serv->gs->task_workers.worker_num; i++)
|
||||
{
|
||||
p = serv->gs->task_workers.workers[i].pipe_object;
|
||||
pfd = p->getFd(p, 1);
|
||||
psock = swReactor_get(reactor, pfd);
|
||||
psock->fdtype = SW_FD_PIPE;
|
||||
swSetNonBlock(pfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//set protocol function point
|
||||
swReactorThread_set_protocol(serv, reactor);
|
||||
|
||||
/**
|
||||
* init timer
|
||||
*/
|
||||
if (swTimer_init(1000) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
/**
|
||||
* 1 second timer, update serv->gs->now
|
||||
*/
|
||||
if (SwooleG.timer.add(&SwooleG.timer, 1000, 1, serv, swServer_master_onTimer) == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (serv->onWorkerStart)
|
||||
{
|
||||
serv->onWorkerStart(serv, worker->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* for heartbeat check
|
||||
*/
|
||||
if (serv->heartbeat_check_interval > 0)
|
||||
{
|
||||
if (SwooleG.timer.add(&SwooleG.timer, serv->heartbeat_check_interval * 1000, 1, reactor, swReactorProcess_onTimeout) == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
reactor->wait(reactor, NULL);
|
||||
|
||||
if (serv->onWorkerStop)
|
||||
{
|
||||
serv->onWorkerStop(serv, worker->id);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swReactorProcess_onClose(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
int fd = event->fd;
|
||||
swServer *serv = reactor->ptr;
|
||||
swConnection *conn = swServer_connection_get(SwooleG.serv, fd);
|
||||
if (conn == NULL || conn->active == 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (reactor->del(reactor, fd) == 0)
|
||||
{
|
||||
return swServer_tcp_notify(serv, conn, SW_EVENT_CLOSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
static int swReactorProcess_send2worker(int pipe_fd, void *data, int length)
|
||||
{
|
||||
if (swIsTaskWorker())
|
||||
{
|
||||
return swSocket_write_blocking(pipe_fd, data, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SwooleG.main_reactor->write(SwooleG.main_reactor, pipe_fd, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
static int swReactorProcess_send2client(swFactory *factory, swSendData *_send)
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
int session_id = _send->info.fd;
|
||||
if (_send->length == 0)
|
||||
{
|
||||
_send->length = _send->info.len;
|
||||
}
|
||||
|
||||
swSession *session = swServer_get_session(serv, session_id);
|
||||
if (session->fd == 0)
|
||||
{
|
||||
swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "send %d byte failed, session#%d does not exist.", _send->length, session_id);
|
||||
return SW_ERR;
|
||||
}
|
||||
//proxy
|
||||
if (session->reactor_id != SwooleWG.id)
|
||||
{
|
||||
swTrace("session->reactor_id=%d, SwooleWG.id=%d", session->reactor_id, SwooleWG.id);
|
||||
swWorker *worker = swProcessPool_get_worker(&serv->gs->event_workers, session->reactor_id);
|
||||
swEventData proxy_msg;
|
||||
|
||||
if (_send->info.type == SW_EVENT_TCP)
|
||||
{
|
||||
proxy_msg.info.fd = session_id;
|
||||
proxy_msg.info.from_id = SwooleWG.id;
|
||||
proxy_msg.info.type = SW_EVENT_PROXY_START;
|
||||
|
||||
size_t send_n = _send->length;
|
||||
size_t offset = 0;
|
||||
|
||||
while (send_n > 0)
|
||||
{
|
||||
if (send_n > SW_BUFFER_SIZE)
|
||||
{
|
||||
proxy_msg.info.len = SW_BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
proxy_msg.info.type = SW_EVENT_PROXY_END;
|
||||
proxy_msg.info.len = send_n;
|
||||
}
|
||||
memcpy(proxy_msg.data, _send->data + offset, proxy_msg.info.len);
|
||||
send_n -= proxy_msg.info.len;
|
||||
offset += proxy_msg.info.len;
|
||||
swReactorProcess_send2worker(worker->pipe_master, &proxy_msg, sizeof(proxy_msg.info) + proxy_msg.info.len);
|
||||
}
|
||||
swTrace("proxy message, fd=%d, len=%ld",worker->pipe_master, sizeof(proxy_msg.info) + proxy_msg.info.len);
|
||||
}
|
||||
else if (_send->info.type == SW_EVENT_SENDFILE)
|
||||
{
|
||||
memcpy(&proxy_msg.info, &_send->info, sizeof(proxy_msg.info));
|
||||
memcpy(proxy_msg.data, _send->data, _send->length);
|
||||
return swReactorProcess_send2worker(worker->pipe_master, &proxy_msg, sizeof(proxy_msg.info) + proxy_msg.info.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
swWarn("unkown event type[%d].", _send->info.type);
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return swFactory_finish(factory, _send);
|
||||
}
|
||||
}
|
||||
|
||||
static void swReactorProcess_onTimeout(swTimer *timer, swTimer_node *tnode)
|
||||
{
|
||||
swReactor *reactor = tnode->data;
|
||||
swServer *serv = reactor->ptr;
|
||||
swEvent notify_ev;
|
||||
swConnection *conn;
|
||||
|
||||
if (serv->gs->now < heartbeat_check_lasttime + 10)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fd;
|
||||
int serv_max_fd;
|
||||
int serv_min_fd;
|
||||
int checktime;
|
||||
|
||||
bzero(¬ify_ev, sizeof(notify_ev));
|
||||
notify_ev.type = SW_EVENT_CLOSE;
|
||||
|
||||
serv_max_fd = swServer_get_maxfd(serv);
|
||||
serv_min_fd = swServer_get_minfd(serv);
|
||||
|
||||
checktime = serv->gs->now - serv->heartbeat_idle_time;
|
||||
|
||||
for (fd = serv_min_fd; fd <= serv_max_fd; fd++)
|
||||
{
|
||||
conn = swServer_connection_get(serv, fd);
|
||||
|
||||
if (conn != NULL && conn->active == 1 && conn->fdtype == SW_FD_TCP)
|
||||
{
|
||||
if (conn->protect || conn->last_time > checktime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#ifdef SW_USE_OPENSSL
|
||||
if (conn->ssl && conn->ssl_state != SW_SSL_STATE_READY)
|
||||
{
|
||||
swReactorThread_close(reactor, fd);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
notify_ev.fd = fd;
|
||||
notify_ev.from_id = conn->from_id;
|
||||
swReactorProcess_onClose(reactor, ¬ify_ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_REUSEPORT
|
||||
static int swReactorProcess_reuse_port(swListenPort *ls)
|
||||
{
|
||||
//create new socket
|
||||
int sock = swSocket_create(ls->type);
|
||||
if (sock < 0)
|
||||
{
|
||||
swSysError("create socket failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
//bind address and port
|
||||
if (swSocket_bind(sock, ls->type, ls->host, &ls->port) < 0)
|
||||
{
|
||||
close(sock);
|
||||
return SW_ERR;
|
||||
}
|
||||
//stream socket, set nonblock
|
||||
if (swSocket_is_stream(ls->type))
|
||||
{
|
||||
swSetNonBlock(sock);
|
||||
}
|
||||
ls->sock = sock;
|
||||
return swPort_listen(ls);
|
||||
}
|
||||
#endif
|
1619
vendor/swoole/src/network/ReactorThread.c
vendored
Executable file
1619
vendor/swoole/src/network/ReactorThread.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1894
vendor/swoole/src/network/Server.c
vendored
Executable file
1894
vendor/swoole/src/network/Server.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
169
vendor/swoole/src/network/Stream.c
vendored
Executable file
169
vendor/swoole/src/network/Stream.c
vendored
Executable file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Client.h"
|
||||
|
||||
static void swStream_free(swStream *stream);
|
||||
|
||||
static void swStream_onConnect(swClient *cli)
|
||||
{
|
||||
swStream *stream = (swStream*) cli->object;
|
||||
if (stream->cancel)
|
||||
{
|
||||
cli->close(cli);
|
||||
}
|
||||
*((uint32_t *) stream->buffer->str) = ntohl(stream->buffer->length - 4);
|
||||
if (cli->send(cli, stream->buffer->str, stream->buffer->length, 0) < 0)
|
||||
{
|
||||
cli->close(cli);
|
||||
}
|
||||
else
|
||||
{
|
||||
swString_free(stream->buffer);
|
||||
stream->buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void swStream_onError(swClient *cli)
|
||||
{
|
||||
swStream_free(cli->object);
|
||||
}
|
||||
|
||||
static void swStream_onReceive(swClient *cli, char *data, uint32_t length)
|
||||
{
|
||||
swStream *stream = (swStream*) cli->object;
|
||||
if (length == 4)
|
||||
{
|
||||
cli->socket->close_wait = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->response(stream, data + 4, length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void swStream_onClose(swClient *cli)
|
||||
{
|
||||
swStream_free(cli->object);
|
||||
}
|
||||
|
||||
static void swStream_free(swStream *stream)
|
||||
{
|
||||
if (stream->buffer)
|
||||
{
|
||||
swString_free(stream->buffer);
|
||||
}
|
||||
sw_free(stream);
|
||||
}
|
||||
|
||||
swStream* swStream_new(char *dst_host, int dst_port, int type)
|
||||
{
|
||||
swStream *stream = (swStream*) sw_malloc(sizeof(swStream));
|
||||
bzero(stream, sizeof(swStream));
|
||||
|
||||
swClient *cli = &stream->client;
|
||||
if (swClient_create(cli, type, 1) < 0)
|
||||
{
|
||||
swStream_free(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli->onConnect = swStream_onConnect;
|
||||
cli->onReceive = swStream_onReceive;
|
||||
cli->onError = swStream_onError;
|
||||
cli->onClose = swStream_onClose;
|
||||
cli->object = stream;
|
||||
|
||||
cli->open_length_check = 1;
|
||||
swStream_set_protocol(&cli->protocol);
|
||||
|
||||
if (cli->connect(cli, dst_host, dst_port, -1, 0) < 0)
|
||||
{
|
||||
swSysError("failed to connect to [%s:%d].", dst_host, dst_port);
|
||||
swStream_free(stream);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream Protocol: Length(32bit/Network Byte Order) + Body
|
||||
*/
|
||||
void swStream_set_protocol(swProtocol *protocol)
|
||||
{
|
||||
protocol->get_package_length = swProtocol_get_package_length;
|
||||
protocol->package_length_size = 4;
|
||||
protocol->package_length_type = 'N';
|
||||
protocol->package_body_offset = 4;
|
||||
protocol->package_length_offset = 0;
|
||||
}
|
||||
|
||||
void swStream_set_max_length(swStream *stream, uint32_t max_length)
|
||||
{
|
||||
stream->client.protocol.package_max_length = max_length;
|
||||
}
|
||||
|
||||
int swStream_send(swStream *stream, char *data, size_t length)
|
||||
{
|
||||
if (stream->buffer == NULL)
|
||||
{
|
||||
stream->buffer = swString_new(swoole_size_align(length + 4, SwooleG.pagesize));
|
||||
if (stream->buffer == NULL)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
stream->buffer->length = 4;
|
||||
}
|
||||
if (swString_append_ptr(stream->buffer, data, length) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swStream_recv_blocking(int fd, void *__buf, size_t __len)
|
||||
{
|
||||
int tmp = 0;
|
||||
int ret = swSocket_recv_blocking(fd, &tmp, sizeof(tmp), MSG_WAITALL);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
return SW_CLOSE;
|
||||
}
|
||||
int length = ntohl(tmp);
|
||||
if (length <= 0)
|
||||
{
|
||||
return SW_CLOSE;
|
||||
}
|
||||
else if (length > __len)
|
||||
{
|
||||
return SW_CLOSE;
|
||||
}
|
||||
|
||||
ret = swSocket_recv_blocking(fd, __buf, length, MSG_WAITALL);
|
||||
if (ret <= 0)
|
||||
{
|
||||
return SW_CLOSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SW_READY;
|
||||
}
|
||||
}
|
320
vendor/swoole/src/network/TaskWorker.c
vendored
Executable file
320
vendor/swoole/src/network/TaskWorker.c
vendored
Executable file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Server.h"
|
||||
|
||||
static swEventData *current_task = NULL;
|
||||
|
||||
static void swTaskWorker_signal_init(void);
|
||||
|
||||
void swTaskWorker_init(swProcessPool *pool)
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
pool->ptr = serv;
|
||||
pool->onTask = swTaskWorker_onTask;
|
||||
pool->onWorkerStart = swTaskWorker_onStart;
|
||||
pool->onWorkerStop = swTaskWorker_onStop;
|
||||
pool->type = SW_PROCESS_TASKWORKER;
|
||||
pool->start_id = serv->worker_num;
|
||||
pool->run_worker_num = serv->task_worker_num;
|
||||
|
||||
if (serv->task_ipc_mode == SW_TASK_IPC_PREEMPTIVE)
|
||||
{
|
||||
pool->dispatch_mode = SW_DISPATCH_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* in worker process
|
||||
*/
|
||||
int swTaskWorker_onFinish(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swServer *serv = reactor->ptr;
|
||||
swEventData task;
|
||||
int n;
|
||||
|
||||
do
|
||||
{
|
||||
n = read(event->fd, &task, sizeof(task));
|
||||
} while (n < 0 && errno == EINTR);
|
||||
|
||||
return serv->onFinish(serv, &task);
|
||||
}
|
||||
|
||||
int swTaskWorker_onTask(swProcessPool *pool, swEventData *task)
|
||||
{
|
||||
int ret = SW_OK;
|
||||
swServer *serv = pool->ptr;
|
||||
current_task = task;
|
||||
|
||||
if (task->info.type == SW_EVENT_PIPE_MESSAGE)
|
||||
{
|
||||
serv->onPipeMessage(serv, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = serv->onTask(serv, task);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int swTaskWorker_large_pack(swEventData *task, void *data, int data_len)
|
||||
{
|
||||
swPackage_task pkg;
|
||||
bzero(&pkg, sizeof(pkg));
|
||||
|
||||
memcpy(pkg.tmpfile, SwooleG.task_tmpdir, SwooleG.task_tmpdir_len);
|
||||
|
||||
//create temp file
|
||||
int tmp_fd = swoole_tmpfile(pkg.tmpfile);
|
||||
if (tmp_fd < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
//write to file
|
||||
if (swoole_sync_writefile(tmp_fd, data, data_len) <= 0)
|
||||
{
|
||||
swWarn("write to tmpfile failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
task->info.len = sizeof(swPackage_task);
|
||||
//use tmp file
|
||||
swTask_type(task) |= SW_TASK_TMPFILE;
|
||||
|
||||
pkg.length = data_len;
|
||||
memcpy(task->data, &pkg, sizeof(swPackage_task));
|
||||
close(tmp_fd);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static void swTaskWorker_signal_init(void)
|
||||
{
|
||||
swSignal_set(SIGHUP, NULL, 1, 0);
|
||||
swSignal_set(SIGPIPE, NULL, 1, 0);
|
||||
swSignal_set(SIGUSR1, swWorker_signal_handler, 1, 0);
|
||||
swSignal_set(SIGUSR2, NULL, 1, 0);
|
||||
swSignal_set(SIGTERM, swWorker_signal_handler, 1, 0);
|
||||
swSignal_set(SIGALRM, swSystemTimer_signal_handler, 1, 0);
|
||||
#ifdef SIGRTMIN
|
||||
swSignal_set(SIGRTMIN, swWorker_signal_handler, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void swTaskWorker_onStart(swProcessPool *pool, int worker_id)
|
||||
{
|
||||
swServer *serv = pool->ptr;
|
||||
SwooleWG.id = worker_id;
|
||||
SwooleG.pid = getpid();
|
||||
|
||||
SwooleG.use_timer_pipe = 0;
|
||||
SwooleG.use_timerfd = 0;
|
||||
|
||||
swServer_close_port(serv, SW_TRUE);
|
||||
|
||||
swTaskWorker_signal_init();
|
||||
swWorker_onStart(serv);
|
||||
|
||||
SwooleG.main_reactor = NULL;
|
||||
swWorker *worker = swProcessPool_get_worker(pool, worker_id);
|
||||
worker->start_time = serv->gs->now;
|
||||
worker->request_count = 0;
|
||||
worker->traced = 0;
|
||||
SwooleWG.worker = worker;
|
||||
SwooleWG.worker->status = SW_WORKER_IDLE;
|
||||
}
|
||||
|
||||
void swTaskWorker_onStop(swProcessPool *pool, int worker_id)
|
||||
{
|
||||
swServer *serv = pool->ptr;
|
||||
swWorker_onStop(serv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the task result to worker
|
||||
*/
|
||||
int swTaskWorker_finish(swServer *serv, char *data, int data_len, int flags)
|
||||
{
|
||||
swEventData buf;
|
||||
if (!current_task)
|
||||
{
|
||||
swWarn("cannot use finish in worker");
|
||||
return SW_ERR;
|
||||
}
|
||||
if (serv->task_worker_num < 1)
|
||||
{
|
||||
swWarn("cannot use task/finish, because no set serv->task_worker_num.");
|
||||
return SW_ERR;
|
||||
}
|
||||
if (current_task->info.type == SW_EVENT_PIPE_MESSAGE)
|
||||
{
|
||||
swWarn("task/finish is not supported in onPipeMessage callback.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
uint16_t source_worker_id = current_task->info.from_id;
|
||||
swWorker *worker = swServer_get_worker(serv, source_worker_id);
|
||||
|
||||
if (worker == NULL)
|
||||
{
|
||||
swWarn("invalid worker_id[%d].", source_worker_id);
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int ret;
|
||||
//for swoole_server_task
|
||||
if (swTask_type(current_task) & SW_TASK_NONBLOCK)
|
||||
{
|
||||
buf.info.type = SW_EVENT_FINISH;
|
||||
buf.info.fd = current_task->info.fd;
|
||||
//callback function
|
||||
if (swTask_type(current_task) & SW_TASK_CALLBACK)
|
||||
{
|
||||
flags |= SW_TASK_CALLBACK;
|
||||
}
|
||||
else if (swTask_type(current_task) & SW_TASK_COROUTINE)
|
||||
{
|
||||
flags |= SW_TASK_COROUTINE;
|
||||
}
|
||||
swTask_type(&buf) = flags;
|
||||
|
||||
//write to file
|
||||
if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info))
|
||||
{
|
||||
if (swTaskWorker_large_pack(&buf, data, data_len) < 0 )
|
||||
{
|
||||
swWarn("large task pack failed()");
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(buf.data, data, data_len);
|
||||
buf.info.len = data_len;
|
||||
}
|
||||
|
||||
if (worker->pool->use_socket && worker->pool->stream->last_connection > 0)
|
||||
{
|
||||
int32_t _len = htonl(data_len);
|
||||
ret = swSocket_write_blocking(worker->pool->stream->last_connection, (void *) &_len, sizeof(_len));
|
||||
if (ret > 0)
|
||||
{
|
||||
ret = swSocket_write_blocking(worker->pool->stream->last_connection, data, data_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = swWorker_send2worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t flag = 1;
|
||||
|
||||
/**
|
||||
* Use worker shm store the result
|
||||
*/
|
||||
swEventData *result = &(serv->task_result[source_worker_id]);
|
||||
swPipe *task_notify_pipe = &(serv->task_notify[source_worker_id]);
|
||||
|
||||
//lock worker
|
||||
worker->lock.lock(&worker->lock);
|
||||
|
||||
if (swTask_type(current_task) & SW_TASK_WAITALL)
|
||||
{
|
||||
sw_atomic_t *finish_count = (sw_atomic_t*) result->data;
|
||||
char *_tmpfile = result->data + 4;
|
||||
int fd = open(_tmpfile, O_APPEND | O_WRONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
buf.info.type = SW_EVENT_FINISH;
|
||||
buf.info.fd = current_task->info.fd;
|
||||
swTask_type(&buf) = flags;
|
||||
//result pack
|
||||
if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info))
|
||||
{
|
||||
if (swTaskWorker_large_pack(&buf, data, data_len) < 0)
|
||||
{
|
||||
swWarn("large task pack failed()");
|
||||
buf.info.len = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.info.len = data_len;
|
||||
memcpy(buf.data, data, data_len);
|
||||
}
|
||||
//write to tmpfile
|
||||
if (swoole_sync_writefile(fd, &buf, sizeof(buf.info) + buf.info.len) < 0)
|
||||
{
|
||||
swSysError("write(%s, %ld) failed.", result->data, sizeof(buf.info) + buf.info.len);
|
||||
}
|
||||
sw_atomic_fetch_add(finish_count, 1);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result->info.type = SW_EVENT_FINISH;
|
||||
result->info.fd = current_task->info.fd;
|
||||
swTask_type(result) = flags;
|
||||
|
||||
if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info))
|
||||
{
|
||||
if (swTaskWorker_large_pack(result, data, data_len) < 0)
|
||||
{
|
||||
//unlock worker
|
||||
worker->lock.unlock(&worker->lock);
|
||||
swWarn("large task pack failed()");
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(result->data, data, data_len);
|
||||
result->info.len = data_len;
|
||||
}
|
||||
}
|
||||
|
||||
//unlock worker
|
||||
worker->lock.unlock(&worker->lock);
|
||||
|
||||
while (1)
|
||||
{
|
||||
ret = task_notify_pipe->write(task_notify_pipe, &flag, sizeof(flag));
|
||||
#ifdef HAVE_KQUEUE
|
||||
if (ret < 0 && (errno == EAGAIN || errno == ENOBUFS))
|
||||
#else
|
||||
if (ret < 0 && errno == EAGAIN)
|
||||
#endif
|
||||
{
|
||||
if (swSocket_wait(task_notify_pipe->getFd(task_notify_pipe, 1), -1, SW_EVENT_WRITE) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
swWarn("TaskWorker: send result to worker failed. Error: %s[%d]", strerror(errno), errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
186
vendor/swoole/src/network/ThreadPool.c
vendored
Executable file
186
vendor/swoole/src/network/ThreadPool.c
vendored
Executable file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
|
||||
#define swThreadPool_thread(p,id) (&p->threads[id])
|
||||
static void* swThreadPool_loop(void *arg);
|
||||
|
||||
int swThreadPool_create(swThreadPool *pool, int thread_num)
|
||||
{
|
||||
bzero(pool, sizeof(swThreadPool));
|
||||
|
||||
pool->threads = (swThread *) sw_calloc(thread_num, sizeof(swThread));
|
||||
pool->params = (swThreadParam *) sw_calloc(thread_num, sizeof(swThreadParam));
|
||||
|
||||
if (pool->threads == NULL || pool->params == NULL)
|
||||
{
|
||||
swWarn("swThreadPool_create malloc fail");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swTrace("threads=%p|params=%p", pool->threads, pool->params);
|
||||
|
||||
#ifdef SW_THREADPOOL_USE_CHANNEL
|
||||
pool->chan = swChannel_create(1024 * 256, 512, 0);
|
||||
if (pool->chan == NULL)
|
||||
{
|
||||
swWarn("swThreadPool_create create channel failed");
|
||||
return SW_ERR;
|
||||
}
|
||||
#else
|
||||
int size = SwooleG.max_sockets >= SW_THREADPOOL_QUEUE_LEN ? SwooleG.max_sockets + 1 : SW_THREADPOOL_QUEUE_LEN;
|
||||
if (swRingQueue_init(&pool->queue, size) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
#endif
|
||||
if (swCond_create(&pool->cond) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
pool->thread_num = thread_num;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swThreadPool_dispatch(swThreadPool *pool, void *task, int task_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pool->cond.lock(&pool->cond);
|
||||
#ifdef SW_THREADPOOL_USE_CHANNEL
|
||||
ret = swChannel_in(pool->chan, task, task_len);
|
||||
#else
|
||||
ret = swRingQueue_push(&pool->queue, task);
|
||||
#endif
|
||||
pool->cond.unlock(&pool->cond);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
swoole_error_log(SW_LOG_ERROR, SW_ERROR_QUEUE_FULL, "the queue of thread pool is full.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
sw_atomic_t *task_num = &pool->task_num;
|
||||
sw_atomic_fetch_add(task_num, 1);
|
||||
|
||||
return pool->cond.notify(&pool->cond);
|
||||
}
|
||||
|
||||
int swThreadPool_run(swThreadPool *pool)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pool->thread_num; i++)
|
||||
{
|
||||
pool->params[i].pti = i;
|
||||
pool->params[i].object = pool;
|
||||
if (pthread_create(&(swThreadPool_thread(pool,i)->tid), NULL, swThreadPool_loop, &pool->params[i]) < 0)
|
||||
{
|
||||
swWarn("pthread_create failed. Error: %s[%d]", strerror(errno), errno);
|
||||
return SW_ERR;
|
||||
}
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swThreadPool_free(swThreadPool *pool)
|
||||
{
|
||||
int i;
|
||||
if (pool->shutdown)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
pool->shutdown = 1;
|
||||
|
||||
//broadcast all thread
|
||||
pool->cond.broadcast(&(pool->cond));
|
||||
|
||||
for (i = 0; i < pool->thread_num; i++)
|
||||
{
|
||||
pthread_join((swThreadPool_thread(pool,i)->tid), NULL);
|
||||
}
|
||||
|
||||
#ifdef SW_THREADPOOL_USE_CHANNEL
|
||||
swChannel_free(pool->chan);
|
||||
#else
|
||||
swRingQueue_free(&pool->queue);
|
||||
#endif
|
||||
|
||||
pool->cond.free(&pool->cond);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static void* swThreadPool_loop(void *arg)
|
||||
{
|
||||
swThreadParam *param = arg;
|
||||
swThreadPool *pool = param->object;
|
||||
|
||||
int id = param->pti;
|
||||
int ret;
|
||||
void *task;
|
||||
|
||||
SwooleTG.buffer_stack = swString_new(8192);
|
||||
if (SwooleTG.buffer_stack == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pool->onStart)
|
||||
{
|
||||
pool->onStart(pool, id);
|
||||
}
|
||||
|
||||
while (SwooleG.running)
|
||||
{
|
||||
pool->cond.lock(&pool->cond);
|
||||
|
||||
if (pool->shutdown)
|
||||
{
|
||||
pool->cond.unlock(&pool->cond);
|
||||
swTrace("thread [%d] will exit\n", id);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
if (pool->task_num == 0)
|
||||
{
|
||||
pool->cond.wait(&pool->cond);
|
||||
}
|
||||
|
||||
swTrace("thread [%d] is starting to work\n", id);
|
||||
|
||||
ret = swRingQueue_pop(&pool->queue, &task);
|
||||
pool->cond.unlock(&pool->cond);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
sw_atomic_t *task_num = &pool->task_num;
|
||||
sw_atomic_fetch_sub(task_num, 1);
|
||||
|
||||
pool->onTask(pool, (void *) task, ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (pool->onStop)
|
||||
{
|
||||
pool->onStop(pool, id);
|
||||
}
|
||||
|
||||
swString_free(SwooleTG.buffer_stack);
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
135
vendor/swoole/src/network/TimeWheel.c
vendored
Executable file
135
vendor/swoole/src/network/TimeWheel.c
vendored
Executable file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
|
||||
#ifdef SW_USE_TIMEWHEEL
|
||||
|
||||
swTimeWheel* swTimeWheel_new(uint16_t size)
|
||||
{
|
||||
swTimeWheel *tw = sw_malloc(sizeof(swTimeWheel));
|
||||
if (!tw)
|
||||
{
|
||||
swWarn("malloc(%ld) failed.", sizeof(swTimeWheel));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tw->size = size;
|
||||
tw->current = 0;
|
||||
tw->wheel = sw_calloc(size, sizeof(void*));
|
||||
if (tw->wheel == NULL)
|
||||
{
|
||||
swWarn("malloc(%ld) failed.", sizeof(void*) * size);
|
||||
sw_free(tw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
tw->wheel[i] = swHashMap_new(16, NULL);
|
||||
if (tw->wheel[i] == NULL)
|
||||
{
|
||||
swTimeWheel_free(tw);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return tw;
|
||||
}
|
||||
|
||||
void swTimeWheel_free(swTimeWheel *tw)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < tw->size; i++)
|
||||
{
|
||||
if (tw->wheel[i] != NULL)
|
||||
{
|
||||
swHashMap_free(tw->wheel[i]);
|
||||
tw->wheel[i] = NULL;
|
||||
}
|
||||
}
|
||||
sw_free(tw->wheel);
|
||||
sw_free(tw);
|
||||
}
|
||||
|
||||
void swTimeWheel_forward(swTimeWheel *tw, swReactor *reactor)
|
||||
{
|
||||
swHashMap *set = tw->wheel[tw->current];
|
||||
tw->current = tw->current == tw->size - 1 ? 0 : tw->current + 1;
|
||||
|
||||
swTraceLog(SW_TRACE_REACTOR, "current=%d.", tw->current);
|
||||
|
||||
swConnection *conn;
|
||||
uint64_t fd;
|
||||
|
||||
while (1)
|
||||
{
|
||||
conn = swHashMap_each_int(set, &fd);
|
||||
if (conn == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
conn->close_force = 1;
|
||||
conn->close_notify = 1;
|
||||
conn->close_wait = 1;
|
||||
conn->close_actively = 1;
|
||||
|
||||
//notify to reactor thread
|
||||
if (conn->removed)
|
||||
{
|
||||
reactor->close(reactor, (int) fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
reactor->set(reactor, fd, SW_FD_TCP | SW_EVENT_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void swTimeWheel_add(swTimeWheel *tw, swConnection *conn)
|
||||
{
|
||||
uint16_t index = tw->current == 0 ? tw->size - 1 : tw->current - 1;
|
||||
swHashMap *new_set = tw->wheel[index];
|
||||
swHashMap_add_int(new_set, conn->fd, conn);
|
||||
|
||||
conn->timewheel_index = index;
|
||||
|
||||
swTraceLog(SW_TRACE_REACTOR, "current=%d, fd=%d, index=%d.", tw->current, conn->fd, index);
|
||||
}
|
||||
|
||||
void swTimeWheel_update(swTimeWheel *tw, swConnection *conn)
|
||||
{
|
||||
uint16_t new_index = swTimeWheel_new_index(tw);
|
||||
swHashMap *new_set = tw->wheel[new_index];
|
||||
swHashMap_add_int(new_set, conn->fd, conn);
|
||||
|
||||
swHashMap *old_set = tw->wheel[conn->timewheel_index];
|
||||
swHashMap_del_int(old_set, conn->fd);
|
||||
|
||||
swTraceLog(SW_TRACE_REACTOR, "current=%d, fd=%d, old_index=%d, new_index=%d.", tw->current, conn->fd, new_index, conn->timewheel_index);
|
||||
|
||||
conn->timewheel_index = new_index;
|
||||
}
|
||||
|
||||
void swTimeWheel_remove(swTimeWheel *tw, swConnection *conn)
|
||||
{
|
||||
swHashMap *set = tw->wheel[conn->timewheel_index];
|
||||
swHashMap_del_int(set, conn->fd);
|
||||
swTraceLog(SW_TRACE_REACTOR, "current=%d, fd=%d.", tw->current, conn->fd);
|
||||
}
|
||||
|
||||
#endif
|
245
vendor/swoole/src/network/Timer.c
vendored
Executable file
245
vendor/swoole/src/network/Timer.c
vendored
Executable file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
|
||||
static int swReactorTimer_init(long msec);
|
||||
static int swReactorTimer_set(swTimer *timer, long exec_msec);
|
||||
static swTimer_node* swTimer_add(swTimer *timer, int _msec, int interval, void *data, swTimerCallback callback);
|
||||
|
||||
int swTimer_now(struct timeval *time)
|
||||
{
|
||||
#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC)
|
||||
struct timespec _now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &_now) < 0)
|
||||
{
|
||||
swSysError("clock_gettime(CLOCK_MONOTONIC) failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
time->tv_sec = _now.tv_sec;
|
||||
time->tv_usec = _now.tv_nsec / 1000;
|
||||
#else
|
||||
if (gettimeofday(time, NULL) < 0)
|
||||
{
|
||||
swSysError("gettimeofday() failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
#endif
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static sw_inline int64_t swTimer_get_relative_msec()
|
||||
{
|
||||
struct timeval now;
|
||||
if (swTimer_now(&now) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
int64_t msec1 = (now.tv_sec - SwooleG.timer.basetime.tv_sec) * 1000;
|
||||
int64_t msec2 = (now.tv_usec - SwooleG.timer.basetime.tv_usec) / 1000;
|
||||
return msec1 + msec2;
|
||||
}
|
||||
|
||||
int swTimer_init(long msec)
|
||||
{
|
||||
if (swTimer_now(&SwooleG.timer.basetime) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
|
||||
SwooleG.timer.heap = swHeap_new(1024, SW_MIN_HEAP);
|
||||
if (!SwooleG.timer.heap)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
SwooleG.timer.map = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, NULL);
|
||||
if (!SwooleG.timer.map)
|
||||
{
|
||||
swHeap_free(SwooleG.timer.heap);
|
||||
SwooleG.timer.heap = NULL;
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
SwooleG.timer._current_id = -1;
|
||||
SwooleG.timer._next_msec = msec;
|
||||
SwooleG.timer._next_id = 1;
|
||||
SwooleG.timer.add = swTimer_add;
|
||||
|
||||
if (swIsTaskWorker())
|
||||
{
|
||||
swSystemTimer_init(msec, SwooleG.use_timer_pipe);
|
||||
}
|
||||
else
|
||||
{
|
||||
swReactorTimer_init(msec);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
void swTimer_free(swTimer *timer)
|
||||
{
|
||||
if (timer->heap)
|
||||
{
|
||||
swHeap_free(timer->heap);
|
||||
}
|
||||
}
|
||||
|
||||
static int swReactorTimer_init(long exec_msec)
|
||||
{
|
||||
SwooleG.main_reactor->check_timer = SW_TRUE;
|
||||
SwooleG.main_reactor->timeout_msec = exec_msec;
|
||||
SwooleG.timer.set = swReactorTimer_set;
|
||||
SwooleG.timer.fd = -1;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swReactorTimer_set(swTimer *timer, long exec_msec)
|
||||
{
|
||||
SwooleG.main_reactor->timeout_msec = exec_msec;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static swTimer_node* swTimer_add(swTimer *timer, int _msec, int interval, void *data, swTimerCallback callback)
|
||||
{
|
||||
swTimer_node *tnode = sw_malloc(sizeof(swTimer_node));
|
||||
if (!tnode)
|
||||
{
|
||||
swSysError("malloc(%ld) failed.", sizeof(swTimer_node));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int64_t now_msec = swTimer_get_relative_msec();
|
||||
if (now_msec < 0)
|
||||
{
|
||||
sw_free(tnode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tnode->data = data;
|
||||
tnode->type = SW_TIMER_TYPE_KERNEL;
|
||||
tnode->exec_msec = now_msec + _msec;
|
||||
tnode->interval = interval ? _msec : 0;
|
||||
tnode->remove = 0;
|
||||
tnode->callback = callback;
|
||||
|
||||
if (timer->_next_msec < 0 || timer->_next_msec > _msec)
|
||||
{
|
||||
timer->set(timer, _msec);
|
||||
timer->_next_msec = _msec;
|
||||
}
|
||||
|
||||
tnode->id = timer->_next_id++;
|
||||
if (unlikely(tnode->id < 0))
|
||||
{
|
||||
tnode->id = 1;
|
||||
timer->_next_id = 2;
|
||||
}
|
||||
timer->num++;
|
||||
|
||||
tnode->heap_node = swHeap_push(timer->heap, tnode->exec_msec, tnode);
|
||||
if (tnode->heap_node == NULL)
|
||||
{
|
||||
sw_free(tnode);
|
||||
return NULL;
|
||||
}
|
||||
swHashMap_add_int(timer->map, tnode->id, tnode);
|
||||
return tnode;
|
||||
}
|
||||
|
||||
int swTimer_del(swTimer *timer, swTimer_node *tnode)
|
||||
{
|
||||
if (tnode->remove)
|
||||
{
|
||||
return SW_FALSE;
|
||||
}
|
||||
if (SwooleG.timer._current_id > 0 && tnode->id == SwooleG.timer._current_id)
|
||||
{
|
||||
tnode->remove = 1;
|
||||
return SW_TRUE;
|
||||
}
|
||||
if (swHashMap_del_int(timer->map, tnode->id) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
if (tnode->heap_node)
|
||||
{
|
||||
//remove from min-heap
|
||||
swHeap_remove(timer->heap, tnode->heap_node);
|
||||
sw_free(tnode->heap_node);
|
||||
}
|
||||
sw_free(tnode);
|
||||
timer->num --;
|
||||
return SW_TRUE;
|
||||
}
|
||||
|
||||
int swTimer_select(swTimer *timer)
|
||||
{
|
||||
int64_t now_msec = swTimer_get_relative_msec();
|
||||
if (now_msec < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
swTimer_node *tnode = NULL;
|
||||
swHeap_node *tmp;
|
||||
long timer_id;
|
||||
|
||||
while ((tmp = swHeap_top(timer->heap)))
|
||||
{
|
||||
tnode = tmp->data;
|
||||
if (tnode->exec_msec > now_msec)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
timer_id = timer->_current_id = tnode->id;
|
||||
if (!tnode->remove)
|
||||
{
|
||||
tnode->callback(timer, tnode);
|
||||
}
|
||||
timer->_current_id = -1;
|
||||
|
||||
//persistent timer
|
||||
if (tnode->interval > 0 && !tnode->remove)
|
||||
{
|
||||
while (tnode->exec_msec <= now_msec)
|
||||
{
|
||||
tnode->exec_msec += tnode->interval;
|
||||
}
|
||||
swHeap_change_priority(timer->heap, tnode->exec_msec, tmp);
|
||||
continue;
|
||||
}
|
||||
|
||||
timer->num--;
|
||||
swHeap_pop(timer->heap);
|
||||
swHashMap_del_int(timer->map, timer_id);
|
||||
sw_free(tnode);
|
||||
}
|
||||
|
||||
if (!tnode || !tmp)
|
||||
{
|
||||
timer->_next_msec = -1;
|
||||
timer->set(timer, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer->set(timer, tnode->exec_msec - now_msec);
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
897
vendor/swoole/src/network/Worker.c
vendored
Executable file
897
vendor/swoole/src/network/Worker.c
vendored
Executable file
@ -0,0 +1,897 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Swoole |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.0 of the Apache license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
||||
| If you did not receive a copy of the Apache2.0 license and are unable|
|
||||
| to obtain it through the world-wide-web, please send a note to |
|
||||
| license@swoole.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "swoole.h"
|
||||
#include "Server.h"
|
||||
#include "Client.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
static int swWorker_onPipeReceive(swReactor *reactor, swEvent *event);
|
||||
static void swWorker_onTimeout(swTimer *timer, swTimer_node *tnode);
|
||||
static int swWorker_onStreamAccept(swReactor *reactor, swEvent *event);
|
||||
static int swWorker_onStreamRead(swReactor *reactor, swEvent *event);
|
||||
static int swWorker_onStreamPackage(swConnection *conn, char *data, uint32_t length);
|
||||
static int swWorker_onStreamClose(swReactor *reactor, swEvent *event);
|
||||
static void swWorker_stop();
|
||||
|
||||
int swWorker_create(swWorker *worker)
|
||||
{
|
||||
/**
|
||||
* Create shared memory storage
|
||||
*/
|
||||
worker->send_shm = sw_shm_malloc(SwooleG.serv->buffer_output_size);
|
||||
if (worker->send_shm == NULL)
|
||||
{
|
||||
swWarn("malloc for worker->store failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
swMutex_create(&worker->lock, 1);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
void swWorker_free(swWorker *worker)
|
||||
{
|
||||
if (worker->send_shm)
|
||||
{
|
||||
sw_shm_free(worker->send_shm);
|
||||
}
|
||||
}
|
||||
|
||||
void swWorker_signal_init(void)
|
||||
{
|
||||
swSignal_clear();
|
||||
/**
|
||||
* use user settings
|
||||
*/
|
||||
SwooleG.use_signalfd = SwooleG.enable_signalfd;
|
||||
|
||||
swSignal_add(SIGHUP, NULL);
|
||||
swSignal_add(SIGPIPE, NULL);
|
||||
swSignal_add(SIGUSR1, NULL);
|
||||
swSignal_add(SIGUSR2, NULL);
|
||||
//swSignal_add(SIGINT, swWorker_signal_handler);
|
||||
swSignal_add(SIGTERM, swWorker_signal_handler);
|
||||
swSignal_add(SIGALRM, swSystemTimer_signal_handler);
|
||||
//for test
|
||||
swSignal_add(SIGVTALRM, swWorker_signal_handler);
|
||||
#ifdef SIGRTMIN
|
||||
swSignal_add(SIGRTMIN, swWorker_signal_handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
void swWorker_signal_handler(int signo)
|
||||
{
|
||||
switch (signo)
|
||||
{
|
||||
case SIGTERM:
|
||||
/**
|
||||
* Event worker
|
||||
*/
|
||||
if (SwooleG.main_reactor)
|
||||
{
|
||||
swWorker_stop();
|
||||
}
|
||||
/**
|
||||
* Task worker
|
||||
*/
|
||||
else
|
||||
{
|
||||
SwooleG.running = 0;
|
||||
}
|
||||
break;
|
||||
case SIGALRM:
|
||||
swSystemTimer_signal_handler(SIGALRM);
|
||||
break;
|
||||
/**
|
||||
* for test
|
||||
*/
|
||||
case SIGVTALRM:
|
||||
swWarn("SIGVTALRM coming");
|
||||
break;
|
||||
case SIGUSR1:
|
||||
break;
|
||||
case SIGUSR2:
|
||||
break;
|
||||
default:
|
||||
#ifdef SIGRTMIN
|
||||
if (signo == SIGRTMIN)
|
||||
{
|
||||
swServer_reopen_log_file(SwooleG.serv);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static sw_inline int swWorker_discard_data(swServer *serv, swEventData *task)
|
||||
{
|
||||
int fd = task->info.fd;
|
||||
//check connection
|
||||
swConnection *conn = swServer_connection_verify(serv, task->info.fd);
|
||||
if (conn == NULL)
|
||||
{
|
||||
if (serv->disable_notify && !serv->discard_timeout_request)
|
||||
{
|
||||
return SW_FALSE;
|
||||
}
|
||||
goto discard_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conn->closed)
|
||||
{
|
||||
goto discard_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SW_FALSE;
|
||||
}
|
||||
}
|
||||
discard_data:
|
||||
#ifdef SW_USE_RINGBUFFER
|
||||
if (task->info.type == SW_EVENT_PACKAGE)
|
||||
{
|
||||
swPackage package;
|
||||
memcpy(&package, task->data, sizeof(package));
|
||||
swReactorThread *thread = swServer_get_thread(SwooleG.serv, task->info.from_id);
|
||||
thread->buffer_input->free(thread->buffer_input, package.data);
|
||||
swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, "[1]received the wrong data[%d bytes] from socket#%d", package.length, fd);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, "[1]received the wrong data[%d bytes] from socket#%d", task->info.len, fd);
|
||||
}
|
||||
return SW_TRUE;
|
||||
}
|
||||
|
||||
static int swWorker_onStreamAccept(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
int fd = 0;
|
||||
swSocketAddress client_addr;
|
||||
socklen_t client_addrlen = sizeof(client_addr);
|
||||
|
||||
#ifdef HAVE_ACCEPT4
|
||||
fd = accept4(event->fd, (struct sockaddr *) &client_addr, &client_addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
#else
|
||||
fd = accept(event->fd, (struct sockaddr *) &client_addr, &client_addrlen);
|
||||
#endif
|
||||
if (fd < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
return SW_OK;
|
||||
default:
|
||||
swoole_error_log(SW_LOG_ERROR, SW_ERROR_SYSTEM_CALL_FAIL, "accept() failed. Error: %s[%d]", strerror(errno),
|
||||
errno);
|
||||
return SW_OK;
|
||||
}
|
||||
}
|
||||
#ifndef HAVE_ACCEPT4
|
||||
else
|
||||
{
|
||||
swoole_fcntl_set_option(fd, 1, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
swConnection *conn = swReactor_get(reactor, fd);
|
||||
bzero(conn, sizeof(swConnection));
|
||||
conn->fd = fd;
|
||||
conn->active = 1;
|
||||
conn->socket_type = SW_SOCK_UNIX_STREAM;
|
||||
memcpy(&conn->info.addr, &client_addr, sizeof(client_addr));
|
||||
|
||||
if (reactor->add(reactor, fd, SW_FD_STREAM | SW_EVENT_READ) < 0)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swWorker_onStreamRead(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swConnection *conn = event->socket;
|
||||
swServer *serv = SwooleG.serv;
|
||||
swProtocol *protocol = &serv->stream_protocol;
|
||||
swString *buffer;
|
||||
|
||||
if (!event->socket->recv_buffer)
|
||||
{
|
||||
buffer = swLinkedList_shift(serv->buffer_pool);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
buffer = swString_new(8192);
|
||||
if (!buffer)
|
||||
{
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
}
|
||||
event->socket->recv_buffer = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = event->socket->recv_buffer;
|
||||
}
|
||||
|
||||
if (swProtocol_recv_check_length(protocol, conn, buffer) < 0)
|
||||
{
|
||||
swWorker_onStreamClose(reactor, event);
|
||||
}
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swWorker_onStreamClose(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swConnection *conn = event->socket;
|
||||
swServer *serv = SwooleG.serv;
|
||||
|
||||
swString_clear(conn->recv_buffer);
|
||||
swLinkedList_append(serv->buffer_pool, conn->recv_buffer);
|
||||
conn->recv_buffer = NULL;
|
||||
|
||||
reactor->del(reactor, conn->fd);
|
||||
reactor->close(reactor, conn->fd);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
static int swWorker_onStreamPackage(swConnection *conn, char *data, uint32_t length)
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
swEventData *task = (swEventData *) (data + 4);
|
||||
|
||||
serv->last_stream_fd = conn->fd;
|
||||
|
||||
swString *package = swWorker_get_buffer(serv, task->info.from_id);
|
||||
uint32_t data_length = length - sizeof(task->info) - 4;
|
||||
//merge data to package buffer
|
||||
swString_append_ptr(package, data + sizeof(task->info) + 4, data_length);
|
||||
|
||||
swWorker_onTask(&serv->factory, task);
|
||||
|
||||
int _end = htonl(0);
|
||||
SwooleG.main_reactor->write(SwooleG.main_reactor, conn->fd, (void *) &_end, sizeof(_end));
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
int swWorker_onTask(swFactory *factory, swEventData *task)
|
||||
{
|
||||
swServer *serv = factory->ptr;
|
||||
swString *package = NULL;
|
||||
swDgramPacket *header;
|
||||
|
||||
#ifdef SW_USE_OPENSSL
|
||||
swConnection *conn;
|
||||
#endif
|
||||
|
||||
factory->last_from_id = task->info.from_id;
|
||||
serv->last_session_id = task->info.fd;
|
||||
swWorker *worker = SwooleWG.worker;
|
||||
//worker busy
|
||||
worker->status = SW_WORKER_BUSY;
|
||||
|
||||
switch (task->info.type)
|
||||
{
|
||||
//no buffer
|
||||
case SW_EVENT_TCP:
|
||||
//ringbuffer shm package
|
||||
case SW_EVENT_PACKAGE:
|
||||
//discard data
|
||||
if (swWorker_discard_data(serv, task) == SW_TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
do_task:
|
||||
{
|
||||
worker->request_time = serv->gs->now;
|
||||
#ifdef SW_BUFFER_RECV_TIME
|
||||
serv->last_receive_usec = task->info.time;
|
||||
#endif
|
||||
serv->onReceive(serv, task);
|
||||
worker->request_time = 0;
|
||||
#ifdef SW_BUFFER_RECV_TIME
|
||||
serv->last_receive_usec = 0;
|
||||
#endif
|
||||
worker->traced = 0;
|
||||
worker->request_count++;
|
||||
sw_atomic_fetch_add(&serv->stats->request_count, 1);
|
||||
}
|
||||
if (task->info.type == SW_EVENT_PACKAGE_END)
|
||||
{
|
||||
package->length = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
//chunk package
|
||||
case SW_EVENT_PACKAGE_START:
|
||||
case SW_EVENT_PACKAGE_END:
|
||||
//discard data
|
||||
if (swWorker_discard_data(serv, task) == SW_TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
package = swWorker_get_buffer(serv, task->info.from_id);
|
||||
if (task->info.len > 0)
|
||||
{
|
||||
//merge data to package buffer
|
||||
swString_append_ptr(package, task->data, task->info.len);
|
||||
}
|
||||
//package end
|
||||
if (task->info.type == SW_EVENT_PACKAGE_END)
|
||||
{
|
||||
goto do_task;
|
||||
}
|
||||
break;
|
||||
|
||||
case SW_EVENT_UDP:
|
||||
case SW_EVENT_UDP6:
|
||||
case SW_EVENT_UNIX_DGRAM:
|
||||
package = swWorker_get_buffer(serv, task->info.from_id);
|
||||
swString_append_ptr(package, task->data, task->info.len);
|
||||
|
||||
if (package->offset == 0)
|
||||
{
|
||||
header = (swDgramPacket *) package->str;
|
||||
package->offset = header->length;
|
||||
}
|
||||
|
||||
//one packet
|
||||
if (package->offset == package->length - sizeof(swDgramPacket))
|
||||
{
|
||||
worker->request_count++;
|
||||
worker->request_time = serv->gs->now;
|
||||
#ifdef SW_BUFFER_RECV_TIME
|
||||
serv->last_receive_usec = task->info.time;
|
||||
#endif
|
||||
sw_atomic_fetch_add(&serv->stats->request_count, 1);
|
||||
serv->onPacket(serv, task);
|
||||
worker->request_time = 0;
|
||||
#ifdef SW_BUFFER_RECV_TIME
|
||||
serv->last_receive_usec = 0;
|
||||
#endif
|
||||
worker->traced = 0;
|
||||
worker->request_count++;
|
||||
swString_clear(package);
|
||||
}
|
||||
break;
|
||||
|
||||
case SW_EVENT_CLOSE:
|
||||
#ifdef SW_USE_OPENSSL
|
||||
conn = swServer_connection_verify_no_ssl(serv, task->info.fd);
|
||||
if (conn && conn->ssl_client_cert.length > 0)
|
||||
{
|
||||
sw_free(conn->ssl_client_cert.str);
|
||||
bzero(&conn->ssl_client_cert, sizeof(conn->ssl_client_cert.str));
|
||||
}
|
||||
#endif
|
||||
factory->end(factory, task->info.fd);
|
||||
break;
|
||||
|
||||
case SW_EVENT_CONNECT:
|
||||
#ifdef SW_USE_OPENSSL
|
||||
//SSL client certificate
|
||||
if (task->info.len > 0)
|
||||
{
|
||||
conn = swServer_connection_verify_no_ssl(serv, task->info.fd);
|
||||
conn->ssl_client_cert.str = sw_strndup(task->data, task->info.len);
|
||||
conn->ssl_client_cert.size = conn->ssl_client_cert.length = task->info.len;
|
||||
}
|
||||
#endif
|
||||
if (serv->onConnect)
|
||||
{
|
||||
serv->onConnect(serv, &task->info);
|
||||
}
|
||||
break;
|
||||
|
||||
case SW_EVENT_BUFFER_FULL:
|
||||
if (serv->onBufferFull)
|
||||
{
|
||||
serv->onBufferFull(serv, &task->info);
|
||||
}
|
||||
break;
|
||||
|
||||
case SW_EVENT_BUFFER_EMPTY:
|
||||
if (serv->onBufferEmpty)
|
||||
{
|
||||
serv->onBufferEmpty(serv, &task->info);
|
||||
}
|
||||
break;
|
||||
|
||||
case SW_EVENT_FINISH:
|
||||
serv->onFinish(serv, task);
|
||||
break;
|
||||
|
||||
case SW_EVENT_PIPE_MESSAGE:
|
||||
serv->onPipeMessage(serv, task);
|
||||
break;
|
||||
|
||||
default:
|
||||
swWarn("[Worker] error event[type=%d]", (int )task->info.type);
|
||||
break;
|
||||
}
|
||||
|
||||
//worker idle
|
||||
worker->status = SW_WORKER_IDLE;
|
||||
|
||||
//maximum number of requests, process will exit.
|
||||
if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request)
|
||||
{
|
||||
swWorker_stop();
|
||||
}
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
void swWorker_onStart(swServer *serv)
|
||||
{
|
||||
/**
|
||||
* Release other worker process
|
||||
*/
|
||||
swWorker *worker;
|
||||
|
||||
if (SwooleWG.id >= serv->worker_num)
|
||||
{
|
||||
SwooleG.process_type = SW_PROCESS_TASKWORKER;
|
||||
}
|
||||
else
|
||||
{
|
||||
SwooleG.process_type = SW_PROCESS_WORKER;
|
||||
}
|
||||
|
||||
int is_root = !geteuid();
|
||||
struct passwd *passwd = NULL;
|
||||
struct group *group = NULL;
|
||||
|
||||
if (is_root)
|
||||
{
|
||||
//get group info
|
||||
if (SwooleG.group)
|
||||
{
|
||||
group = getgrnam(SwooleG.group);
|
||||
if (!group)
|
||||
{
|
||||
swWarn("get group [%s] info failed.", SwooleG.group);
|
||||
}
|
||||
}
|
||||
//get user info
|
||||
if (SwooleG.user)
|
||||
{
|
||||
passwd = getpwnam(SwooleG.user);
|
||||
if (!passwd)
|
||||
{
|
||||
swWarn("get user [%s] info failed.", SwooleG.user);
|
||||
}
|
||||
}
|
||||
//chroot
|
||||
if (SwooleG.chroot)
|
||||
{
|
||||
if (0 > chroot(SwooleG.chroot))
|
||||
{
|
||||
swSysError("chroot to [%s] failed.", SwooleG.chroot);
|
||||
}
|
||||
}
|
||||
//set process group
|
||||
if (SwooleG.group && group)
|
||||
{
|
||||
if (setgid(group->gr_gid) < 0)
|
||||
{
|
||||
swSysError("setgid to [%s] failed.", SwooleG.group);
|
||||
}
|
||||
}
|
||||
//set process user
|
||||
if (SwooleG.user && passwd)
|
||||
{
|
||||
if (setuid(passwd->pw_uid) < 0)
|
||||
{
|
||||
swSysError("setuid to [%s] failed.", SwooleG.user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwooleWG.worker = swServer_get_worker(serv, SwooleWG.id);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < serv->worker_num + serv->task_worker_num; i++)
|
||||
{
|
||||
worker = swServer_get_worker(serv, i);
|
||||
if (SwooleWG.id == i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
swWorker_free(worker);
|
||||
}
|
||||
if (swIsWorker())
|
||||
{
|
||||
swSetNonBlock(worker->pipe_master);
|
||||
}
|
||||
}
|
||||
|
||||
SwooleWG.worker->status = SW_WORKER_IDLE;
|
||||
sw_shm_protect(serv->session_list, PROT_READ);
|
||||
|
||||
if (serv->onWorkerStart)
|
||||
{
|
||||
serv->onWorkerStart(serv, SwooleWG.id);
|
||||
}
|
||||
}
|
||||
|
||||
void swWorker_onStop(swServer *serv)
|
||||
{
|
||||
if (serv->onWorkerStop)
|
||||
{
|
||||
serv->onWorkerStop(serv, SwooleWG.id);
|
||||
}
|
||||
}
|
||||
|
||||
static void swWorker_stop()
|
||||
{
|
||||
swWorker *worker = SwooleWG.worker;
|
||||
swServer *serv = SwooleG.serv;
|
||||
worker->status = SW_WORKER_BUSY;
|
||||
|
||||
/**
|
||||
* force to end
|
||||
*/
|
||||
if (serv->reload_async == 0)
|
||||
{
|
||||
SwooleG.running = 0;
|
||||
SwooleG.main_reactor->running = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//The worker process is shutting down now.
|
||||
if (SwooleWG.wait_exit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//remove read event
|
||||
if (worker->pipe_worker)
|
||||
{
|
||||
swReactor_remove_read_event(SwooleG.main_reactor, worker->pipe_worker);
|
||||
}
|
||||
|
||||
if (serv->stream_fd > 0)
|
||||
{
|
||||
SwooleG.main_reactor->del(SwooleG.main_reactor, serv->stream_fd);
|
||||
close(serv->stream_fd);
|
||||
serv->stream_fd = 0;
|
||||
}
|
||||
|
||||
if (serv->onWorkerStop)
|
||||
{
|
||||
serv->onWorkerStop(serv, SwooleWG.id);
|
||||
serv->onWorkerStop = NULL;
|
||||
}
|
||||
|
||||
if (serv->factory_mode == SW_MODE_SINGLE)
|
||||
{
|
||||
swListenPort *port;
|
||||
LL_FOREACH(serv->listen_list, port)
|
||||
{
|
||||
SwooleG.main_reactor->del(SwooleG.main_reactor, port->sock);
|
||||
swPort_free(port);
|
||||
}
|
||||
|
||||
if (worker->pipe_worker)
|
||||
{
|
||||
SwooleG.main_reactor->del(SwooleG.main_reactor, worker->pipe_worker);
|
||||
SwooleG.main_reactor->del(SwooleG.main_reactor, worker->pipe_master);
|
||||
}
|
||||
|
||||
goto try_to_exit;
|
||||
}
|
||||
|
||||
swWorkerStopMessage msg;
|
||||
msg.pid = SwooleG.pid;
|
||||
msg.worker_id = SwooleWG.id;
|
||||
|
||||
//send message to manager
|
||||
if (swChannel_push(SwooleG.serv->message_box, &msg, sizeof(msg)) < 0)
|
||||
{
|
||||
SwooleG.running = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
kill(serv->gs->manager_pid, SIGIO);
|
||||
}
|
||||
|
||||
try_to_exit: SwooleWG.wait_exit = 1;
|
||||
if (SwooleG.timer.fd == 0)
|
||||
{
|
||||
swTimer_init(serv->max_wait_time * 1000);
|
||||
}
|
||||
SwooleG.timer.add(&SwooleG.timer, serv->max_wait_time * 1000, 0, NULL, swWorker_onTimeout);
|
||||
|
||||
swWorker_try_to_exit();
|
||||
}
|
||||
|
||||
static void swWorker_onTimeout(swTimer *timer, swTimer_node *tnode)
|
||||
{
|
||||
SwooleG.running = 0;
|
||||
SwooleG.main_reactor->running = 0;
|
||||
swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced to terminate.");
|
||||
}
|
||||
|
||||
void swWorker_try_to_exit()
|
||||
{
|
||||
swServer *serv = SwooleG.serv;
|
||||
int expect_event_num = SwooleG.use_signalfd ? 1 : 0;
|
||||
|
||||
if (SwooleAIO.init && SwooleAIO.task_num == 0)
|
||||
{
|
||||
swAio_free();
|
||||
}
|
||||
|
||||
swDNSResolver_free();
|
||||
|
||||
//close all client connections
|
||||
if (serv->factory_mode == SW_MODE_SINGLE)
|
||||
{
|
||||
int find_fd = swServer_get_minfd(serv);
|
||||
int max_fd = swServer_get_maxfd(serv);
|
||||
swConnection *conn;
|
||||
for (; find_fd <= max_fd; find_fd++)
|
||||
{
|
||||
conn = &serv->connection_list[find_fd];
|
||||
if (conn->active == 1 && swSocket_is_stream(conn->socket_type) && !(conn->events & SW_EVENT_WRITE))
|
||||
{
|
||||
serv->close(serv, conn->session_id, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t call_worker_exit_func = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (SwooleG.main_reactor->event_num == expect_event_num)
|
||||
{
|
||||
SwooleG.main_reactor->running = 0;
|
||||
SwooleG.running = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->onWorkerExit && call_worker_exit_func == 0)
|
||||
{
|
||||
serv->onWorkerExit(serv, SwooleWG.id);
|
||||
call_worker_exit_func = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void swWorker_clean(void)
|
||||
{
|
||||
int i;
|
||||
swServer *serv = SwooleG.serv;
|
||||
swWorker *worker;
|
||||
|
||||
for (i = 0; i < serv->worker_num + serv->task_worker_num; i++)
|
||||
{
|
||||
worker = swServer_get_worker(serv, i);
|
||||
if (SwooleG.main_reactor)
|
||||
{
|
||||
if (worker->pipe_worker)
|
||||
{
|
||||
swReactor_wait_write_buffer(SwooleG.main_reactor, worker->pipe_worker);
|
||||
}
|
||||
if (worker->pipe_master)
|
||||
{
|
||||
swReactor_wait_write_buffer(SwooleG.main_reactor, worker->pipe_master);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* worker main loop
|
||||
*/
|
||||
int swWorker_loop(swFactory *factory, int worker_id)
|
||||
{
|
||||
swServer *serv = factory->ptr;
|
||||
|
||||
#ifndef SW_WORKER_USE_SIGNALFD
|
||||
SwooleG.use_signalfd = 0;
|
||||
#elif defined(HAVE_SIGNALFD)
|
||||
SwooleG.use_signalfd = 1;
|
||||
#endif
|
||||
//timerfd
|
||||
#ifdef HAVE_TIMERFD
|
||||
SwooleG.use_timerfd = 1;
|
||||
#endif
|
||||
|
||||
//worker_id
|
||||
SwooleWG.id = worker_id;
|
||||
SwooleG.pid = getpid();
|
||||
|
||||
swWorker *worker = swServer_get_worker(serv, worker_id);
|
||||
swServer_worker_init(serv, worker);
|
||||
|
||||
SwooleG.main_reactor = sw_malloc(sizeof(swReactor));
|
||||
if (SwooleG.main_reactor == NULL)
|
||||
{
|
||||
swError("[Worker] malloc for reactor failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
if (swReactor_create(SwooleG.main_reactor, SW_REACTOR_MAXEVENTS) < 0)
|
||||
{
|
||||
swError("[Worker] create worker_reactor failed.");
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
worker->status = SW_WORKER_IDLE;
|
||||
|
||||
int pipe_worker = worker->pipe_worker;
|
||||
|
||||
swSetNonBlock(pipe_worker);
|
||||
SwooleG.main_reactor->ptr = serv;
|
||||
SwooleG.main_reactor->add(SwooleG.main_reactor, pipe_worker, SW_FD_PIPE | SW_EVENT_READ);
|
||||
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_PIPE, swWorker_onPipeReceive);
|
||||
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_WRITE, swReactor_onWrite);
|
||||
|
||||
/**
|
||||
* set pipe buffer size
|
||||
*/
|
||||
int i;
|
||||
swConnection *pipe_socket;
|
||||
for (i = 0; i < serv->worker_num + serv->task_worker_num; i++)
|
||||
{
|
||||
worker = swServer_get_worker(serv, i);
|
||||
pipe_socket = swReactor_get(SwooleG.main_reactor, worker->pipe_master);
|
||||
pipe_socket->buffer_size = SW_MAX_INT;
|
||||
pipe_socket = swReactor_get(SwooleG.main_reactor, worker->pipe_worker);
|
||||
pipe_socket->buffer_size = SW_MAX_INT;
|
||||
}
|
||||
|
||||
if (serv->dispatch_mode == SW_DISPATCH_STREAM)
|
||||
{
|
||||
SwooleG.main_reactor->add(SwooleG.main_reactor, serv->stream_fd, SW_FD_LISTEN | SW_EVENT_READ);
|
||||
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_LISTEN, swWorker_onStreamAccept);
|
||||
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_STREAM, swWorker_onStreamRead);
|
||||
swStream_set_protocol(&serv->stream_protocol);
|
||||
serv->stream_protocol.package_max_length = SW_MAX_INT;
|
||||
serv->stream_protocol.onPackage = swWorker_onStreamPackage;
|
||||
serv->buffer_pool = swLinkedList_new(0, NULL);
|
||||
}
|
||||
|
||||
swWorker_onStart(serv);
|
||||
|
||||
#ifdef HAVE_SIGNALFD
|
||||
if (SwooleG.use_signalfd)
|
||||
{
|
||||
swSignalfd_setup(SwooleG.main_reactor);
|
||||
}
|
||||
#endif
|
||||
//main loop
|
||||
SwooleG.main_reactor->wait(SwooleG.main_reactor, NULL);
|
||||
//clear pipe buffer
|
||||
swWorker_clean();
|
||||
//worker shutdown
|
||||
swWorker_onStop(serv);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to ReactorThread
|
||||
*/
|
||||
int swWorker_send2reactor(swEventData *ev_data, size_t sendn, int session_id)
|
||||
{
|
||||
int ret;
|
||||
swServer *serv = SwooleG.serv;
|
||||
int _pipe_fd = swWorker_get_send_pipe(serv, session_id, ev_data->info.from_id);
|
||||
|
||||
if (SwooleG.main_reactor)
|
||||
{
|
||||
ret = SwooleG.main_reactor->write(SwooleG.main_reactor, _pipe_fd, ev_data, sendn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = swSocket_write_blocking(_pipe_fd, ev_data, sendn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* receive data from reactor
|
||||
*/
|
||||
static int swWorker_onPipeReceive(swReactor *reactor, swEvent *event)
|
||||
{
|
||||
swEventData task;
|
||||
swServer *serv = reactor->ptr;
|
||||
swFactory *factory = &serv->factory;
|
||||
int ret;
|
||||
|
||||
read_from_pipe:
|
||||
|
||||
if (read(event->fd, &task, sizeof(task)) > 0)
|
||||
{
|
||||
ret = swWorker_onTask(factory, &task);
|
||||
#ifndef SW_WORKER_RECV_AGAIN
|
||||
/**
|
||||
* Big package
|
||||
*/
|
||||
if (task.info.type == SW_EVENT_PACKAGE_START)
|
||||
#endif
|
||||
{
|
||||
//no data
|
||||
if (ret < 0 && errno == EAGAIN)
|
||||
{
|
||||
return SW_OK;
|
||||
}
|
||||
else if (ret > 0)
|
||||
{
|
||||
goto read_from_pipe;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return SW_ERR;
|
||||
}
|
||||
|
||||
int swWorker_send2worker(swWorker *dst_worker, void *buf, int n, int flag)
|
||||
{
|
||||
int pipefd, ret;
|
||||
|
||||
if (flag & SW_PIPE_MASTER)
|
||||
{
|
||||
pipefd = dst_worker->pipe_master;
|
||||
}
|
||||
else
|
||||
{
|
||||
pipefd = dst_worker->pipe_worker;
|
||||
}
|
||||
|
||||
//message-queue
|
||||
if (dst_worker->pool->use_msgqueue)
|
||||
{
|
||||
struct
|
||||
{
|
||||
long mtype;
|
||||
swEventData buf;
|
||||
} msg;
|
||||
|
||||
msg.mtype = dst_worker->id + 1;
|
||||
memcpy(&msg.buf, buf, n);
|
||||
|
||||
return swMsgQueue_push(dst_worker->pool->queue, (swQueue_data *) &msg, n);
|
||||
}
|
||||
|
||||
if ((flag & SW_PIPE_NONBLOCK) && SwooleG.main_reactor)
|
||||
{
|
||||
return SwooleG.main_reactor->write(SwooleG.main_reactor, pipefd, buf, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = swSocket_write_blocking(pipefd, buf, n);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user