#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif
#include <unistd.h>
#include <stdlib.h>
#ifdef XP_UNIX
#include <sys/uio.h>
#include <sys/uio.h>
#endif
#ifdef XP_WIN32
#endif
#if defined(
LINUX) || defined(
SOLARIS)
#include <sys/sendfile.h>
#define WS_SENDFILE
#elif defined(
BSD)
#if defined(__NetBSD__) || defined(__OpenBSD__)
#define net_sys_sendfile net_fallback_sendfile
#else
#define WS_SENDFILE
#endif
#endif
#ifdef WS_SENDFILE
#define NET_SYS_SENDFILE net_sys_sendfile
#else
#define NET_SYS_SENDFILE net_fallback_sendfile
#endif
#include "../daemon/vfs.h"
#include "io.h"
#include "pool.h"
#include "../daemon/netsite.h"
#include "../daemon/event.h"
#include "ucx/utils.h"
IOStream native_io_funcs = {
(io_write_f)net_sys_write,
(io_writev_f)net_sys_writev,
(io_read_f)net_sys_read,
(io_sendfile_f)
NET_SYS_SENDFILE,
(io_close_f)net_sys_close,
NULL,
(io_setmode_f)net_sys_setmode,
(io_poll_f)net_sys_poll,
0
};
IOStream http_io_funcs = {
(io_write_f)net_http_write,
(io_writev_f)net_http_writev,
(io_read_f)net_http_read,
(io_sendfile_f)net_http_sendfile,
(io_close_f)net_http_close,
(io_finish_f)net_http_finish,
(io_setmode_f)net_http_setmode,
(io_poll_f)net_http_poll,
0
};
IOStream ssl_io_funcs = {
(io_write_f)net_ssl_write,
(io_writev_f)net_ssl_writev,
(io_read_f)net_ssl_read,
NULL,
(io_close_f)net_ssl_close,
(io_finish_f)net_ssl_finish,
(io_setmode_f)net_ssl_setmode,
(io_poll_f)net_ssl_poll,
0
};
IOStream* sysstream_new(
pool_handle_t *pool,
SYS_SOCKET fd) {
SysStream *st = pool_malloc(pool,
sizeof(SysStream));
st->st = native_io_funcs;
st->fd = fd;
return (IOStream*)st;
}
#ifdef XP_UNIX
ssize_t net_sys_write(SysStream *st,
void *buf,
size_t nbytes) {
return write(st->fd, buf, nbytes);
}
ssize_t net_sys_writev(SysStream *st,
struct iovec *iovec,
int iovcnt) {
return writev(st->fd, iovec, iovcnt);
}
ssize_t net_sys_read(SysStream *st,
void *buf,
size_t nbytes) {
return read(st->fd, buf, nbytes);
}
#ifdef WS_SENDFILE
ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) {
ssize_t ret =
0;
off_t fileoffset = sfd->offset;
if(sfd->fd->fd != -
1) {
#ifdef BSD
struct iovec hdvec;
hdvec.iov_base = (
void*)sfd->header;
hdvec.iov_len = sfd->hlen;
struct iovec trvec;
trvec.iov_base = (
void*)sfd->trailer;
trvec.iov_len = sfd->tlen;
struct sf_hdtr hdtr;
hdtr.headers = &hdvec;
hdtr.hdr_cnt =
1;
hdtr.trailers = &trvec;
hdtr.trl_cnt =
1;
off_t len = sfd->len;
#ifdef OSX
ret = sendfile(sfd->fd->fd, st->fd, fileoffset, &len, &hdtr,
0);
#else
ret = sendfile(
sfd->fd->fd,
st->fd,
fileoffset,
sfd->len,
&hdtr,
NULL,
0);
#endif
#else
if(sfd->header) {
ret += write(st->fd, sfd->header, sfd->hlen);
}
ret += sendfile(st->fd, sfd->fd->fd, &fileoffset, sfd->len);
if(sfd->trailer) {
ret += write(st->fd, sfd->trailer, sfd->tlen);
}
#endif
}
else {
return net_fallback_sendfile((IOStream*)st, sfd);
}
return ret;
}
#endif
void net_sys_close(SysStream *st) {
system_close(st->fd);
}
void net_sys_setmode(SysStream *st,
int mode) {
int flags;
if (-
1 == (flags = fcntl(st->fd,
F_GETFL,
0))) {
flags =
0;
}
if(mode ==
IO_MODE_BLOCKING) {
if (fcntl(st->fd,
F_SETFL, flags & ~
O_NONBLOCK) !=
0) {
perror(
"fcntl");
}
}
else if(mode ==
IO_MODE_NONBLOCKING) {
if (fcntl(st->fd,
F_SETFL, flags |
O_NONBLOCK) !=
0) {
perror(
"fcntl");
}
}
}
int net_sys_poll(SysStream *st, EventHandler *ev,
int events, Event *cb) {
switch(events) {
default:
return -
1;
case IO_POLL_NONE:
return ev_remove_poll(ev, st->fd);
case IO_POLL_IN:
return ev_pollin(ev, st->fd, cb);
case IO_POLL_OUT:
return ev_pollout(ev, st->fd, cb);
case IO_POLL_IN |
IO_POLL_OUT:
return -
1;
}
}
#elif defined(
XP_WIN32)
ssize_t net_sys_write(SysStream *st,
void *buf,
size_t nbytes) {
int ret = send(st->fd, buf, nbytes,
0);
if(ret ==
SOCKET_ERROR) {
return IO_ERROR;
}
return ret;
}
ssize_t net_sys_writev(SysStream *st,
struct iovec *iovec,
int iovcnt) {
}
ssize_t net_sys_read(SysStream *st,
void *buf,
size_t nbytes) {
int ret = recv(st->fd, buf, nbytes,
0);
if(ret ==
SOCKET_ERROR) {
return IO_ERROR;
}
return ret;
}
ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) {
}
void net_sys_close(SysStream *st) {
closesocket(st->fd);
}
#endif
IOStream* httpstream_new(
pool_handle_t *pool, IOStream *fd) {
HttpStream *st = pool_malloc(pool,
sizeof(HttpStream));
st->st = http_io_funcs;
st->fd = fd;
st->max_read =
0;
st->read =
0;
st->chunked_enc =
WS_FALSE;
st->buffered =
WS_FALSE;
return (IOStream*)st;
}
ssize_t net_http_write(HttpStream *st,
void *buf,
size_t nbytes) {
IOStream *fd = st->fd;
if(st->chunked_enc) {
struct iovec io[
3];
char chunk_len[
16];
io[
0].iov_base = chunk_len;
io[
0].iov_len = snprintf(chunk_len,
16,
"%zx\r\n", nbytes);
io[
1].iov_base = buf;
io[
1].iov_len = nbytes;
io[
2].iov_base =
"\r\n";
io[
2].iov_len =
2;
ssize_t r = fd->writev(fd, io,
3);
return r - io[
0].iov_len;
}
else {
return fd->write(fd, buf, nbytes);
}
}
ssize_t net_http_writev(HttpStream *st,
struct iovec *iovec,
int iovcnt) {
IOStream *fd = st->fd;
if(st->chunked_enc) {
struct iovec *io = calloc(iovcnt +
1,
sizeof(
struct iovec));
char chunk_len[
16];
io[
0].iov_base = chunk_len;
size_t len =
0;
for(
int i=
0;i<iovcnt;i++) {
len += iovec[i].iov_len;
}
io[
0].iov_len = snprintf(chunk_len,
16,
"\r\n%zx\r\n", len);
memcpy(io +
1, iovec, iovcnt *
sizeof(
struct iovec));
ssize_t r = fd->writev(fd, io, iovcnt +
1);
return r - io[
0].iov_len;
}
else {
return fd->writev(fd, iovec, iovcnt);
}
}
ssize_t net_http_read(HttpStream *st,
void *buf,
size_t nbytes) {
if(st->max_read !=
0 && st->read >= st->max_read) {
return 0;
}
ssize_t r = st->fd->read(st->fd, buf, nbytes);
st->read += r;
return r;
}
ssize_t net_http_sendfile(HttpStream *st, sendfiledata *sfd) {
ssize_t ret =
0;
if(st->fd->sendfile) {
ret = st->fd->sendfile(st->fd, sfd);
}
else {
ret = net_fallback_sendfile((IOStream*)st, sfd);
}
return ret;
}
void net_http_close(HttpStream *st) {
st->fd->close(st->fd);
}
void net_http_finish(HttpStream *st) {
if(st->chunked_enc) {
st->fd->write(st->fd,
"0\r\n\r\n",
5);
}
}
void net_http_setmode(HttpStream *st,
int mode) {
st->fd->setmode(st->fd, mode);
}
int net_http_poll(HttpStream *st, EventHandler *ev,
int events, Event *cb) {
return st->fd->poll(st->fd, ev, events, cb);
}
IOStream* sslstream_new(
pool_handle_t *pool,
SSL *ssl) {
SSLStream *st = pool_malloc(pool,
sizeof(SSLStream));
st->st = ssl_io_funcs;
st->ssl = ssl;
st->error =
0;
return (IOStream*)st;
}
ssize_t net_ssl_write(SSLStream *st,
void *buf,
size_t nbytes) {
int ret = SSL_write(st->ssl, buf, nbytes);
if(ret <=
0) {
st->error = SSL_get_error(st->ssl, ret);
}
return ret;
}
ssize_t net_ssl_writev(SSLStream *st,
struct iovec *iovec,
int iovcnt) {
ssize_t r =
0;
for(
int i=
0;i<iovcnt;i++) {
int ret = SSL_write(st->ssl, iovec[i].iov_base, iovec[i].iov_len);
if(ret <=
0) {
st->error = SSL_get_error(st->ssl, ret);
return 0;
}
r += ret;
}
return r;
}
ssize_t net_ssl_read(SSLStream *st,
void *buf,
size_t nbytes) {
int ret = SSL_read(st->ssl, buf, nbytes);
if(ret <=
0) {
st->error = SSL_get_error(st->ssl, ret);
}
return ret;
}
void net_ssl_close(SSLStream *st) {
int ret = SSL_shutdown(st->ssl);
if(ret !=
1) {
st->error = SSL_get_error(st->ssl, ret);
}
system_close(SSL_get_fd(st->ssl));
}
void net_ssl_finish(SSLStream *st) {
}
void net_ssl_setmode(SSLStream *st,
int mode) {
int flags;
if (-
1 == (flags = fcntl(SSL_get_fd(st->ssl),
F_GETFL,
0))) {
flags =
0;
}
if(mode ==
IO_MODE_BLOCKING) {
if (fcntl(SSL_get_fd(st->ssl),
F_SETFL, flags & ~
O_NONBLOCK) !=
0) {
perror(
"fcntl");
}
}
else if(mode ==
IO_MODE_NONBLOCKING) {
if (fcntl(SSL_get_fd(st->ssl),
F_SETFL, flags |
O_NONBLOCK) !=
0) {
perror(
"fcntl");
}
}
}
int net_ssl_poll(SSLStream *st, EventHandler *ev,
int events, Event *cb) {
int fd = SSL_get_fd(st->ssl);
switch(events) {
default:
return -
1;
case IO_POLL_NONE:
return ev_remove_poll(ev, fd);
case IO_POLL_IN:
return ev_pollin(ev, fd, cb);
case IO_POLL_OUT:
return ev_pollout(ev, fd, cb);
case IO_POLL_IN |
IO_POLL_OUT:
return -
1;
}
}
ssize_t net_read(
SYS_NETFD fd,
void *buf,
size_t nbytes) {
ssize_t r = ((IOStream*)fd)->read(fd, buf, nbytes);
if(r ==
0) {
return IO_EOF;
}
else if(r <
0) {
((IOStream*)fd)->io_errno = errno;
return IO_ERROR;
}
return r;
}
ssize_t net_write(
SYS_NETFD fd,
void *buf,
size_t nbytes) {
ssize_t r = ((IOStream*)fd)->write(fd, buf, nbytes);
if(r <
0) {
((IOStream*)fd)->io_errno = errno;
return IO_ERROR;
}
return r;
}
ssize_t net_writev(
SYS_NETFD fd,
struct iovec *iovec,
int iovcnt) {
ssize_t r = ((IOStream*)fd)->writev(fd, iovec, iovcnt);
if(r <
0) {
((IOStream*)fd)->io_errno = errno;
return IO_ERROR;
}
return r;
}
ssize_t net_printf(
SYS_NETFD fd,
char *format, ...) {
va_list arg;
va_start(arg, format);
sstr_t buf = ucx_vasprintf(ucx_default_allocator(), format, arg);
ssize_t r = net_write(fd, buf.ptr, buf.length);
free(buf.ptr);
va_end(arg);
if(r <
0) {
((IOStream*)fd)->io_errno = errno;
}
return r;
}
ssize_t net_sendfile(
SYS_NETFD fd, sendfiledata *sfd) {
IOStream *out = fd;
if(out->sendfile && sfd->fd && sfd->fd->fd != -
1) {
ssize_t r = out->sendfile(fd, sfd);
if(r <
0) {
out->io_errno = errno;
return IO_ERROR;
}
return r;
}
else {
return net_fallback_sendfile(out, sfd);
}
}
ssize_t net_fallback_sendfile(IOStream *fd, sendfiledata *sfd) {
char *buf = malloc(
4096);
if(!buf) {
return IO_ERROR;
}
char *header = (
char*)sfd->header;
int hlen = sfd->hlen;
char *trailer = (
char*)sfd->trailer;
int tlen = sfd->tlen;
if(header ==
NULL) {
hlen =
0;
}
if(trailer ==
NULL) {
tlen =
0;
}
ssize_t r;
while(hlen >
0) {
r = fd->write(fd, header, hlen);
header += r;
hlen -= r;
if(r <=
0) {
free(buf);
fd->io_errno = errno;
return IO_ERROR;
}
}
if(system_lseek(sfd->fd, sfd->offset,
SEEK_SET) == -
1) {
free(buf);
fd->io_errno = errno;
return IO_ERROR;
}
size_t length = sfd->len;
while(length >
0) {
if(length > sfd->len) {
log_ereport(
LOG_WARN,
"net_fallback_sendfile: length > sfd->len: %zu > %zu", length, sfd->len);
free(buf);
return IO_ERROR;
}
if((r = system_fread(sfd->fd, buf,
4096)) <=
0) {
break;
}
char *write_buf = buf;
while(r >
0) {
ssize_t w = fd->write(fd, write_buf, r);
if(w > r) {
log_ereport(
LOG_WARN,
"net_fallback_sendfile: w > r, %zd > %zd", w, r);
w =
0;
}
if(w <=
0) {
free(buf);
fd->io_errno = errno;
return IO_ERROR;
}
r -= w;
length -= w;
write_buf += w;
}
}
free(buf);
if(length >
0) {
fd->io_errno = errno;
return IO_ERROR;
}
while(tlen >
0) {
r = fd->write(fd, trailer, tlen);
trailer += r;
tlen -= r;
if(r <=
0) {
fd->io_errno = errno;
return IO_ERROR;
}
}
return sfd->hlen + sfd->len + sfd->tlen;
}
int net_flush(
SYS_NETFD sd) {
return 0;
}
void net_close(
SYS_NETFD fd) {
((IOStream*)fd)->close(fd);
}
int net_setnonblock(
SYS_NETFD fd,
int nonblock) {
((IOStream*)fd)->setmode(
fd,
nonblock ?
IO_MODE_NONBLOCKING :
IO_MODE_BLOCKING);
return 0;
}
int net_errno(
SYS_NETFD fd) {
return ((IOStream*)fd)->io_errno;
}
void net_finish(
SYS_NETFD fd) {
((IOStream*)fd)->finish(fd);
}