mirror of
https://github.com/lxsang/ant-http
synced 2024-11-13 23:38:21 +01:00
58a7738afe
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
- New plugin interface that supports multiple instances - Fix and improve memory bugs - Refactory and cleanup lib - Improve scheduler
1035 lines
28 KiB
C
1035 lines
28 KiB
C
#include "handle.h"
|
|
#include "utils.h"
|
|
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <poll.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
//open ssl
|
|
#ifdef USE_OPENSSL
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#endif
|
|
#ifdef USE_ZLIB
|
|
#include <zlib.h>
|
|
#endif
|
|
#ifdef USE_DB
|
|
#include "dbhelper.h"
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
|
|
#define HTML_TPL "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><h2>%s</h2></BODY></HTML>"
|
|
|
|
static const char *S_100 = "Continue";
|
|
static const char *S_101 = "Switching Protocols";
|
|
static const char *S_102 = "Processing";
|
|
static const char *S_103 = "Early Hints";
|
|
|
|
static const char *S_200 = "OK";
|
|
static const char *S_201 = "Created";
|
|
static const char *S_202 = "Accepted";
|
|
static const char *S_203 = "Non-Authoritative Information";
|
|
static const char *S_204 = "No Content";
|
|
static const char *S_205 = "Reset Content";
|
|
static const char *S_206 = "Partial Content";
|
|
static const char *S_207 = "Multi-Status";
|
|
static const char *S_208 = "Already Reported";
|
|
static const char *S_226 = "IM Used";
|
|
|
|
static const char *S_300 = "Multiple Choices";
|
|
static const char *S_301 = "Moved Permanently";
|
|
static const char *S_302 = "Found";
|
|
static const char *S_303 = "See Other";
|
|
static const char *S_304 = "Not Modified";
|
|
static const char *S_305 = "Use Proxy";
|
|
static const char *S_306 = "Switch Proxy";
|
|
static const char *S_307 = "Temporary Redirect";
|
|
static const char *S_308 = "Permanent Redirect";
|
|
|
|
static const char *S_400 = "Bad Request";
|
|
static const char *S_401 = "Unauthorized";
|
|
static const char *S_402 = "Payment Required";
|
|
static const char *S_403 = "Forbidden";
|
|
static const char *S_404 = "Not Found";
|
|
static const char *S_405 = "Method Not Allowed";
|
|
static const char *S_406 = "Not Acceptable";
|
|
static const char *S_407 = "Proxy Authentication Required";
|
|
static const char *S_408 = "Request Timeout";
|
|
static const char *S_409 = "Conflict";
|
|
static const char *S_410 = "Gone";
|
|
static const char *S_411 = "Length Required";
|
|
static const char *S_412 = "Precondition Failed";
|
|
static const char *S_413 = "Payload Too Large";
|
|
static const char *S_414 = "URI Too Long";
|
|
static const char *S_415 = "Unsupported Media Type";
|
|
static const char *S_416 = "Range Not Satisfiable";
|
|
static const char *S_417 = "Expectation Failed";
|
|
static const char *S_421 = "Misdirected Request";
|
|
static const char *S_422 = "Unprocessable Entity";
|
|
static const char *S_423 = "Locked";
|
|
static const char *S_424 = "Failed Dependency";
|
|
static const char *S_425 = "Too Early";
|
|
static const char *S_426 = "Upgrade Required";
|
|
static const char *S_428 = "Precondition Required";
|
|
static const char *S_429 = "Too Many Requests";
|
|
static const char *S_431 = "Request Header Fields Too Large";
|
|
static const char *S_451 = "Unavailable For Legal Reasons";
|
|
|
|
static const char *S_500 = "Internal Server Error";
|
|
static const char *S_501 = "Not Implemented";
|
|
static const char *S_502 = "Bad Gateway";
|
|
static const char *S_503 = "Service Unavailable";
|
|
static const char *S_504 = "Gateway Timeout";
|
|
static const char *S_505 = "HTTP Version Not Supported";
|
|
static const char *S_506 = "Variant Also Negotiates";
|
|
static const char *S_507 = "Insufficient Storage";
|
|
static const char *S_508 = "Loop Detected";
|
|
static const char *S_510 = "Not Extended";
|
|
static const char *S_511 = "Network Authentication Required";
|
|
static const char *S_UNOF = "Unofficial Status";
|
|
|
|
int require_plugin(const char *name)
|
|
{
|
|
UNUSED(name);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_ZLIB
|
|
static list_t g_gzip_types = NULL;
|
|
void set_gzip_types(list_t list)
|
|
{
|
|
g_gzip_types = list;
|
|
}
|
|
int compressable(char *ctype)
|
|
{
|
|
if (g_gzip_types == NULL)
|
|
return 0;
|
|
item_t it;
|
|
list_for_each(it, g_gzip_types)
|
|
{
|
|
if(it->type == LIST_TYPE_POINTER && it->value.ptr)
|
|
{
|
|
//LOG("Checking content type %s against GZIP support %s", ctype,(const char *)it->value.ptr);
|
|
if (regex_match((const char *)it->value.ptr, ctype, 0, NULL))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
const char *get_status_str(int stat)
|
|
{
|
|
switch (stat)
|
|
{
|
|
case 100:
|
|
return S_100;
|
|
case 101:
|
|
return S_101;
|
|
case 102:
|
|
return S_102;
|
|
case 103:
|
|
return S_103;
|
|
|
|
case 200:
|
|
return S_200;
|
|
case 201:
|
|
return S_201;
|
|
case 202:
|
|
return S_202;
|
|
case 203:
|
|
return S_203;
|
|
case 204:
|
|
return S_204;
|
|
case 205:
|
|
return S_205;
|
|
case 206:
|
|
return S_206;
|
|
case 207:
|
|
return S_207;
|
|
case 208:
|
|
return S_208;
|
|
case 226:
|
|
return S_226;
|
|
|
|
case 300:
|
|
return S_300;
|
|
case 301:
|
|
return S_301;
|
|
case 302:
|
|
return S_302;
|
|
case 303:
|
|
return S_303;
|
|
case 304:
|
|
return S_304;
|
|
case 305:
|
|
return S_305;
|
|
case 306:
|
|
return S_306;
|
|
case 307:
|
|
return S_307;
|
|
case 308:
|
|
return S_308;
|
|
|
|
case 400:
|
|
return S_400;
|
|
case 401:
|
|
return S_401;
|
|
case 402:
|
|
return S_402;
|
|
case 403:
|
|
return S_403;
|
|
case 404:
|
|
return S_404;
|
|
case 405:
|
|
return S_405;
|
|
case 406:
|
|
return S_406;
|
|
case 407:
|
|
return S_407;
|
|
case 408:
|
|
return S_408;
|
|
case 409:
|
|
return S_409;
|
|
case 410:
|
|
return S_410;
|
|
case 411:
|
|
return S_411;
|
|
case 412:
|
|
return S_412;
|
|
case 413:
|
|
return S_413;
|
|
case 414:
|
|
return S_414;
|
|
case 415:
|
|
return S_415;
|
|
case 416:
|
|
return S_416;
|
|
case 417:
|
|
return S_417;
|
|
case 421:
|
|
return S_421;
|
|
case 422:
|
|
return S_422;
|
|
case 423:
|
|
return S_423;
|
|
case 424:
|
|
return S_424;
|
|
case 425:
|
|
return S_425;
|
|
case 426:
|
|
return S_426;
|
|
case 428:
|
|
return S_428;
|
|
case 429:
|
|
return S_429;
|
|
case 431:
|
|
return S_431;
|
|
case 451:
|
|
return S_451;
|
|
|
|
case 500:
|
|
return S_500;
|
|
case 501:
|
|
return S_501;
|
|
case 502:
|
|
return S_502;
|
|
case 503:
|
|
return S_503;
|
|
case 504:
|
|
return S_504;
|
|
case 505:
|
|
return S_505;
|
|
case 506:
|
|
return S_506;
|
|
case 507:
|
|
return S_507;
|
|
case 508:
|
|
return S_508;
|
|
case 510:
|
|
return S_510;
|
|
case 511:
|
|
return S_511;
|
|
default:
|
|
return S_UNOF;
|
|
}
|
|
}
|
|
|
|
void antd_send_header(void *cl, antd_response_header_t *res)
|
|
{
|
|
if (!res->header)
|
|
res->header = dict();
|
|
antd_client_t *client = (antd_client_t *)cl;
|
|
#ifdef USE_ZLIB
|
|
antd_compress_t current_zlevel = client->z_level;
|
|
char *str = dvalue(res->header, "Content-Encoding");
|
|
if (!str)
|
|
{
|
|
// check for compress
|
|
str = dvalue(res->header, "Content-Type");
|
|
if (str)
|
|
{
|
|
if (compressable(str) && client->z_level != ANTD_CNONE)
|
|
{
|
|
client->zstream = (z_stream *)malloc(sizeof(z_stream));
|
|
if (client->zstream)
|
|
{
|
|
((z_stream *)client->zstream)->zalloc = Z_NULL;
|
|
((z_stream *)client->zstream)->zfree = Z_NULL;
|
|
((z_stream *)client->zstream)->opaque = Z_NULL;
|
|
if (client->z_level == ANTD_CGZ)
|
|
{
|
|
if (deflateInit2(client->zstream, Z_BEST_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
|
|
{
|
|
ERROR("Cannot init gzip stream");
|
|
free(client->zstream);
|
|
client->zstream = NULL;
|
|
}
|
|
else
|
|
{
|
|
LOG("Gzip enable on stream %d", client->sock);
|
|
client->z_status = Z_NO_FLUSH;
|
|
dput(res->header, "Content-Encoding", strdup("gzip"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (deflateInit(client->zstream, Z_BEST_COMPRESSION) != Z_OK)
|
|
{
|
|
ERROR("Cannot init deflate stream");
|
|
free(client->zstream);
|
|
client->zstream = NULL;
|
|
}
|
|
else
|
|
{
|
|
LOG("deflate enable on stream %d", client->sock);
|
|
client->z_status = Z_NO_FLUSH;
|
|
dput(res->header, "Content-Encoding", strdup("deflate"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
client->z_level = ANTD_CNONE;
|
|
#endif
|
|
dput(res->header, "Server", strdup(SERVER_NAME));
|
|
const char *stat_str = get_status_str(res->status);
|
|
__t(client, "HTTP/1.1 %d %s", res->status, stat_str);
|
|
chain_t it;
|
|
for_each_assoc(it, res->header)
|
|
{
|
|
__t(client, "%s: %s", it->key, (const char *)it->value);
|
|
}
|
|
// send out cookie
|
|
if (res->cookie)
|
|
{
|
|
item_t el;
|
|
list_for_each(el, res->cookie)
|
|
{
|
|
if (el->type == LIST_TYPE_POINTER && el->value.ptr)
|
|
{
|
|
__t(client, "Set-Cookie: %s", (char *)el->value.ptr);
|
|
}
|
|
}
|
|
list_free(&res->cookie);
|
|
res->cookie = NULL;
|
|
}
|
|
__b(client, (unsigned char *)"\r\n", 2);
|
|
#ifdef USE_ZLIB
|
|
client->z_level = current_zlevel;
|
|
#endif
|
|
freedict(res->header);
|
|
res->header = NULL;
|
|
}
|
|
|
|
/*
|
|
void octstream(void* client, char* name)
|
|
{
|
|
set_status(client,200,"OK");
|
|
__t(client,"Content-Type: application/octet-stream");
|
|
__t(client,"Content-Disposition: attachment; filename=\"%s\"", name);
|
|
response(client,"");
|
|
//Content-Disposition: attachment; filename="fname.ext"
|
|
}*/
|
|
|
|
int antd_send(void *src, const void *data_in, int len_in)
|
|
{
|
|
uint8_t *data = (uint8_t *)data_in;
|
|
int len = len_in;
|
|
antd_client_t *source = (antd_client_t *)src;
|
|
if (!src || !data)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#ifdef USE_ZLIB
|
|
if (source->zstream && source->z_level != ANTD_CNONE)
|
|
{
|
|
antd_compress_t current_zlevel = source->z_level;
|
|
source->z_level = ANTD_CNONE;
|
|
uint8_t buf[BUFFLEN];
|
|
z_stream *zstream = (z_stream *)source->zstream;
|
|
zstream->avail_in = (uInt)len;
|
|
zstream->next_in = (Bytef *)data_in;
|
|
len = 0;
|
|
int have = 0;
|
|
do
|
|
{
|
|
zstream->avail_out = BUFFLEN;
|
|
zstream->next_out = buf;
|
|
if (deflate(zstream, source->z_status) == Z_STREAM_ERROR)
|
|
{
|
|
source->z_level = current_zlevel;
|
|
data = NULL;
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
have = BUFFLEN - zstream->avail_out;
|
|
antd_send(source, buf, have);
|
|
len += have;
|
|
}
|
|
} while (zstream->avail_out == 0);
|
|
source->z_level = current_zlevel;
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
int written;
|
|
char *ptr;
|
|
int writelen = 0;
|
|
int count;
|
|
|
|
#ifdef USE_OPENSSL
|
|
if (source->ssl)
|
|
{
|
|
//LOG("SSL WRITE\n");
|
|
//ret = SSL_write((SSL*) source->ssl, data, len);
|
|
ptr = (char *)data;
|
|
writelen = len > BUFFLEN ? BUFFLEN : len;
|
|
written = 0;
|
|
struct pollfd pfd;
|
|
while (writelen > 0) //source->attempt < MAX_ATTEMPT
|
|
{
|
|
// clear the error queue
|
|
ERR_clear_error();
|
|
count = SSL_write(source->ssl, ptr + written, writelen);
|
|
int err = SSL_get_error(source->ssl, count);
|
|
if (count > 0)
|
|
{
|
|
written += count;
|
|
writelen = (len - written) > BUFFLEN ? BUFFLEN : (len - written);
|
|
time(&source->last_io);
|
|
}
|
|
else
|
|
{
|
|
if (difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME)
|
|
{
|
|
if (written == 0)
|
|
written = count;
|
|
break;
|
|
}
|
|
//printf(" received equal to or less than 0\n")
|
|
switch (err)
|
|
{
|
|
case SSL_ERROR_NONE:
|
|
{
|
|
// no real error, just try again...
|
|
//LOG("SSL_ERROR_NONE \n");
|
|
//source->attempt++;
|
|
continue;
|
|
}
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
{
|
|
// peer disconnected...
|
|
// ERROR("SSLWRITE: SSL_ERROR_ZERO_RETURN: peer disconected: %d", source->sock);
|
|
break;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
{
|
|
// no data available right now, wait a few seconds in case new data arrives...
|
|
//printf("SSL_ERROR_WANT_READ\n");
|
|
|
|
int sock = SSL_get_rfd(source->ssl);
|
|
pfd.fd = sock;
|
|
pfd.events = POLLIN;
|
|
err = poll(&pfd, 1, 500);
|
|
if (err == 0 || (err > 0 && (pfd.revents & POLLIN)))
|
|
{
|
|
//source->attempt++;
|
|
continue; // more data to read...
|
|
}
|
|
//ERROR("SSL WRITE: want read but select error on the socket %d: %s", source->sock, strerror(errno));
|
|
break;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
{
|
|
// socket not writable right now, wait a few seconds and try again...
|
|
//printf("SSL_ERROR_WANT_WRITE \n");
|
|
int sock = SSL_get_wfd(source->ssl);
|
|
pfd.fd = sock;
|
|
pfd.events = POLLOUT;
|
|
err = poll(&pfd, 1, 500);
|
|
if (err == 0 || (err > 0 && (pfd.revents & POLLOUT)))
|
|
{
|
|
//source->attempt++;
|
|
continue; // can write more data now...
|
|
}
|
|
//ERROR("SSL WRITE: want write but select error on the socket %d: %s", source->sock, strerror(errno));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// other error
|
|
//ERROR("SSL WRITE: Unknown error on %d: %s", source->sock, ERR_error_string(ERR_get_error(), NULL) );
|
|
break;
|
|
}
|
|
}
|
|
if (written == 0)
|
|
written = count;
|
|
break;
|
|
}
|
|
}
|
|
//source->attempt = 0;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ptr = (char *)data;
|
|
writelen = len > BUFFLEN ? BUFFLEN : len;
|
|
written = 0;
|
|
while (writelen > 0)
|
|
{
|
|
count = send(source->sock, ptr + written, writelen, 0);
|
|
if (count > 0)
|
|
{
|
|
written += count;
|
|
writelen = (len - written) > BUFFLEN ? BUFFLEN : (len - written);
|
|
time(&source->last_io);
|
|
}
|
|
else if ((difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME) || (count == -1 && errno != EAGAIN && errno != EWOULDBLOCK))
|
|
{
|
|
if (written == 0)
|
|
written = count;
|
|
//ERROR("Error while writing: %s", strerror(errno));
|
|
break;
|
|
//return written;
|
|
}
|
|
}
|
|
#ifdef USE_OPENSSL
|
|
}
|
|
#endif
|
|
|
|
return written;
|
|
}
|
|
/**
|
|
* Read up to n bytes, not guaranty to have exactly nbytes
|
|
* - return -1 if false
|
|
* */
|
|
int antd_recv_upto(void *src, void *data, int len)
|
|
{
|
|
if (!src)
|
|
return -1;
|
|
int received = 0;
|
|
antd_client_t *source = (antd_client_t *)src;
|
|
#ifdef USE_OPENSSL
|
|
if (source->ssl)
|
|
{
|
|
ERR_clear_error();
|
|
received = SSL_read(source->ssl, data, len);
|
|
int err = SSL_get_error(source->ssl, received);
|
|
if (received > 0)
|
|
{
|
|
time(&source->last_io);
|
|
return received;
|
|
}
|
|
else
|
|
{
|
|
/*if (difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME)
|
|
{
|
|
return -1;
|
|
}*/
|
|
switch (err)
|
|
{
|
|
case SSL_ERROR_NONE:
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
{
|
|
// peer disconnected...
|
|
return -1;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
received = recv(((int)source->sock), data, len, 0);
|
|
//LOG("Read : %c\n", *ptr);
|
|
if (received > 0)
|
|
{
|
|
time(&source->last_io);
|
|
return received;
|
|
}
|
|
/*else if (difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME)
|
|
{
|
|
return -1;
|
|
}*/
|
|
if (received < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
{
|
|
return 0;
|
|
}
|
|
return -1;
|
|
#ifdef USE_OPENSSL
|
|
}
|
|
#endif
|
|
}
|
|
int antd_recv(void *src, void *data, int len)
|
|
{
|
|
if (!src)
|
|
return -1;
|
|
int read = 0;
|
|
char *ptr = NULL;
|
|
int received = 0;
|
|
int readlen = 0;
|
|
antd_client_t *source = (antd_client_t *)src;
|
|
#ifdef USE_OPENSSL
|
|
if (source->ssl)
|
|
{
|
|
ptr = (char *)data;
|
|
readlen = len > BUFFLEN ? BUFFLEN : len;
|
|
read = 0;
|
|
struct pollfd pfd;
|
|
while (readlen > 0) //&& source->attempt < MAX_ATTEMPT
|
|
{
|
|
ERR_clear_error();
|
|
received = SSL_read(source->ssl, ptr + read, readlen);
|
|
int err = SSL_get_error(source->ssl, received);
|
|
if (received > 0)
|
|
{
|
|
read += received;
|
|
readlen = (len - read) > BUFFLEN ? BUFFLEN : (len - read);
|
|
time(&source->last_io);
|
|
}
|
|
else
|
|
{
|
|
// Timeout, quit
|
|
if (difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME)
|
|
{
|
|
if (read == 0)
|
|
read = received;
|
|
break;
|
|
}
|
|
//printf(" received equal to or less than 0\n")
|
|
switch (err)
|
|
{
|
|
case SSL_ERROR_NONE:
|
|
{
|
|
// no real error, just try again...
|
|
//LOG("SSL_ERROR_NONE \n");
|
|
//source->attempt++;
|
|
continue;
|
|
}
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
{
|
|
// peer disconnected...
|
|
//ERROR("SSL READ: SSL_ERROR_ZERO_RETURN, peer disconnected %d", source->sock);
|
|
break;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
{
|
|
// no data available right now, wait a few seconds in case new data arrives...
|
|
//printf("SSL_ERROR_WANT_READ\n");
|
|
|
|
int sock = SSL_get_rfd(source->ssl);
|
|
pfd.fd = sock;
|
|
pfd.events = POLLIN;
|
|
err = poll(&pfd, 1, 500);
|
|
if (err == 0 || (err > 0 && (pfd.revents & POLLIN)))
|
|
{
|
|
//source->attempt++;
|
|
continue; // more data to read...
|
|
}
|
|
//ERROR("SSL READ: want read but select error on the socket %d: %s", source->sock, strerror(errno));
|
|
break;
|
|
}
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
{
|
|
// socket not writable right now, wait a few seconds and try again...
|
|
//printf("SSL_ERROR_WANT_WRITE \n");
|
|
int sock = SSL_get_wfd(source->ssl);
|
|
pfd.fd = sock;
|
|
pfd.events = POLLOUT;
|
|
err = poll(&pfd, 1, 500);
|
|
if (err == 0 || (err > 0 && (pfd.revents & POLLOUT)))
|
|
{
|
|
//source->attempt++;
|
|
continue; // can write more data now...
|
|
}
|
|
//ERROR("SSL READ: want write but select error on the socket %d: %s", source->sock, strerror(errno));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// other error
|
|
//ERROR("SSL READ: unkown error on %d: %s", source->sock, ERR_error_string(ERR_get_error(), NULL));
|
|
break;
|
|
}
|
|
}
|
|
if (read == 0)
|
|
read = received;
|
|
break;
|
|
}
|
|
}
|
|
//source->attempt = 0;
|
|
/*
|
|
int stat, r, st;
|
|
do{
|
|
ret = SSL_read((SSL*) source->ssl, data, len);
|
|
stat = SSL_get_error((SSL*)source->ssl, r);
|
|
} while(ret == -1 &&
|
|
(
|
|
stat == SSL_ERROR_WANT_READ ||
|
|
stat == SSL_ERROR_WANT_WRITE ||
|
|
stat == SSL_ERROR_NONE ||
|
|
(stat == SSL_ERROR_SYSCALL && r== 0 && !ERR_get_error())
|
|
));
|
|
if(ret == -1)
|
|
{
|
|
LOG("Problem reading %d %d %d\n", ret, stat, r);
|
|
}
|
|
//set_nonblock(source->sock);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ptr = (char *)data;
|
|
readlen = len > BUFFLEN ? BUFFLEN : len;
|
|
read = 0;
|
|
while (readlen > 0)
|
|
{
|
|
received = recv(((int)source->sock), ptr + read, readlen, 0);
|
|
//LOG("Read : %c\n", *ptr);
|
|
if (received > 0)
|
|
{
|
|
read += received;
|
|
readlen = (len - read) > BUFFLEN ? BUFFLEN : (len - read);
|
|
time(&source->last_io);
|
|
//LOG("Read len is %d\n", readlen);
|
|
}
|
|
else if (difftime(time(NULL), source->last_io) > MAX_IO_WAIT_TIME || (errno != EAGAIN && errno != EWOULDBLOCK))
|
|
{
|
|
//ERROR("Error while reading: %s", strerror(errno));
|
|
if (read == 0)
|
|
read = received;
|
|
break;
|
|
}
|
|
}
|
|
//read = recv(((int) source->sock), data, len, 0);
|
|
#ifdef USE_OPENSSL
|
|
}
|
|
#endif
|
|
//LOG("Received %d bytes\n", read);
|
|
/*if(ret == 0)
|
|
{
|
|
antd_close(src);
|
|
}*/
|
|
return read;
|
|
}
|
|
void set_nonblock(int socket)
|
|
{
|
|
int flags;
|
|
flags = fcntl(socket, F_GETFL, 0);
|
|
//assert(flags != -1);
|
|
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
|
|
}
|
|
/*void set_block()
|
|
{
|
|
int flags;
|
|
flags = fcntl(socket,F_GETFL,0);
|
|
//assert(flags != -1);
|
|
fcntl(socket, F_SETFL, flags & (~O_NONBLOCK));
|
|
}*/
|
|
int antd_close(void *src)
|
|
{
|
|
if (!src)
|
|
return -1;
|
|
antd_client_t *source = (antd_client_t *)src;
|
|
#ifdef USE_ZLIB
|
|
//TODO: send finish data to the socket before quit
|
|
if (source->zstream)
|
|
{
|
|
if (source->z_status == Z_NO_FLUSH && source->z_level != ANTD_CNONE)
|
|
{
|
|
source->z_status = Z_FINISH;
|
|
antd_send(source, "", 0);
|
|
}
|
|
deflateEnd(source->zstream);
|
|
free(source->zstream);
|
|
source->zstream = NULL;
|
|
source->z_level = ANTD_CNONE;
|
|
}
|
|
#endif
|
|
#ifdef USE_OPENSSL
|
|
if (source->ssl)
|
|
{
|
|
//printf("SSL:Shutdown ssl\n");
|
|
//SSL_shutdown((SSL*) source->ssl);
|
|
SSL_set_shutdown((SSL *)source->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
|
//printf("SSL:Free ssl\n");
|
|
SSL_free((SSL *)source->ssl);
|
|
|
|
//EVP_cleanup();
|
|
//ENGINE_cleanup();
|
|
CRYPTO_cleanup_all_ex_data();
|
|
ERR_remove_state(0);
|
|
ERR_free_strings();
|
|
source->ssl = NULL;
|
|
//LOG("Freeing SSL\n");
|
|
}
|
|
#endif
|
|
//printf("Close sock %d\n", source->sock);
|
|
int ret = close(source->sock);
|
|
free(src);
|
|
src = NULL;
|
|
return ret;
|
|
}
|
|
|
|
int __t(void *client, const char *fstring, ...)
|
|
{
|
|
int dlen;
|
|
int st;
|
|
va_list arguments;
|
|
char *data;
|
|
va_start(arguments, fstring);
|
|
dlen = vsnprintf(0, 0, fstring, arguments) + 1;
|
|
va_end(arguments);
|
|
if ((data = (char *)malloc(dlen * sizeof(char))) != 0)
|
|
{
|
|
va_start(arguments, fstring);
|
|
vsnprintf(data, dlen, fstring, arguments);
|
|
va_end(arguments);
|
|
st = __b(client, (const unsigned char *)data, strlen(data));
|
|
if (st)
|
|
__b(client, (unsigned char *)"\r\n", 2);
|
|
free(data);
|
|
return st;
|
|
}
|
|
return 0;
|
|
//
|
|
}
|
|
int __b(void *client, const unsigned char *data, int size)
|
|
{
|
|
int sent = 0;
|
|
int buflen = 0;
|
|
int nbytes = 0;
|
|
char *ptr = (char *)data;
|
|
/*if(size <= BUFFLEN)
|
|
{
|
|
nbytes = antd_send(client,data,size);
|
|
return (nbytes==-1?0:1);
|
|
}
|
|
else
|
|
{*/
|
|
while (sent < size)
|
|
{
|
|
if (size - sent > BUFFLEN)
|
|
buflen = BUFFLEN;
|
|
else
|
|
buflen = size - sent;
|
|
nbytes = antd_send(client, ptr, buflen);
|
|
if (nbytes == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
sent += buflen;
|
|
ptr += buflen;
|
|
}
|
|
//}
|
|
return 1;
|
|
}
|
|
int __f(void *client, const char *file)
|
|
{
|
|
unsigned char buffer[BUFFLEN];
|
|
int fd;
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
LOG("Cannot read : %s", file);
|
|
return 0;
|
|
}
|
|
int size;
|
|
while ((size = read(fd, buffer, BUFFLEN)) > 0)
|
|
{
|
|
if (antd_send(client, buffer, size) == -1)
|
|
return 0;
|
|
}
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
|
|
int upload(const char *tmp, const char *path)
|
|
{
|
|
return !rename(tmp, path);
|
|
}
|
|
|
|
/*
|
|
void set_cookie(void* client,const char* type, dictionary_t dic, const char* name)
|
|
{
|
|
set_status(client,200,"OK");
|
|
__t(client,"Content-Type: %s",type);
|
|
chain_t assoc;
|
|
for_each_assoc(assoc,dic){
|
|
__t(client,"Set-Cookie: %s=%s; Path=/%s",assoc->key, (char*)assoc->value, name);
|
|
}
|
|
response(client,"");
|
|
}
|
|
void clear_cookie(void* client, dictionary_t dic)
|
|
{
|
|
set_status(client,200,"OK");
|
|
__t(client,"Content-Type: text/html; charset=utf-8");
|
|
chain_t assoc;
|
|
for_each_assoc(assoc,dic){
|
|
__t(client,"Set-Cookie: %s=%s;expires=%s",assoc->key, (char*)assoc->value, server_time());
|
|
}
|
|
response(client,"");
|
|
}
|
|
*/
|
|
|
|
void antd_error(void *client, int status, const char *msg)
|
|
{
|
|
antd_response_header_t rsh;
|
|
rsh.header = dict();
|
|
rsh.cookie = NULL;
|
|
const char *stat_str = get_status_str(status);
|
|
rsh.status = status;
|
|
char *ctype = "text/html; charset=utf-8";
|
|
dput(rsh.header, "Content-Type", strdup(ctype));
|
|
char *res_str = __s(HTML_TPL, stat_str, msg);
|
|
int clen = 0;
|
|
if (res_str)
|
|
{
|
|
clen = strlen(res_str);
|
|
}
|
|
char ibuf[20];
|
|
snprintf(ibuf, sizeof(ibuf), "%d", clen);
|
|
#ifdef USE_ZLIB
|
|
if (((antd_client_t *)client)->z_level == ANTD_CNONE || !compressable(ctype))
|
|
#endif
|
|
dput(rsh.header, "Content-Length", strdup(ibuf));
|
|
antd_send_header(client, &rsh);
|
|
if (res_str)
|
|
{
|
|
//printf("%s\n", res_str);
|
|
__b(client, (unsigned char *)res_str, clen);
|
|
//__t(client, HTML_TPL, stat_str, msg);
|
|
free(res_str);
|
|
}
|
|
}
|
|
|
|
int ws_enable(dictionary_t dic)
|
|
{
|
|
if (!dic)
|
|
return 0;
|
|
char *v = (char *)dvalue(dic, "__web_socket__");
|
|
if (!v)
|
|
return 0;
|
|
return atoi(v) == 1;
|
|
}
|
|
|
|
/**
|
|
* Read the socket request in to a buffer or size
|
|
* The data is read until the buffer is full or
|
|
* there are a carrier return character
|
|
* @param sock socket
|
|
* @param buf buffer
|
|
* @param size size of buffer
|
|
* @return number of bytes read
|
|
*/
|
|
int read_buf(void *sock, char *buf, int size)
|
|
{
|
|
int i = 0;
|
|
char c = '\0';
|
|
int n;
|
|
while ((i < size - 1) && (c != '\n'))
|
|
{
|
|
n = antd_recv(sock, &c, 1);
|
|
if (n > 0)
|
|
{
|
|
//LOG("Data : %c\n", c);
|
|
buf[i] = c;
|
|
i++;
|
|
}
|
|
else
|
|
c = '\n';
|
|
}
|
|
buf[i] = '\0';
|
|
return i;
|
|
}
|
|
/*
|
|
We put it here since we want the plugin is able
|
|
to destroy the request if it want to
|
|
in this case, the plugin should return an empty
|
|
with no data
|
|
*/
|
|
void destroy_request(void *data)
|
|
{
|
|
if (!data)
|
|
return;
|
|
antd_request_t *rq = (antd_request_t *)data;
|
|
//LOG("Close request %d", rq->client->sock);
|
|
// free all other thing
|
|
if (rq->request)
|
|
{
|
|
dictionary_t tmp = dvalue(rq->request, "COOKIE");
|
|
if (tmp)
|
|
freedict(tmp);
|
|
tmp = dvalue(rq->request, "REQUEST_HEADER");
|
|
if (tmp)
|
|
freedict(tmp);
|
|
tmp = dvalue(rq->request, "REQUEST_DATA");
|
|
if (tmp)
|
|
freedict(tmp);
|
|
dput(rq->request, "REQUEST_HEADER", NULL);
|
|
dput(rq->request, "REQUEST_DATA", NULL);
|
|
dput(rq->request, "COOKIE", NULL);
|
|
freedict(rq->request);
|
|
}
|
|
antd_close(rq->client);
|
|
free(rq);
|
|
}
|