enhanced qse_upxd_t

This commit is contained in:
hyung-hwan 2012-06-20 15:12:18 +00:00
parent ba24a28f16
commit c60ca301c4
12 changed files with 1375 additions and 138 deletions

View File

@ -298,7 +298,7 @@ qse_wchar_t* qse_mbsatowcsalldup (
* n = qse_wcstombs (wcs, &wcslen, mbs, &mbslen); * n = qse_wcstombs (wcs, &wcslen, mbs, &mbslen);
* if (n <= -1) * if (n <= -1)
* { * {
* // wcs fully scanned and mbs null-terminated * // conversion error
* } * }
* @endcode * @endcode
*/ */

View File

@ -229,7 +229,7 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
pkginclude_HEADERS = http.h htre.h htrd.h httpd.h pkginclude_HEADERS = http.h htre.h htrd.h httpd.h upxd.h
all: all-am all: all-am
.SUFFIXES: .SUFFIXES:

View File

@ -5,8 +5,8 @@
This file is part of QSE. This file is part of QSE.
QSE is free software: you can redistribute it and/or modify QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version. the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful, QSE is distributed in the hope that it will be useful,
@ -14,7 +14,7 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>. License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/ */
@ -23,65 +23,193 @@
#include <qse/types.h> #include <qse/types.h>
#include <qse/macros.h> #include <qse/macros.h>
#include <qse/cmn/nwad.h>
#include <qse/cmn/time.h>
typedef struct qse_upxd_t qse_upxd_t; typedef struct qse_upxd_t qse_upxd_t;
typedef struct qse_upxd_client_t qse_upxd_client_t;
typedef struct qse_upxd_server_t qse_upxd_server_t;
enum qse_upxd_errnum_t enum qse_upxd_errnum_t
{ {
QSE_UPXD_ENOERR, QSE_UPXD_ENOERR,
QSE_UPXD_ENOMEM, QSE_UPXD_ENOMEM,
QSE_UPXD_EINVAL, QSE_UPXD_EINVAL,
QSE_UPXD_EACCES, QSE_UPXD_EACCES,
QSE_UPXD_ENOENT, QSE_UPXD_ENOENT,
QSE_UPXD_EEXIST, QSE_UPXD_EEXIST,
QSE_UPXD_EINTR, QSE_UPXD_EINTR,
QSE_UPXD_EAGAIN, QSE_UPXD_EAGAIN,
QSE_UPXD_EINTERN,
QSE_UPXD_ESYSERR,
QSE_UPXD_ENOIMPL, QSE_UPXD_ENOIMPL,
QSE_UPXD_EOTHER QSE_UPXD_EOTHER
}; };
typedef qse_upxd_errnum_t qse_upxd_errnum_t; typedef enum qse_upxd_errnum_t qse_upxd_errnum_t;
struct qse_upxd_server_t typedef struct qse_upxd_server_t qse_upxd_server_t;
typedef struct qse_upxd_session_t qse_upxd_session_t;
typedef struct qse_upxd_sock_t qse_upxd_sock_t;
struct qse_upxd_sock_t
{ {
qse_upxd_server_t* next; qse_ubi_t handle;
/* ------------------------------ */ qse_nwad_t bind;
qse_nwad_t nwad; const qse_char_t* dev;
qse_ubi_t handle; qse_nwad_t from;
qse_nwad_t to;
}; };
struct qse_upxd_client_t struct qse_upxd_session_t
{ {
qse_nwad_t remote_addr; /** the server that this session belongs to */
qse_nwad_t local_addr; qse_upxd_server_t* server;
/** client's address that initiated this session */
qse_nwad_t client;
/* session configuration to be filled in upxd->cbs->config(). */
struct
{
/** peer's address that the client wants to talk with */
qse_nwad_t peer;
/** binding address for peer socket */
qse_nwad_t bind;
#define QSE_UPXD_SESSION_DEV_LEN (31)
/** binding device for peer socket */
qse_char_t dev[QSE_UPXD_SESSION_DEV_LEN + 1];
#define QSE_UPXD_SESSION_DORMANCY (30000)
/** session's idle-timeout */
qse_ntime_t dormancy;
} config;
}; };
typedef int (*qse_upxd_muxcb_t) (
qse_upxd_t* upxd,
void* mux,
qse_ubi_t handle,
void* cbarg
);
struct qse_upxd_cbs_t struct qse_upxd_cbs_t
{ {
struct struct
{ {
int (*open) (qse_upxd_t* upxd, qse_upxd_server_t* server); int (*open) (qse_upxd_t* upxd, qse_upxd_sock_t* server);
void (*close) (qse_upxd_t* upxd, qse_upxd_server_t* server); void (*close) (qse_upxd_t* upxd, qse_upxd_sock_t* server);
qse_ssize_t (*recv) ( qse_ssize_t (*recv) (
qse_upxd_t* upxd, qse_upxd_t* upxd, qse_upxd_sock_t* server,
qse_upxd_client_t* client, void* buf, qse_size_t bufsize);
qse_mchar_t* buf, qse_size_t bufsize);
qse_ssize_t (*send) ( qse_ssize_t (*send) (
qse_upxd_t* upxd, qse_upxd_t* upxd, qse_upxd_sock_t* sock,
qse_upxd_client_t* client, const void* buf, qse_size_t bufsize);
const qse_mchar_t* buf, qse_size_t bufsize); } sock;
} server;
};
struct
{
int (*config) (qse_upxd_t* upxd, qse_upxd_session_t* session);
void (*error) (qse_upxd_t* upxd, qse_upxd_session_t* session);
} session;
struct
{
void* (*open) (qse_upxd_t* upxd);
void (*close) (qse_upxd_t* upxd, void* mux);
int (*addhnd) (
qse_upxd_t* upxd, void* mux, qse_ubi_t handle,
qse_upxd_muxcb_t cbfun, void* cbarg);
int (*delhnd) (qse_upxd_t* upxd, void* mux, qse_ubi_t handle);
int (*poll) (qse_upxd_t* upxd, void* mux, qse_ntime_t timeout);
} mux;
struct
{
void (*acquire) (qse_upxd_t* upxd);
void (*release) (qse_upxd_t* upxd);
} lock;
};
typedef struct qse_upxd_cbs_t qse_upxd_cbs_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
QSE_DEFINE_COMMON_FUNCTIONS (upxd)
qse_upxd_t* qse_upxd_open (
qse_mmgr_t* mmgr, /**< memory manager */
qse_size_t xtnsize /**< extension size in bytes */
);
void qse_upxd_close (
qse_upxd_t* upxd
);
qse_upxd_errnum_t qse_upxd_geterrnum (
qse_upxd_t* upxd
);
void qse_upxd_seterrnum (
qse_upxd_t* upxd,
qse_upxd_errnum_t errnum
);
qse_upxd_cbs_t* qse_upxd_getcbs (
qse_upxd_t* upxd
);
void qse_upxd_setcbs (
qse_upxd_t* upxd,
qse_upxd_cbs_t* cbs
);
void* qse_upxd_allocmem (
qse_upxd_t* upxd,
qse_size_t size
);
void* qse_upxd_reallocmem (
qse_upxd_t* upxd,
void* ptr,
qse_size_t size
);
void qse_upxd_freemem (
qse_upxd_t* upxd,
void* ptr
);
qse_upxd_server_t* qse_upxd_addserver (
qse_upxd_t* upxd,
const qse_nwad_t* nwad,
const qse_char_t* dev
);
void* qse_upxd_getserverctx (
qse_upxd_t* upxd,
qse_upxd_server_t* server
);
void qse_upxd_setserverctx (
qse_upxd_t* upxd,
qse_upxd_server_t* server,
void* ctx
);
void qse_upxd_stop (
qse_upxd_t* upxd
);
int qse_upxd_loop (
qse_upxd_t* upxd,
qse_ntime_t timeout
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1753,7 +1753,7 @@ static qse_lda_walk_t walk_cands_for_match (
{ {
int equal; int equal;
equal =(e->rex->option & QSE_REX_IGNORECASE)? equal = (e->rex->option & QSE_REX_IGNORECASE)?
(QSE_TOUPPER(node->u.c) == QSE_TOUPPER(*cand->mptr)): (QSE_TOUPPER(node->u.c) == QSE_TOUPPER(*cand->mptr)):
(node->u.c == *cand->mptr) ; (node->u.c == *cand->mptr) ;

View File

@ -7,10 +7,11 @@ AM_CPPFLAGS = \
lib_LTLIBRARIES = libqsenet.la lib_LTLIBRARIES = libqsenet.la
libqsenet_la_SOURCES = \ libqsenet_la_SOURCES = \
httpd.h \
upxd.h \
http.c \ http.c \
htre.c \ htre.c \
htrd.c \ htrd.c \
httpd.h \
httpd.c \ httpd.c \
httpd-cgi.c \ httpd-cgi.c \
httpd-proxy.c \ httpd-proxy.c \

View File

@ -79,7 +79,8 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES) LTLIBRARIES = $(lib_LTLIBRARIES)
libqsenet_la_DEPENDENCIES = libqsenet_la_DEPENDENCIES =
am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \ am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
httpd-cgi.lo httpd-proxy.lo httpd-resol.lo httpd-task.lo httpd-cgi.lo httpd-proxy.lo httpd-resol.lo httpd-task.lo \
upxd.lo
libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS) libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS)
libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@ -256,15 +257,17 @@ AM_CPPFLAGS = \
lib_LTLIBRARIES = libqsenet.la lib_LTLIBRARIES = libqsenet.la
libqsenet_la_SOURCES = \ libqsenet_la_SOURCES = \
httpd.h \
upxd.h \
http.c \ http.c \
htre.c \ htre.c \
htrd.c \ htrd.c \
httpd.h \
httpd.c \ httpd.c \
httpd-cgi.c \ httpd-cgi.c \
httpd-proxy.c \ httpd-proxy.c \
httpd-resol.c \ httpd-resol.c \
httpd-task.c httpd-task.c \
upxd.c
libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir) libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsenet_la_LIBADD = -lqsecmn libqsenet_la_LIBADD = -lqsecmn
@ -350,6 +353,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-task.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-task.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd.Plo@am__quote@
.c.o: .c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<

View File

@ -5,8 +5,8 @@
This file is part of QSE. This file is part of QSE.
QSE is free software: you can redistribute it and/or modify QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version. the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful, QSE is distributed in the hope that it will be useful,
@ -14,11 +14,21 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>. License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/ */
#include "upxd.h" #include "upxd.h"
#include <qse/cmn/str.h>
QSE_IMPLEMENT_COMMON_FUNCTIONS (upxd)
static void free_all_servers (qse_upxd_t* upxd);
static qse_upxd_server_session_t* find_server_session (
qse_upxd_t* upxd, qse_upxd_server_t* server, qse_nwad_t* from);
static void release_session (
qse_upxd_t* upxd, qse_upxd_server_session_t* session);
qse_upxd_t* qse_upxd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) qse_upxd_t* qse_upxd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
{ {
@ -53,16 +63,11 @@ int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr)
void qse_upxd_fini (qse_upxd_t* upxd) void qse_upxd_fini (qse_upxd_t* upxd)
{ {
free_server_list (upxd, upxd->server.list); QSE_ASSERTX (upxd->server.nactive == 0,
QSE_ASSERT (upxd->server.navail == 0); "Deactivate all servers before destroying me");
upxd->server.list = QSE_NULL; free_all_servers (upxd);
} }
void qse_upxd_stop (qse_upxd_t* upxd)
{
upxd->stopreq = 1;
}
qse_upxd_errnum_t qse_upxd_geterrnum (qse_upxd_t* upxd) qse_upxd_errnum_t qse_upxd_geterrnum (qse_upxd_t* upxd)
{ {
return upxd->errnum; return upxd->errnum;
@ -73,37 +78,461 @@ void qse_upxd_seterrnum (qse_upxd_t* upxd, qse_upxd_errnum_t errnum)
upxd->errnum = errnum; upxd->errnum = errnum;
} }
int qse_upxd_addserver (qse_upxd_t* upxd, const qse_nwad_t* nwad) QSE_INLINE void* qse_upxd_allocmem (qse_upxd_t* upxd, qse_size_t size)
{
void* ptr = QSE_MMGR_ALLOC (upxd->mmgr, size);
if (ptr == QSE_NULL) upxd->errnum = QSE_UPXD_ENOMEM;
return ptr;
}
QSE_INLINE void* qse_upxd_reallocmem (
qse_upxd_t* upxd, void* ptr, qse_size_t size)
{
void* nptr = QSE_MMGR_REALLOC (upxd->mmgr, ptr, size);
if (nptr == QSE_NULL) upxd->errnum = QSE_UPXD_ENOMEM;
return nptr;
}
QSE_INLINE void qse_upxd_freemem (qse_upxd_t* upxd, void* ptr)
{
QSE_MMGR_FREE (upxd->mmgr, ptr);
}
void qse_upxd_stop (qse_upxd_t* upxd)
{
upxd->stopreq = 1;
}
static int perform_session_task (
qse_upxd_t* upxd, void* mux, qse_ubi_t handle, void* cbarg)
{
qse_upxd_server_session_t* session;
qse_upxd_server_t* server;
qse_ssize_t n;
session = (qse_upxd_server_session_t*)cbarg;
server = session->inner.server;
qse_gettime (&session->modified);
/* this handler should set the 'from' field of server->scok */
n = upxd->cbs->sock.recv (
upxd, &session->peer, upxd->rbuf, QSE_SIZEOF(upxd->rbuf));
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
/* TODO: inspect if session->inner.to matches session->sock.from.
drop it if they don't match if a certain option (QSE_UPXD_STRICT)
is set??? */
/* send the peer's packet back to the client */
server->local.to = session->inner.client;
n = upxd->cbs->sock.send (upxd, &server->local, upxd->rbuf, n);
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
return 0;
}
static int perform_server_task (
qse_upxd_t* upxd, void* mux, qse_ubi_t handle, void* cbarg)
{ {
qse_upxd_server_t* server; qse_upxd_server_t* server;
qse_upxd_server_session_t* session;
qse_ssize_t n;
server = (qse_upxd_server_t*)cbarg;
/* this handler should set the 'from' field of server->scok */
n = upxd->cbs->sock.recv (
upxd, &server->local, upxd->rbuf, QSE_SIZEOF(upxd->rbuf));
if (n <= -1) return -1;
/* get the existing session or create a new session based on
* server->local->from */
session = find_server_session (upxd, server, &server->local.from);
if (session == QSE_NULL)
{
qse_upxd_session_t interim;
QSE_MEMSET (&interim, 0, QSE_SIZEOF(interim));
interim.client = server->local.from;
upxd->cbs->session.error (upxd, &interim);
return -1;
}
n = upxd->cbs->sock.send (upxd, &session->peer, upxd->rbuf, n);
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
return 0;
}
static qse_upxd_server_session_t* find_server_session (
qse_upxd_t* upxd, qse_upxd_server_t* server, qse_nwad_t* from)
{
qse_upxd_server_session_t* session;
/* TODO: make it indexable or hashable with 'from'
* don't perform linear search */
/* find an existing session made for the source address 'from' */
for (session = server->session.list; session; session = session->next)
{
if (QSE_MEMCMP (&session->inner.client, from, QSE_SIZEOF(*from)) == 0)
{
qse_gettime (&session->modified);
return session;
}
}
/* there is no session found for the source address 'from'.
* let's create a new session. */
session = qse_upxd_allocmem (upxd, QSE_SIZEOF(*session));
if (session == QSE_NULL) return QSE_NULL;
QSE_MEMSET (session, 0, QSE_SIZEOF(*session));
if (qse_gettime (&session->created) <= -1)
{
qse_upxd_freemem (upxd, session);
upxd->errnum = QSE_UPXD_ESYSERR;
return QSE_NULL;
}
session->modified = session->created;
session->inner.server = server;
session->inner.client = *from;
/* set the default dormancy */
session->inner.config.dormancy = QSE_UPXD_SESSION_DORMANCY;
/* call the configurationc callback for configuration data */
if (upxd->cbs->session.config (upxd, &session->inner) <= -1)
{
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
/* set up the peer socket with the configuration data */
session->peer.bind = session->inner.config.bind;
session->peer.to = session->inner.config.peer;
if (session->inner.config.dev[0] != QSE_T('\0'))
session->peer.dev = session->inner.config.dev;
if (upxd->cbs->sock.open (upxd, &session->peer) <= -1)
{
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
if (upxd->cbs->mux.addhnd (
upxd, upxd->mux, session->peer.handle,
perform_session_task, session) <= -1)
{
upxd->cbs->sock.close (upxd, &session->peer);
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
/* insert the session into the head of the session list */
if (server->session.list)
server->session.list->prev = session;
session->next = server->session.list;
server->session.list = session;
return session;
}
static void release_session (
qse_upxd_t* upxd, qse_upxd_server_session_t* session)
{
qse_upxd_server_t* server;
server = session->inner.server;
QSE_ASSERT (server != QSE_NULL);
upxd->cbs->mux.delhnd (upxd, upxd->mux, session->peer.handle);
upxd->cbs->sock.close (upxd, &session->peer);
/* remove the session from the session list */
if (session->next) session->next->prev = session->prev;
if (session->prev) session->prev->next = session->next;
else server->session.list = session->next;
/* destroy the session */
qse_upxd_freemem (upxd, session);
}
static int activate_server (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
QSE_ASSERT (upxd->cbs != QSE_NULL);
QSE_ASSERT (!(server->flags & QSE_UPXD_SERVER_ACTIVE));
if (upxd->cbs->sock.open (upxd, &server->local) <= -1)
{
return -1;
}
if (upxd->cbs->mux.addhnd (
upxd, upxd->mux, server->local.handle,
perform_server_task, server) <= -1)
{
upxd->cbs->sock.close (upxd, &server->local);
return -1;
}
server->flags |= QSE_UPXD_SERVER_ACTIVE;
upxd->server.nactive++;
return 0;
}
static void deactivate_server (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
qse_upxd_server_session_t* session;
QSE_ASSERT (upxd->cbs != QSE_NULL);
QSE_ASSERT (server->flags & QSE_UPXD_SERVER_ACTIVE);
session = server->session.list;
while (session)
{
qse_upxd_server_session_t* next = session->next;
release_session (upxd, session);
session = next;
}
upxd->cbs->mux.delhnd (upxd, upxd->mux, server->local.handle);
upxd->cbs->sock.close (upxd, &server->local);
server->flags &= ~QSE_UPXD_SERVER_ACTIVE;
upxd->server.nactive--;
}
static void activate_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
for (server = upxd->server.list; server; server = server->next)
{
if (!(server->flags & QSE_UPXD_SERVER_ACTIVE))
{
activate_server (upxd, server);
}
}
}
static void deactivate_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
server = upxd->server.list;
while (server)
{
if (server->flags & QSE_UPXD_SERVER_ACTIVE)
deactivate_server (upxd, server);
server = server->next;
}
}
static void free_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
qse_upxd_server_t* next;
server = upxd->server.list;
while (server)
{
next = server->next;
QSE_MMGR_FREE (upxd->mmgr, server);
server = next;
}
upxd->server.list = QSE_NULL;
}
static void purge_deleted_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
qse_upxd_server_t* next;
server = upxd->server.list;
while (server)
{
next = server->next;
if (server->flags & QSE_UPXD_SERVER_DELETED)
{
if (server->flags & QSE_UPXD_SERVER_ACTIVE)
deactivate_server (upxd, server);
if (server == upxd->server.list) upxd->server.list = next;
QSE_MMGR_FREE (upxd->mmgr, server);
}
server = next;
}
}
qse_upxd_server_t* qse_upxd_addserver (
qse_upxd_t* upxd, const qse_nwad_t* nwad, const qse_char_t* dev)
{
qse_upxd_server_t* server;
if (dev && qse_strlen(dev) >= QSE_COUNTOF(server->dev))
{
upxd->errnum = QSE_UPXD_EINVAL;
return QSE_NULL;
}
server = QSE_MMGR_ALLOC (upxd->mmgr, QSE_SIZEOF(*server)); server = QSE_MMGR_ALLOC (upxd->mmgr, QSE_SIZEOF(*server));
if (server == QSE_NULL) if (server == QSE_NULL)
{ {
upxd->errnum = QSE_UPXD_ENOMEM; upxd->errnum = QSE_UPXD_ENOMEM;
return -1; return QSE_NULL;
} }
QSE_MEMSET (server, 0, QSE_SIZEOF(*server)); QSE_MEMSET (server, 0, QSE_SIZEOF(*server));
server->nwad = *nwad; if (dev)
if (upxd->cbs->server.open (upxd, server) <= -1)
{ {
QSE_MMGR_FREE (upxd->mmgr, server); qse_strxcpy (server->dev, QSE_COUNTOF(server->dev), dev);
return -1; server->local.dev = server->dev;
} }
server->local.bind = *nwad;
server->next = upxd->server.list; upxd->cbs->lock.acquire (upxd);
server->next = upxd->server.list;
upxd->server.list = server; upxd->server.list = server;
upxd->cbs->lock.release (upxd);
return server;
} }
int qse_upxd_loop (qse_upxd_t* upxd, qse_upxd_cbls_t* cbs) void qse_upxd_delserver (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{ {
server->flags |= QSE_UPXD_SERVER_DELETED;
}
void* qse_upxd_getserverctx (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{
return server->ctx;
}
void qse_upxd_setserverctx (
qse_upxd_t* upxd, qse_upxd_server_t* server, void* ctx)
{
server->ctx = ctx;
}
qse_upxd_cbs_t* qse_upxd_getcbs (qse_upxd_t* upxd)
{
return upxd->cbs;
}
void qse_upxd_setcbs (qse_upxd_t* upxd, qse_upxd_cbs_t* cbs)
{
upxd->cbs = cbs;
}
static QSE_INLINE void purge_idle_sessions_in_server (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{
qse_upxd_server_session_t* session;
qse_upxd_server_session_t* next;
qse_ntime_t now;
qse_gettime (&now);
session = server->session.list;
while (session)
{
next = session->next;
if (session->inner.config.dormancy > 0 &&
now > session->modified &&
now - session->modified > session->inner.config.dormancy)
{
release_session (upxd, session);
}
session = next;
}
}
static void purge_idle_sessions (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
for (server = upxd->server.list; server; server = server->next)
{
if (server->flags & QSE_UPXD_SERVER_ACTIVE)
{
purge_idle_sessions_in_server (upxd, server);
}
}
}
int qse_upxd_loop (qse_upxd_t* upxd, qse_ntime_t timeout)
{
int retv = -1;
QSE_ASSERTX (upxd->cbs != QSE_NULL,
"Call qse_upxd_setcbs() before calling qse_upxd_loop()");
QSE_ASSERT (upxd->mux == QSE_NULL);
if (upxd->cbs == QSE_NULL || upxd->mux /*||
upxd->server.list == QSE_NULL*/)
{
upxd->errnum = QSE_UPXD_EINVAL;
goto oops;
}
upxd->stopreq = 0; upxd->stopreq = 0;
upxd->mux = upxd->cbs-> mux.open (upxd);
if (upxd->mux == QSE_NULL) goto oops;
activate_all_servers (upxd);
if (upxd->server.nactive == 0)
{
/* at least 1 server must be activated here */
upxd->errnum = QSE_UPXD_EINVAL;
goto oops;
}
while (!upxd->stopreq) while (!upxd->stopreq)
{ {
int count;
count = upxd->cbs->mux.poll (upxd, upxd->mux, timeout);
if (count <= -1)
{
/* TODO: anything? */
}
upxd->cbs->lock.acquire (upxd);
purge_idle_sessions (upxd);
purge_deleted_servers (upxd);
activate_all_servers (upxd);
upxd->cbs->lock.release (upxd);
} }
return 0; retv = 0;
oops:
if (upxd->server.nactive > 0) deactivate_all_servers (upxd);
if (upxd->mux) upxd->cbs->mux.close (upxd, upxd->mux);
return retv;
} }

View File

@ -5,8 +5,8 @@
This file is part of QSE. This file is part of QSE.
QSE is free software: you can redistribute it and/or modify QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version. the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful, QSE is distributed in the hope that it will be useful,
@ -14,7 +14,7 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>. License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/ */
@ -22,15 +22,80 @@
#define _QSE_LIB_NET_UPXD_H_ #define _QSE_LIB_NET_UPXD_H_
#include <qse/net/upxd.h> #include <qse/net/upxd.h>
#include "../cmn/mem.h"
typedef struct qse_upxd_server_session_t qse_upxd_server_session_t;
struct qse_upxd_server_t
{
qse_upxd_server_t* next;
#define QSE_UPXD_SERVER_ACTIVE (1 << 0)
#define QSE_UPXD_SERVER_DELETED (1 << 1)
int flags;
/* the socket can be bound to this interface.
* sock->dev points to this buffer when necessary. */
qse_char_t dev[QSE_UPXD_SESSION_DEV_LEN + 1];
/* list of sessions beloning to this server */
struct
{
qse_upxd_server_session_t* list;
qse_size_t count;
} session;
qse_upxd_sock_t local;
/* user-defined context data that can be set
* with qse_upxd_setserverctx() */
void* ctx;
};
struct qse_upxd_server_session_t
{
/* internal fields */
qse_upxd_server_session_t* next;
qse_upxd_server_session_t* prev;
/* timestamps for housekeeping */
qse_ntime_t created;
qse_ntime_t modified;
/* socket used to talk with a peer */
qse_upxd_sock_t peer;
/* exposed to a caller via callbacks */
qse_upxd_session_t inner;
};
struct qse_upxd_t struct qse_upxd_t
{ {
QSE_DEFINE_COMMON_FIELDS (upxd)
qse_upxd_errnum_t errnum;
int stopreq; int stopreq;
qse_upxd_cbs_t* cbs;
struct struct
{ {
qse_upxd_server_t* list; qse_upxd_server_t* list;
qse_size_t nactive;
} server; } server;
void* mux;
qse_uint8_t rbuf[65535];
}; };
#ifdef __cplusplus
extern "C" {
#endif
int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr);
void qse_upxd_fini (qse_upxd_t* upxd);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@ -5,7 +5,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/include \ -I$(top_srcdir)/include \
-I$(includedir) -I$(includedir)
bin_PROGRAMS = http01 bin_PROGRAMS = http01 upxd01
LDFLAGS += -L../../lib/cmn -L../../lib/net LDFLAGS += -L../../lib/cmn -L../../lib/net
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) -lssl LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) -lssl
@ -15,4 +15,5 @@ LDADD += $(UNICOWS_LIBS)
endif endif
http01_SOURCES = http01.c http01_SOURCES = http01.c
upxd01_SOURCES = upxd01.c

View File

@ -34,7 +34,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = : POST_UNINSTALL = :
build_triplet = @build@ build_triplet = @build@
host_triplet = @host@ host_triplet = @host@
bin_PROGRAMS = http01$(EXEEXT) bin_PROGRAMS = http01$(EXEEXT) upxd01$(EXEEXT)
@WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS) @WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS)
subdir = samples/net subdir = samples/net
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
@ -59,6 +59,11 @@ am__DEPENDENCIES_1 =
@WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
http01_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ http01_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
am_upxd01_OBJECTS = upxd01.$(OBJEXT)
upxd01_OBJECTS = $(am_upxd01_OBJECTS)
upxd01_LDADD = $(LDADD)
upxd01_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
DEFAULT_INCLUDES = DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/ac/depcomp depcomp = $(SHELL) $(top_srcdir)/ac/depcomp
am__depfiles_maybe = depfiles am__depfiles_maybe = depfiles
@ -72,8 +77,8 @@ CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@ $(LDFLAGS) -o $@
SOURCES = $(http01_SOURCES) SOURCES = $(http01_SOURCES) $(upxd01_SOURCES)
DIST_SOURCES = $(http01_SOURCES) DIST_SOURCES = $(http01_SOURCES) $(upxd01_SOURCES)
ETAGS = etags ETAGS = etags
CTAGS = ctags CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@ -232,6 +237,7 @@ AM_CPPFLAGS = \
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \ LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \
$(SENDFILE_LIBS) -lssl $(am__append_1) $(SENDFILE_LIBS) -lssl $(am__append_1)
http01_SOURCES = http01.c http01_SOURCES = http01.c
upxd01_SOURCES = upxd01.c
all: all-am all: all-am
.SUFFIXES: .SUFFIXES:
@ -312,6 +318,9 @@ clean-binPROGRAMS:
http01$(EXEEXT): $(http01_OBJECTS) $(http01_DEPENDENCIES) $(EXTRA_http01_DEPENDENCIES) http01$(EXEEXT): $(http01_OBJECTS) $(http01_DEPENDENCIES) $(EXTRA_http01_DEPENDENCIES)
@rm -f http01$(EXEEXT) @rm -f http01$(EXEEXT)
$(LINK) $(http01_OBJECTS) $(http01_LDADD) $(LIBS) $(LINK) $(http01_OBJECTS) $(http01_LDADD) $(LIBS)
upxd01$(EXEEXT): $(upxd01_OBJECTS) $(upxd01_DEPENDENCIES) $(EXTRA_upxd01_DEPENDENCIES)
@rm -f upxd01$(EXEEXT)
$(LINK) $(upxd01_OBJECTS) $(upxd01_LDADD) $(LIBS)
mostlyclean-compile: mostlyclean-compile:
-rm -f *.$(OBJEXT) -rm -f *.$(OBJEXT)
@ -320,6 +329,7 @@ distclean-compile:
-rm -f *.tab.c -rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http01.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http01.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd01.Po@am__quote@
.c.o: .c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<

View File

@ -10,7 +10,8 @@
#include <signal.h> #include <signal.h>
#include <locale.h> #include <locale.h>
#include <string.h> #include <string.h>
#if defined(_WIN32)
#if defined(_WIN32)
# include <windows.h> # include <windows.h>
#else #else
# include <unistd.h> # include <unistd.h>
@ -71,7 +72,7 @@ static qse_ssize_t xsendfile (
vec.sfv_flag = 0; vec.sfv_flag = 0;
if (offset) if (offset)
{ {
vec.sfv_off = *offset; vec.sfv_off = *offset;
} }
else else
{ {
@ -154,7 +155,7 @@ static qse_httpd_errnum_t syserr_to_errnum (int e)
case EEXIST: case EEXIST:
return QSE_HTTPD_EEXIST; return QSE_HTTPD_EEXIST;
case EINTR: case EINTR:
return QSE_HTTPD_EINTR; return QSE_HTTPD_EINTR;
@ -177,8 +178,8 @@ struct httpd_xtn_t
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
static int init_xtn_ssl ( static int init_xtn_ssl (
httpd_xtn_t* xtn, httpd_xtn_t* xtn,
const qse_mchar_t* pemfile, const qse_mchar_t* pemfile,
const qse_mchar_t* keyfile/*, const qse_mchar_t* keyfile/*,
const qse_mchar_t* chainfile*/) const qse_mchar_t* chainfile*/)
{ {
@ -195,7 +196,7 @@ static int init_xtn_ssl (
if (SSL_CTX_use_certificate_file (ctx, pemfile, SSL_FILETYPE_PEM) == 0 || if (SSL_CTX_use_certificate_file (ctx, pemfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM) == 0 || SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_check_private_key (ctx) == 0 /*|| SSL_CTX_check_private_key (ctx) == 0 /*||
SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/) SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/)
{ {
qse_mchar_t buf[128]; qse_mchar_t buf[128];
@ -240,7 +241,7 @@ static int sockaddr_to_nwad (
{ {
case AF_INET: case AF_INET:
{ {
struct sockaddr_in* in; struct sockaddr_in* in;
in = (struct sockaddr_in*)addr; in = (struct sockaddr_in*)addr;
addrsize = QSE_SIZEOF(*in); addrsize = QSE_SIZEOF(*in);
@ -254,7 +255,7 @@ static int sockaddr_to_nwad (
#if defined(AF_INET6) #if defined(AF_INET6)
case AF_INET6: case AF_INET6:
{ {
struct sockaddr_in6* in; struct sockaddr_in6* in;
in = (struct sockaddr_in6*)addr; in = (struct sockaddr_in6*)addr;
addrsize = QSE_SIZEOF(*in); addrsize = QSE_SIZEOF(*in);
@ -280,7 +281,7 @@ static int nwad_to_sockaddr (
{ {
case QSE_NWAD_IN4: case QSE_NWAD_IN4:
{ {
struct sockaddr_in* in; struct sockaddr_in* in;
in = (struct sockaddr_in*)addr; in = (struct sockaddr_in*)addr;
addrsize = QSE_SIZEOF(*in); addrsize = QSE_SIZEOF(*in);
@ -295,7 +296,7 @@ static int nwad_to_sockaddr (
case QSE_NWAD_IN6: case QSE_NWAD_IN6:
{ {
#if defined(AF_INET6) #if defined(AF_INET6)
struct sockaddr_in6* in; struct sockaddr_in6* in;
in = (struct sockaddr_in6*)addr; in = (struct sockaddr_in6*)addr;
addrsize = QSE_SIZEOF(*in); addrsize = QSE_SIZEOF(*in);
@ -345,10 +346,10 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server)
setsockopt (fd, SOL_IP, IP_TRANSPARENT, &flag, QSE_SIZEOF(flag)); setsockopt (fd, SOL_IP, IP_TRANSPARENT, &flag, QSE_SIZEOF(flag));
#endif #endif
/* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the /* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the
* address size for AF_INET. */ * address size for AF_INET. */
/*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/ /*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/
if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1)
{ {
#if defined(IPV6_V6ONLY) #if defined(IPV6_V6ONLY)
if (errno == EADDRINUSE && addr.ss_family == AF_INET6) if (errno == EADDRINUSE && addr.ss_family == AF_INET6)
@ -382,7 +383,7 @@ static void server_close (qse_httpd_t* httpd, qse_httpd_server_t* server)
} }
static int server_accept ( static int server_accept (
qse_httpd_t* httpd, qse_httpd_t* httpd,
qse_httpd_server_t* server, qse_httpd_client_t* client) qse_httpd_server_t* server, qse_httpd_client_t* client)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
@ -396,7 +397,7 @@ static int server_accept (
addrlen = QSE_SIZEOF(addr); addrlen = QSE_SIZEOF(addr);
fd = accept (server->handle.i, (struct sockaddr*)&addr, &addrlen); fd = accept (server->handle.i, (struct sockaddr*)&addr, &addrlen);
if (fd <= -1) if (fd <= -1)
{ {
qse_httpd_seterrnum (httpd, syserr_to_errnum (errno)); qse_httpd_seterrnum (httpd, syserr_to_errnum (errno));
return -1; return -1;
@ -472,7 +473,7 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
flag = fcntl (fd, F_GETFL); flag = fcntl (fd, F_GETFL);
if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK);
if (connect (fd, (struct sockaddr*)&addr, addrsize) <= -1) if (connect (fd, (struct sockaddr*)&addr, addrsize) <= -1)
{ {
if (errno != EINPROGRESS) goto oops; if (errno != EINPROGRESS) goto oops;
connected = 0; connected = 0;
@ -508,7 +509,7 @@ static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
if (getsockopt (peer->handle.i, SOL_SOCKET, SO_ERROR, &ret, &len) <= -1) return -1; if (getsockopt (peer->handle.i, SOL_SOCKET, SO_ERROR, &ret, &len) <= -1) return -1;
if (ret == EINPROGRESS) return 0; if (ret == EINPROGRESS) return 0;
if (ret != 0) if (ret != 0)
{ {
qse_httpd_seterrnum (httpd, syserr_to_errnum (ret)); qse_httpd_seterrnum (httpd, syserr_to_errnum (ret));
return -1; return -1;
@ -552,7 +553,7 @@ struct mux_t
struct struct
{ {
struct epoll_event* ptr; struct epoll_event* ptr;
qse_size_t len; qse_size_t len;
qse_size_t capa; qse_size_t capa;
} ee; } ee;
@ -579,7 +580,7 @@ static void* mux_open (qse_httpd_t* httpd)
#else #else
mux->fd = epoll_create (100); mux->fd = epoll_create (100);
#endif #endif
if (mux->fd <= -1) if (mux->fd <= -1)
{ {
qse_httpd_freemem (httpd, mux); qse_httpd_freemem (httpd, mux);
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
@ -602,7 +603,7 @@ static void mux_close (qse_httpd_t* httpd, void* vmux)
{ {
struct mux_t* mux = (struct mux_t*)vmux; struct mux_t* mux = (struct mux_t*)vmux;
if (mux->ee.ptr) qse_httpd_freemem (httpd, mux->ee.ptr); if (mux->ee.ptr) qse_httpd_freemem (httpd, mux->ee.ptr);
if (mux->mev.ptr) if (mux->mev.ptr)
{ {
qse_size_t i; qse_size_t i;
for (i = 0; i < mux->mev.capa; i++) for (i = 0; i < mux->mev.capa; i++)
@ -614,7 +615,7 @@ static void mux_close (qse_httpd_t* httpd, void* vmux)
} }
static int mux_addhnd ( static int mux_addhnd (
qse_httpd_t* httpd, void* vmux, qse_ubi_t handle, qse_httpd_t* httpd, void* vmux, qse_ubi_t handle,
int mask, qse_httpd_muxcb_t cbfun, void* cbarg) int mask, qse_httpd_muxcb_t cbfun, void* cbarg)
{ {
struct mux_t* mux = (struct mux_t*)vmux; struct mux_t* mux = (struct mux_t*)vmux;
@ -635,12 +636,12 @@ static int mux_addhnd (
{ {
struct mux_ev_t** tmp; struct mux_ev_t** tmp;
qse_size_t tmpcapa, i; qse_size_t tmpcapa, i;
tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN); tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN);
tmp = (struct mux_ev_t**) qse_httpd_reallocmem ( tmp = (struct mux_ev_t**) qse_httpd_reallocmem (
httpd, mux->mev.ptr, httpd, mux->mev.ptr,
QSE_SIZEOF(*mux->mev.ptr) * tmpcapa); QSE_SIZEOF(*mux->mev.ptr) * tmpcapa);
if (tmp == QSE_NULL) return -1; if (tmp == QSE_NULL) return -1;
for (i = mux->mev.capa; i < tmpcapa; i++) tmp[i] = QSE_NULL; for (i = mux->mev.capa; i < tmpcapa; i++) tmp[i] = QSE_NULL;
@ -648,7 +649,7 @@ static int mux_addhnd (
mux->mev.capa = tmpcapa; mux->mev.capa = tmpcapa;
} }
if (mux->mev.ptr[handle.i] == QSE_NULL) if (mux->mev.ptr[handle.i] == QSE_NULL)
{ {
/* the location of the data passed to epoll_ctl() /* the location of the data passed to epoll_ctl()
* must not change unless i update the info with epoll() * must not change unless i update the info with epoll()
@ -658,14 +659,14 @@ static int mux_addhnd (
mux->mev.ptr[handle.i] = qse_httpd_allocmem ( mux->mev.ptr[handle.i] = qse_httpd_allocmem (
httpd, QSE_SIZEOF(*mux->mev.ptr[handle.i])); httpd, QSE_SIZEOF(*mux->mev.ptr[handle.i]));
if (mux->mev.ptr[handle.i] == QSE_NULL) return -1; if (mux->mev.ptr[handle.i] == QSE_NULL) return -1;
} }
if (mux->ee.len >= mux->ee.capa) if (mux->ee.len >= mux->ee.capa)
{ {
struct epoll_event* tmp; struct epoll_event* tmp;
tmp = qse_httpd_reallocmem ( tmp = qse_httpd_reallocmem (
httpd, mux->ee.ptr, httpd, mux->ee.ptr,
QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2); QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2);
if (tmp == QSE_NULL) return -1; if (tmp == QSE_NULL) return -1;
@ -725,14 +726,14 @@ static int mux_poll (qse_httpd_t* httpd, void* vmux, qse_ntime_t timeout)
mask = 0; mask = 0;
if (mux->ee.ptr[i].events & EPOLLIN) if (mux->ee.ptr[i].events & EPOLLIN)
mask |= QSE_HTTPD_MUX_READ; mask |= QSE_HTTPD_MUX_READ;
if (mux->ee.ptr[i].events & EPOLLOUT) if (mux->ee.ptr[i].events & EPOLLOUT)
mask |= QSE_HTTPD_MUX_WRITE; mask |= QSE_HTTPD_MUX_WRITE;
if (mux->ee.ptr[i].events & EPOLLHUP) if (mux->ee.ptr[i].events & EPOLLHUP)
{ {
if (mev->reqmask & QSE_HTTPD_MUX_READ) if (mev->reqmask & QSE_HTTPD_MUX_READ)
mask |= QSE_HTTPD_MUX_READ; mask |= QSE_HTTPD_MUX_READ;
if (mev->reqmask & QSE_HTTPD_MUX_WRITE) if (mev->reqmask & QSE_HTTPD_MUX_WRITE)
mask |= QSE_HTTPD_MUX_WRITE; mask |= QSE_HTTPD_MUX_WRITE;
@ -759,7 +760,7 @@ static int mux_readable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
FD_ZERO (&r); FD_ZERO (&r);
FD_SET (handle.i, &r); FD_SET (handle.i, &r);
return select (handle.i + 1, &r, QSE_NULL, QSE_NULL, tvp); return select (handle.i + 1, &r, QSE_NULL, QSE_NULL, tvp);
} }
static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec) static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
@ -800,9 +801,9 @@ static int file_stat (
{ {
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1; return -1;
} }
/* stating for a file. it should be a regular file. /* stating for a file. it should be a regular file.
* i don't allow other file types. */ * i don't allow other file types. */
if (!S_ISREG(st.st_mode)) if (!S_ISREG(st.st_mode))
{ {
@ -842,7 +843,7 @@ static int file_ropen (
qse_printf (QSE_T("opening file [%hs] for reading\n"), path); qse_printf (QSE_T("opening file [%hs] for reading\n"), path);
fd = open (path, flags, 0); fd = open (path, flags, 0);
if (fd <= -1) if (fd <= -1)
{ {
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1; return -1;
@ -857,7 +858,7 @@ qse_printf (QSE_T("opened file %hs\n"), path);
} }
static int file_wopen ( static int file_wopen (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_t* httpd, const qse_mchar_t* path,
qse_ubi_t* handle) qse_ubi_t* handle)
{ {
int fd; int fd;
@ -870,7 +871,7 @@ static int file_wopen (
qse_printf (QSE_T("opening file [%hs] for writing\n"), path); qse_printf (QSE_T("opening file [%hs] for writing\n"), path);
fd = open (path, flags, 0644); fd = open (path, flags, 0644);
if (fd <= -1) if (fd <= -1)
{ {
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1; return -1;
@ -887,14 +888,14 @@ qse_printf (QSE_T("closing file %d\n"), handle.i);
} }
static qse_ssize_t file_read ( static qse_ssize_t file_read (
qse_httpd_t* httpd, qse_ubi_t handle, qse_httpd_t* httpd, qse_ubi_t handle,
qse_mchar_t* buf, qse_size_t len) qse_mchar_t* buf, qse_size_t len)
{ {
return read (handle.i, buf, len); return read (handle.i, buf, len);
} }
static qse_ssize_t file_write ( static qse_ssize_t file_write (
qse_httpd_t* httpd, qse_ubi_t handle, qse_httpd_t* httpd, qse_ubi_t handle,
const qse_mchar_t* buf, qse_size_t len) const qse_mchar_t* buf, qse_size_t len)
{ {
return write (handle.i, buf, len); return write (handle.i, buf, len);
@ -916,7 +917,7 @@ static void client_shutdown (
shutdown (client->handle.i, 2); shutdown (client->handle.i, 2);
#endif #endif
} }
static qse_ssize_t client_recv ( static qse_ssize_t client_recv (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_mchar_t* buf, qse_size_t bufsize) qse_mchar_t* buf, qse_size_t bufsize)
@ -926,7 +927,7 @@ static qse_ssize_t client_recv (
int ret = SSL_read (client->handle2.ptr, buf, bufsize); int ret = SSL_read (client->handle2.ptr, buf, bufsize);
if (ret <= -1) if (ret <= -1)
{ {
if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_READ) if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_READ)
qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN); qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN);
else else
qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
@ -950,7 +951,7 @@ static qse_ssize_t client_send (
int ret = SSL_write (client->handle2.ptr, buf, bufsize); int ret = SSL_write (client->handle2.ptr, buf, bufsize);
if (ret <= -1) if (ret <= -1)
{ {
if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_WRITE) if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_WRITE)
qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN); qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN);
else else
qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
@ -1003,7 +1004,7 @@ qse_printf (QSE_T("SSL ACCEPTING %d\n"), client->handle.i);
qse_fflush (QSE_STDOUT); qse_fflush (QSE_STDOUT);
if (SSL_set_fd (ssl, client->handle.i) == 0) if (SSL_set_fd (ssl, client->handle.i) == 0)
{ {
/* don't free ssl here since client_closed() /* don't free ssl here since client_closed()
* will be closed */ * will be closed */
return -1; return -1;
} }
@ -1012,7 +1013,7 @@ qse_fflush (QSE_STDOUT);
ret = SSL_accept (ssl); ret = SSL_accept (ssl);
if (ret <= 0) if (ret <= 0)
{ {
if (SSL_get_error(ssl,ret) == SSL_ERROR_WANT_READ) if (SSL_get_error(ssl,ret) == SSL_ERROR_WANT_READ)
{ {
/* handshaking isn't complete. */ /* handshaking isn't complete. */
return 0; return 0;
@ -1054,7 +1055,7 @@ qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_
} }
static int process_request ( static int process_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_htre_t* req, int peek) qse_htre_t* req, int peek)
{ {
int method; int method;
@ -1065,12 +1066,12 @@ static int process_request (
content_received = (qse_htre_getcontentlen(req) > 0); content_received = (qse_htre_getcontentlen(req) > 0);
/* percent-decode the query path to the original buffer /* percent-decode the query path to the original buffer
* since i'm not gonna need it in the original form * since i'm not gonna need it in the original form
* any more */ * any more */
qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req)); qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req));
qse_printf (QSE_T("================================\n")); qse_printf (QSE_T("================================\n"));
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"), qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
(unsigned long)time(NULL), (unsigned long)time(NULL),
(peek? QSE_MT("PEEK"): QSE_MT("HANDLE")), (peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
qse_htre_getqpath(req), qse_htre_getqpath(req),
@ -1079,11 +1080,11 @@ qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"
qse_htre_getverstr(req), qse_htre_getverstr(req),
qse_htre_getqmethodname(req) qse_htre_getqmethodname(req)
); );
if (qse_htre_getqparam(req)) if (qse_htre_getqparam(req))
qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparam(req)); qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparam(req));
qse_htb_walk (&req->hdrtab, walk, QSE_NULL); qse_htb_walk (&req->hdrtab, walk, QSE_NULL);
if (qse_htre_getcontentlen(req) > 0) if (qse_htre_getcontentlen(req) > 0)
{ {
qse_printf (QSE_T("CONTENT before discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req)); qse_printf (QSE_T("CONTENT before discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
} }
@ -1091,21 +1092,21 @@ if (qse_htre_getcontentlen(req) > 0)
if (peek) if (peek)
{ {
if (method != QSE_HTTP_POST && method != QSE_HTTP_PUT) if (method != QSE_HTTP_POST && method != QSE_HTTP_PUT)
{ {
/* i'll discard request contents if the method is none of /* i'll discard request contents if the method is none of
* post and put */ * post and put */
qse_httpd_discardcontent (httpd, req); qse_httpd_discardcontent (httpd, req);
} }
if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) && if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) &&
(req->version.major > 1 || (req->version.major > 1 ||
(req->version.major == 1 && req->version.minor >= 1)) && (req->version.major == 1 && req->version.minor >= 1)) &&
!content_received) !content_received)
{ {
/* TODO: check method.... */ /* TODO: check method.... */
/* "expect" in the header, version 1.1 or higher, /* "expect" in the header, version 1.1 or higher,
* and no content received yet */ * and no content received yet */
/* TODO: determine if to return 100-continue or other errors */ /* TODO: determine if to return 100-continue or other errors */
{ {
qse_ntime_t now; qse_ntime_t now;
@ -1117,7 +1118,7 @@ qse_printf (QSE_T("entasking continue at %lld\n"), (long long)now);
} }
} }
if (qse_htre_getcontentlen(req) > 0) if (qse_htre_getcontentlen(req) > 0)
{ {
qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req)); qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
} }
@ -1144,11 +1145,11 @@ qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
#endif #endif
} }
else else
#endif #endif
/*if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))*/ /*if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))*/
if (method == QSE_HTTP_POST && if (method == QSE_HTTP_POST &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
{ {
@ -1191,7 +1192,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
auth = qse_htre_getheaderval (req, QSE_MT("Authorization")); auth = qse_htre_getheaderval (req, QSE_MT("Authorization"));
if (auth) if (auth)
{ {
/* TODO: PERFORM authorization... */ /* TODO: PERFORM authorization... */
/* BASE64 decode... */ /* BASE64 decode... */
while (auth->next) auth = auth->next; while (auth->next) auth = auth->next;
authorized = 1; authorized = 1;
@ -1276,7 +1277,7 @@ qse_printf (QSE_T("Host not included....\n"));
goto oops; goto oops;
} }
} }
else else
{ {
const qse_mchar_t* host; const qse_mchar_t* host;
qse_parseuri (); qse_parseuri ();
@ -1287,15 +1288,15 @@ qse_printf (QSE_T("Host not included....\n"));
#if 0 #if 0
if (peek) if (peek)
{ {
if (req->attr.expect && if (req->attr.expect &&
(req->version.major > 1 || (req->version.major > 1 ||
(req->version.major == 1 && req->version.minor >= 1)) && (req->version.major == 1 && req->version.minor >= 1)) &&
!content_received) !content_received)
{ {
/* TODO: check method.... */ /* TODO: check method.... */
/* "expect" in the header, version 1.1 or higher, /* "expect" in the header, version 1.1 or higher,
* and no content received yet */ * and no content received yet */
if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0) if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0)
{ {
if (qse_httpd_entaskerror ( if (qse_httpd_entaskerror (
@ -1385,7 +1386,7 @@ static qse_httpd_cbs_t httpd_cbs =
/* server */ /* server */
{ server_open, server_close, server_accept }, { server_open, server_close, server_accept },
{ peer_open, { peer_open,
peer_close, peer_close,
peer_connected, peer_connected,
peer_recv, peer_recv,
@ -1397,9 +1398,9 @@ static qse_httpd_cbs_t httpd_cbs =
mux_addhnd, mux_addhnd,
mux_delhnd, mux_delhnd,
mux_poll, mux_poll,
mux_readable, mux_readable,
mux_writable mux_writable
}, },
/* file operation */ /* file operation */
@ -1414,11 +1415,11 @@ static qse_httpd_cbs_t httpd_cbs =
/* client connection */ /* client connection */
{ client_close, { client_close,
client_shutdown, client_shutdown,
client_recv, client_recv,
client_send, client_send,
client_sendfile, client_sendfile,
client_accepted, client_accepted,
client_closed }, client_closed },
/* http request */ /* http request */
@ -1468,7 +1469,7 @@ int httpd_main (int argc, qse_char_t* argv[])
{ {
if (qse_httpd_addserver (httpd, argv[i]) <= -1) if (qse_httpd_addserver (httpd, argv[i]) <= -1)
{ {
qse_fprintf (QSE_STDERR, qse_fprintf (QSE_STDERR,
QSE_T("Failed to add httpd listener - %s\n"), argv[i]); QSE_T("Failed to add httpd listener - %s\n"), argv[i]);
goto oops; goto oops;
} }
@ -1497,7 +1498,7 @@ int qse_main (int argc, qse_achar_t* argv[])
{ {
#if defined(_WIN32) #if defined(_WIN32)
char locale[100]; char locale[100];
UINT codepage = GetConsoleOutputCP(); UINT codepage = GetConsoleOutputCP();
if (codepage == CP_UTF8) if (codepage == CP_UTF8)
{ {
/*SetConsoleOUtputCP (CP_UTF8);*/ /*SetConsoleOUtputCP (CP_UTF8);*/

598
qse/samples/net/upxd01.c Normal file
View File

@ -0,0 +1,598 @@
#include <qse/net/upxd.h>
#include <qse/cmn/stdio.h>
#include <qse/cmn/main.h>
#include <qse/cmn/str.h>
#include <qse/cmn/mem.h>
#include <qse/cmn/mbwc.h>
#include <qse/cmn/time.h>
#include <signal.h>
#include <locale.h>
#include <string.h>
#if defined(_WIN32)
# include <windows.h>
#else
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <net/if.h>
#endif
#if defined(HAVE_EPOLL)
# if defined(HAVE_SYS_EPOLL_H)
# include <sys/epoll.h>
# endif
#endif
/* ------------------------------------------------------------------- */
static qse_upxd_errnum_t syserr_to_errnum (int e)
{
switch (e)
{
case ENOMEM:
return QSE_UPXD_ENOMEM;
case EINVAL:
return QSE_UPXD_EINVAL;
case EACCES:
case ECONNREFUSED:
return QSE_UPXD_EACCES;
case ENOENT:
return QSE_UPXD_ENOENT;
case EEXIST:
return QSE_UPXD_EEXIST;
case EINTR:
return QSE_UPXD_EINTR;
case EAGAIN:
/*case EWOULDBLOCK:*/
return QSE_UPXD_EAGAIN;
default:
return QSE_UPXD_ESYSERR;
}
}
/* ------------------------------------------------------------------- */
static int sockaddr_to_nwad (
const struct sockaddr_storage* addr, qse_nwad_t* nwad)
{
int addrsize = -1;
switch (addr->ss_family)
{
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)addr;
addrsize = QSE_SIZEOF(*in);
memset (nwad, 0, QSE_SIZEOF(*nwad));
nwad->type = QSE_NWAD_IN4;
nwad->u.in4.addr.value = in->sin_addr.s_addr;
nwad->u.in4.port = in->sin_port;
break;
}
#if defined(AF_INET6)
case AF_INET6:
{
struct sockaddr_in6* in;
in = (struct sockaddr_in6*)addr;
addrsize = QSE_SIZEOF(*in);
memset (nwad, 0, QSE_SIZEOF(*nwad));
nwad->type = QSE_NWAD_IN6;
memcpy (&nwad->u.in6.addr, &in->sin6_addr, QSE_SIZEOF(nwad->u.in6.addr));
nwad->u.in6.scope = in->sin6_scope_id;
nwad->u.in6.port = in->sin6_port;
break;
}
#endif
}
return addrsize;
}
static int nwad_to_sockaddr (
const qse_nwad_t* nwad, struct sockaddr_storage* addr)
{
int addrsize = -1;
switch (nwad->type)
{
case QSE_NWAD_IN4:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)addr;
addrsize = QSE_SIZEOF(*in);
memset (in, 0, addrsize);
in->sin_family = AF_INET;
in->sin_addr.s_addr = nwad->u.in4.addr.value;
in->sin_port = nwad->u.in4.port;
break;
}
case QSE_NWAD_IN6:
{
#if defined(AF_INET6)
struct sockaddr_in6* in;
in = (struct sockaddr_in6*)addr;
addrsize = QSE_SIZEOF(*in);
memset (in, 0, addrsize);
in->sin6_family = AF_INET6;
memcpy (&in->sin6_addr, &nwad->u.in6.addr, QSE_SIZEOF(nwad->u.in6.addr));
in->sin6_scope_id = nwad->u.in6.scope;
in->sin6_port = nwad->u.in6.port;
#endif
break;
}
}
return addrsize;
}
/* ------------------------------------------------------------------- */
static int sock_open (qse_upxd_t* upxd, qse_upxd_sock_t* sock)
{
int fd = -1, flag;
int syserr = 1;
struct sockaddr_storage addr;
int addrsize;
addrsize = nwad_to_sockaddr (&sock->bind, &addr);
if (addrsize <= -1)
{
qse_upxd_seterrnum (upxd, QSE_UPXD_ENOIMPL);
syserr = 0;
goto oops;
}
/* TODO: if AF_INET6 is not defined sockaddr_storage is not available...
* create your own union or somehting similar... */
fd = socket (addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
if (fd <= -1) goto oops;
flag = fcntl (fd, F_GETFD);
if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC);
if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1)
{
#if defined(IPV6_V6ONLY)
if (errno == EADDRINUSE && addr.ss_family == AF_INET6)
{
int on = 1;
setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) goto oops;
}
else goto oops;
#else
goto oops;
#endif
}
if (sock->dev)
{
#if defined(SO_BINDTODEVICE)
struct ifreq ifr;
qse_size_t wsz, msz;
memset (&ifr, 0, sizeof(ifr));
#if defined(QSE_CHAR_IS_MCHAR)
qse_mbscpy (ifr.ifr_name, sock->dev, QSE_COUNTOF(ifr.ifr_name));
#else
msz = QSE_COUNTOF(ifr.ifr_name);
if (qse_wcstombs (sock->dev, &wsz, ifr.ifr_name, &msz) <= -1)
{
qse_upxd_seterrnum (upxd, QSE_UPXD_EINVAL);
syserr = 0;
goto oops;
}
#endif
if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, QSE_SIZEOF(ifr)) <= -1) goto oops;
#endif
}
flag = fcntl (fd, F_GETFL);
if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK);
sock->handle.i = fd;
return 0;
oops:
if (syserr) qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
if (fd >= 0) close (fd);
return -1;
}
static void sock_close (qse_upxd_t* upxd, qse_upxd_sock_t* sock)
{
close (sock->handle.i);
}
static qse_ssize_t sock_recv (
qse_upxd_t* upxd, qse_upxd_sock_t* sock, void* buf, qse_size_t bufsize)
{
ssize_t ret;
struct sockaddr_storage addr;
socklen_t addrsize;
addrsize = QSE_SIZEOF(addr);
ret = recvfrom (sock->handle.i, buf, bufsize, 0, (struct sockaddr*)&addr, &addrsize);
if (ret <= -1) qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
else
{
if (sockaddr_to_nwad (&addr, &sock->from) <= -1)
{
qse_upxd_seterrnum (upxd, QSE_UPXD_ENOIMPL);
ret = -1;
}
}
return ret;
}
static qse_ssize_t sock_send (
qse_upxd_t* upxd, qse_upxd_sock_t* sock, const void* buf, qse_size_t bufsize)
{
struct sockaddr_storage addr;
int addrsize;
ssize_t ret;
addrsize = nwad_to_sockaddr (&sock->to, &addr);
if (addrsize <= -1)
{
qse_upxd_seterrnum (upxd, QSE_UPXD_ENOIMPL);
return -1;
}
ret = sendto (sock->handle.i, buf, bufsize,
0, (struct sockaddr*)&addr, addrsize);
if (ret <= -1) qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
return ret;
}
/* ------------------------------------------------------------------- */
static int session_config (
qse_upxd_t* upxd, qse_upxd_session_t* session)
{
/* you can check the source address (session->from).
* you can set the destination address.
* you can set the binding address.
* you can set the binding interface.
*/
qse_strtonwad (QSE_T("127.0.0.1:9991"), &session->config.peer);
qse_strtonwad (QSE_T("0.0.0.0:0"), &session->config.bind);
/*qse_strxcpy (session->config.dev, QSE_COUNTOF(session->config.dev), QSE_T("eth1"));*/
session->config.dormancy = 10000;
return 0;
}
static void session_error (
qse_upxd_t* upxd, qse_upxd_session_t* session)
{
if (session->server)
{
}
else
{
/* session->local.nwad is not associated with a session. */
}
qse_printf (QSE_T("ERROR IN SESSION COMMUNICATION\n"));
}
/* ------------------------------------------------------------------- */
struct mux_ev_t
{
qse_ubi_t handle;
int reqmask;
qse_upxd_muxcb_t cbfun;
void* cbarg;
};
struct mux_t
{
int fd;
struct
{
struct epoll_event* ptr;
qse_size_t len;
qse_size_t capa;
} ee;
struct
{
struct mux_ev_t** ptr;
qse_size_t capa;
} mev;
};
#define MUX_EV_ALIGN 64
static void* mux_open (qse_upxd_t* upxd)
{
struct mux_t* mux;
mux = qse_upxd_allocmem (upxd, QSE_SIZEOF(*mux));
if (mux == QSE_NULL) return QSE_NULL;
memset (mux, 0, QSE_SIZEOF(*mux));
#if defined(HAVE_EPOLL_CREATE1) && defined(O_CLOEXEC)
mux->fd = epoll_create1 (O_CLOEXEC);
#else
mux->fd = epoll_create (100);
#endif
if (mux->fd <= -1)
{
qse_upxd_freemem (upxd, mux);
qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
return QSE_NULL;
}
#if defined(HAVE_EPOLL_CREATE1) && defined(O_CLOEXEC)
/* nothing else to do */
#else
{
int flag = fcntl (mux->fd, F_GETFD);
if (flag >= 0) fcntl (mux->fd, F_SETFD, flag | FD_CLOEXEC);
}
#endif
return mux;
}
static void mux_close (qse_upxd_t* upxd, void* vmux)
{
struct mux_t* mux = (struct mux_t*)vmux;
if (mux->ee.ptr) qse_upxd_freemem (upxd, mux->ee.ptr);
if (mux->mev.ptr)
{
qse_size_t i;
for (i = 0; i < mux->mev.capa; i++)
if (mux->mev.ptr[i]) qse_upxd_freemem (upxd, mux->mev.ptr[i]);
qse_upxd_freemem (upxd, mux->mev.ptr);
}
close (mux->fd);
qse_upxd_freemem (upxd, mux);
}
static int mux_addhnd (
qse_upxd_t* upxd, void* vmux, qse_ubi_t handle,
qse_upxd_muxcb_t cbfun, void* cbarg)
{
struct mux_t* mux = (struct mux_t*)vmux;
struct epoll_event ev;
struct mux_ev_t* mev;
ev.events = EPOLLIN; /* inspect IN and HUP only */
if (handle.i >= mux->mev.capa)
{
struct mux_ev_t** tmp;
qse_size_t tmpcapa, i;
tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN);
tmp = (struct mux_ev_t**) qse_upxd_reallocmem (
upxd, mux->mev.ptr,
QSE_SIZEOF(*mux->mev.ptr) * tmpcapa);
if (tmp == QSE_NULL) return -1;
for (i = mux->mev.capa; i < tmpcapa; i++) tmp[i] = QSE_NULL;
mux->mev.ptr = tmp;
mux->mev.capa = tmpcapa;
}
if (mux->mev.ptr[handle.i] == QSE_NULL)
{
/* the location of the data passed to epoll_ctl()
* must not change unless i update the info with epoll()
* whenever there is reallocation. so i simply
* make mux-mev.ptr reallocatable but auctual
* data fixed once allocated. */
mux->mev.ptr[handle.i] = qse_upxd_allocmem (
upxd, QSE_SIZEOF(*mux->mev.ptr[handle.i]));
if (mux->mev.ptr[handle.i] == QSE_NULL) return -1;
}
if (mux->ee.len >= mux->ee.capa)
{
struct epoll_event* tmp;
tmp = qse_upxd_reallocmem (
upxd, mux->ee.ptr,
QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2);
if (tmp == QSE_NULL) return -1;
mux->ee.ptr = tmp;
mux->ee.capa = (mux->ee.capa + 1) * 2;
}
mev = mux->mev.ptr[handle.i];
mev->handle = handle;
mev->cbfun = cbfun;
mev->cbarg = cbarg;
ev.data.ptr = mev;
if (epoll_ctl (mux->fd, EPOLL_CTL_ADD, handle.i, &ev) <= -1)
{
/* don't rollback ee.ptr */
qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
return -1;
}
mux->ee.len++;
return 0;
}
static int mux_delhnd (qse_upxd_t* upxd, void* vmux, qse_ubi_t handle)
{
struct mux_t* mux = (struct mux_t*)vmux;
if (epoll_ctl (mux->fd, EPOLL_CTL_DEL, handle.i, QSE_NULL) <= -1)
{
qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
return -1;
}
mux->ee.len--;
return 0;
}
static int mux_poll (qse_upxd_t* upxd, void* vmux, qse_ntime_t timeout)
{
struct mux_t* mux = (struct mux_t*)vmux;
struct mux_ev_t* mev;
int nfds, i;
nfds = epoll_wait (mux->fd, mux->ee.ptr, mux->ee.len, timeout);
if (nfds <= -1)
{
qse_upxd_seterrnum (upxd, syserr_to_errnum(errno));
return -1;
}
for (i = 0; i < nfds; i++)
{
mev = mux->ee.ptr[i].data.ptr;
if (mux->ee.ptr[i].events & (EPOLLIN | EPOLLHUP))
mev->cbfun (upxd, mux, mev->handle, mev->cbarg);
}
return 0;
}
/* ------------------------------------------------------------------- */
void lock_acquire (qse_upxd_t* upxd)
{
}
void lock_release (qse_upxd_t* upxd)
{
}
/* ------------------------------------------------------------------- */
static qse_upxd_cbs_t upxd_cbs =
{
/* socket */
{ sock_open, sock_close, sock_recv, sock_send },
/* session */
{ session_config, session_error },
/* multiplexer */
{ mux_open, mux_close, mux_addhnd, mux_delhnd, mux_poll },
/* lock */
{ lock_acquire, lock_release }
};
static qse_upxd_t* g_upxd = QSE_NULL;
static void sigint (int sig)
{
if (g_upxd) qse_upxd_stop (g_upxd);
}
int upxd_main (int argc, qse_char_t* argv[])
{
qse_upxd_t* upxd = QSE_NULL;
int ret = -1, i;
if (argc <= 1)
{
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <server-address> ...\n"), argv[0]);
goto oops;
}
upxd = qse_upxd_open (QSE_MMGR_GETDFL(), 0);
if (upxd == QSE_NULL)
{
qse_fprintf (QSE_STDERR, QSE_T("Cannot open upxd\n"));
goto oops;
}
for (i = 1; i < argc; i++)
{
qse_nwad_t nwad;
if (qse_strtonwad (argv[i], &nwad) <= -1)
{
qse_fprintf (QSE_STDERR,
QSE_T("Wrong server - %s\n"), argv[i]);
goto oops;
}
if (qse_upxd_addserver (upxd, &nwad, QSE_NULL) == QSE_NULL)
{
qse_fprintf (QSE_STDERR,
QSE_T("Failed to add server - %s\n"), argv[i]);
goto oops;
}
}
g_upxd = upxd;
signal (SIGINT, sigint);
signal (SIGPIPE, SIG_IGN);
qse_upxd_setcbs (upxd, &upxd_cbs);
ret = qse_upxd_loop (upxd, 5000);
signal (SIGINT, SIG_DFL);
signal (SIGPIPE, SIG_DFL);
g_upxd = QSE_NULL;
if (ret <= -1)
qse_fprintf (QSE_STDERR, QSE_T("Error - %d\n"), (int)qse_upxd_geterrnum(upxd));
oops:
if (upxd) qse_upxd_close (upxd);
return ret;
}
int qse_main (int argc, qse_achar_t* argv[])
{
#if defined(_WIN32)
char locale[100];
UINT codepage = GetConsoleOutputCP();
if (codepage == CP_UTF8)
{
/*SetConsoleOUtputCP (CP_UTF8);*/
qse_setdflcmgr (qse_utf8cmgr);
}
else
{
sprintf (locale, ".%u", (unsigned int)codepage);
setlocale (LC_ALL, locale);
qse_setdflcmgr (qse_slmbcmgr);
}
#else
setlocale (LC_ALL, "");
qse_setdflcmgr (qse_slmbcmgr);
#endif
return qse_runmain (argc, argv, upxd_main);
}