From c60ca301c49d5fced37810abe6aeb34524d9dcf2 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 20 Jun 2012 15:12:18 +0000 Subject: [PATCH] enhanced qse_upxd_t --- qse/include/qse/cmn/mbwc.h | 2 +- qse/include/qse/net/Makefile.in | 2 +- qse/include/qse/net/upxd.h | 190 ++++++++-- qse/lib/cmn/rex.c | 2 +- qse/lib/net/Makefile.am | 3 +- qse/lib/net/Makefile.in | 10 +- qse/lib/net/upxd.c | 471 +++++++++++++++++++++++-- qse/lib/net/upxd.h | 71 +++- qse/samples/net/Makefile.am | 3 +- qse/samples/net/Makefile.in | 16 +- qse/samples/net/http01.c | 145 ++++---- qse/samples/net/upxd01.c | 598 ++++++++++++++++++++++++++++++++ 12 files changed, 1375 insertions(+), 138 deletions(-) create mode 100644 qse/samples/net/upxd01.c diff --git a/qse/include/qse/cmn/mbwc.h b/qse/include/qse/cmn/mbwc.h index d89559d5..cda0d9e1 100644 --- a/qse/include/qse/cmn/mbwc.h +++ b/qse/include/qse/cmn/mbwc.h @@ -298,7 +298,7 @@ qse_wchar_t* qse_mbsatowcsalldup ( * n = qse_wcstombs (wcs, &wcslen, mbs, &mbslen); * if (n <= -1) * { - * // wcs fully scanned and mbs null-terminated + * // conversion error * } * @endcode */ diff --git a/qse/include/qse/net/Makefile.in b/qse/include/qse/net/Makefile.in index 57b53064..389ea4f2 100644 --- a/qse/include/qse/net/Makefile.in +++ b/qse/include/qse/net/Makefile.in @@ -229,7 +229,7 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ 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 .SUFFIXES: diff --git a/qse/include/qse/net/upxd.h b/qse/include/qse/net/upxd.h index 2a340589..b6f41cf3 100644 --- a/qse/include/qse/net/upxd.h +++ b/qse/include/qse/net/upxd.h @@ -5,8 +5,8 @@ This file is part of QSE. QSE is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QSE is distributed in the hope that it will be useful, @@ -14,7 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 . */ @@ -23,65 +23,193 @@ #include #include +#include +#include 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 { QSE_UPXD_ENOERR, - QSE_UPXD_ENOMEM, - QSE_UPXD_EINVAL, - QSE_UPXD_EACCES, - QSE_UPXD_ENOENT, - QSE_UPXD_EEXIST, - QSE_UPXD_EINTR, - QSE_UPXD_EAGAIN, + QSE_UPXD_ENOMEM, + QSE_UPXD_EINVAL, + QSE_UPXD_EACCES, + QSE_UPXD_ENOENT, + QSE_UPXD_EEXIST, + QSE_UPXD_EINTR, + QSE_UPXD_EAGAIN, + QSE_UPXD_EINTERN, + QSE_UPXD_ESYSERR, QSE_UPXD_ENOIMPL, 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_nwad_t nwad; - qse_ubi_t handle; + qse_ubi_t handle; + qse_nwad_t bind; + const qse_char_t* dev; + qse_nwad_t from; + qse_nwad_t to; }; -struct qse_upxd_client_t +struct qse_upxd_session_t { - qse_nwad_t remote_addr; - qse_nwad_t local_addr; + /** the server that this session belongs to */ + 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 { - int (*open) (qse_upxd_t* upxd, qse_upxd_server_t* server); - void (*close) (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_sock_t* server); qse_ssize_t (*recv) ( - qse_upxd_t* upxd, - qse_upxd_client_t* client, - qse_mchar_t* buf, qse_size_t bufsize); + qse_upxd_t* upxd, qse_upxd_sock_t* server, + void* buf, qse_size_t bufsize); qse_ssize_t (*send) ( - qse_upxd_t* upxd, - qse_upxd_client_t* client, - const qse_mchar_t* buf, qse_size_t bufsize); - } server; -}; + qse_upxd_t* upxd, qse_upxd_sock_t* sock, + const void* buf, qse_size_t bufsize); + } sock; + 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 extern "C" { #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 } #endif diff --git a/qse/lib/cmn/rex.c b/qse/lib/cmn/rex.c index 328e3f63..1fe8a13b 100644 --- a/qse/lib/cmn/rex.c +++ b/qse/lib/cmn/rex.c @@ -1753,7 +1753,7 @@ static qse_lda_walk_t walk_cands_for_match ( { 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)): (node->u.c == *cand->mptr) ; diff --git a/qse/lib/net/Makefile.am b/qse/lib/net/Makefile.am index 2cf4d0da..9cbd51c2 100644 --- a/qse/lib/net/Makefile.am +++ b/qse/lib/net/Makefile.am @@ -7,10 +7,11 @@ AM_CPPFLAGS = \ lib_LTLIBRARIES = libqsenet.la libqsenet_la_SOURCES = \ + httpd.h \ + upxd.h \ http.c \ htre.c \ htrd.c \ - httpd.h \ httpd.c \ httpd-cgi.c \ httpd-proxy.c \ diff --git a/qse/lib/net/Makefile.in b/qse/lib/net/Makefile.in index b5e92dde..94b1faa9 100644 --- a/qse/lib/net/Makefile.in +++ b/qse/lib/net/Makefile.in @@ -79,7 +79,8 @@ am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libqsenet_la_DEPENDENCIES = 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_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -256,15 +257,17 @@ AM_CPPFLAGS = \ lib_LTLIBRARIES = libqsenet.la libqsenet_la_SOURCES = \ + httpd.h \ + upxd.h \ http.c \ htre.c \ htrd.c \ - httpd.h \ httpd.c \ httpd-cgi.c \ httpd-proxy.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_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-task.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: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/qse/lib/net/upxd.c b/qse/lib/net/upxd.c index 53bf44b3..d431dd0c 100644 --- a/qse/lib/net/upxd.c +++ b/qse/lib/net/upxd.c @@ -5,8 +5,8 @@ This file is part of QSE. QSE is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QSE is distributed in the hope that it will be useful, @@ -14,11 +14,21 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 . */ #include "upxd.h" +#include + +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) { @@ -53,16 +63,11 @@ int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr) void qse_upxd_fini (qse_upxd_t* upxd) { - free_server_list (upxd, upxd->server.list); - QSE_ASSERT (upxd->server.navail == 0); - upxd->server.list = QSE_NULL; + QSE_ASSERTX (upxd->server.nactive == 0, + "Deactivate all servers before destroying me"); + 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) { return upxd->errnum; @@ -73,37 +78,461 @@ void qse_upxd_seterrnum (qse_upxd_t* upxd, qse_upxd_errnum_t 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_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)); if (server == QSE_NULL) { upxd->errnum = QSE_UPXD_ENOMEM; - return -1; + return QSE_NULL; } QSE_MEMSET (server, 0, QSE_SIZEOF(*server)); - server->nwad = *nwad; - - if (upxd->cbs->server.open (upxd, server) <= -1) + if (dev) { - QSE_MMGR_FREE (upxd->mmgr, server); - return -1; + qse_strxcpy (server->dev, QSE_COUNTOF(server->dev), dev); + 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->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->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) { + 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; } diff --git a/qse/lib/net/upxd.h b/qse/lib/net/upxd.h index ce4e393b..ac06ad6d 100644 --- a/qse/lib/net/upxd.h +++ b/qse/lib/net/upxd.h @@ -5,8 +5,8 @@ This file is part of QSE. QSE is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QSE is distributed in the hope that it will be useful, @@ -14,7 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 . */ @@ -22,15 +22,80 @@ #define _QSE_LIB_NET_UPXD_H_ #include +#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 { + QSE_DEFINE_COMMON_FIELDS (upxd) + qse_upxd_errnum_t errnum; + int stopreq; + qse_upxd_cbs_t* cbs; struct { qse_upxd_server_t* list; + qse_size_t nactive; } 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 diff --git a/qse/samples/net/Makefile.am b/qse/samples/net/Makefile.am index 31c2fd27..1e4325a8 100644 --- a/qse/samples/net/Makefile.am +++ b/qse/samples/net/Makefile.am @@ -5,7 +5,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/include \ -I$(includedir) -bin_PROGRAMS = http01 +bin_PROGRAMS = http01 upxd01 LDFLAGS += -L../../lib/cmn -L../../lib/net LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) -lssl @@ -15,4 +15,5 @@ LDADD += $(UNICOWS_LIBS) endif http01_SOURCES = http01.c +upxd01_SOURCES = upxd01.c diff --git a/qse/samples/net/Makefile.in b/qse/samples/net/Makefile.in index be02db35..cb22888b 100644 --- a/qse/samples/net/Makefile.in +++ b/qse/samples/net/Makefile.in @@ -34,7 +34,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -bin_PROGRAMS = http01$(EXEEXT) +bin_PROGRAMS = http01$(EXEEXT) upxd01$(EXEEXT) @WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS) subdir = samples/net DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in @@ -59,6 +59,11 @@ am__DEPENDENCIES_1 = @WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) http01_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(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 = depcomp = $(SHELL) $(top_srcdir)/ac/depcomp am__depfiles_maybe = depfiles @@ -72,8 +77,8 @@ CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ -SOURCES = $(http01_SOURCES) -DIST_SOURCES = $(http01_SOURCES) +SOURCES = $(http01_SOURCES) $(upxd01_SOURCES) +DIST_SOURCES = $(http01_SOURCES) $(upxd01_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -232,6 +237,7 @@ AM_CPPFLAGS = \ LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \ $(SENDFILE_LIBS) -lssl $(am__append_1) http01_SOURCES = http01.c +upxd01_SOURCES = upxd01.c all: all-am .SUFFIXES: @@ -312,6 +318,9 @@ clean-binPROGRAMS: http01$(EXEEXT): $(http01_OBJECTS) $(http01_DEPENDENCIES) $(EXTRA_http01_DEPENDENCIES) @rm -f http01$(EXEEXT) $(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: -rm -f *.$(OBJEXT) @@ -320,6 +329,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http01.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd01.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 5f1c63e4..5f4fafff 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -10,7 +10,8 @@ #include #include #include -#if defined(_WIN32) + +#if defined(_WIN32) # include #else # include @@ -71,7 +72,7 @@ static qse_ssize_t xsendfile ( vec.sfv_flag = 0; if (offset) { - vec.sfv_off = *offset; + vec.sfv_off = *offset; } else { @@ -154,7 +155,7 @@ static qse_httpd_errnum_t syserr_to_errnum (int e) case EEXIST: return QSE_HTTPD_EEXIST; - + case EINTR: return QSE_HTTPD_EINTR; @@ -177,8 +178,8 @@ struct httpd_xtn_t /* ------------------------------------------------------------------- */ static int init_xtn_ssl ( - httpd_xtn_t* xtn, - const qse_mchar_t* pemfile, + httpd_xtn_t* xtn, + const qse_mchar_t* pemfile, const qse_mchar_t* keyfile/*, 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 || 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*/) { qse_mchar_t buf[128]; @@ -240,7 +241,7 @@ static int sockaddr_to_nwad ( { case AF_INET: { - struct sockaddr_in* in; + struct sockaddr_in* in; in = (struct sockaddr_in*)addr; addrsize = QSE_SIZEOF(*in); @@ -254,7 +255,7 @@ static int sockaddr_to_nwad ( #if defined(AF_INET6) case AF_INET6: { - struct sockaddr_in6* in; + struct sockaddr_in6* in; in = (struct sockaddr_in6*)addr; addrsize = QSE_SIZEOF(*in); @@ -280,7 +281,7 @@ static int nwad_to_sockaddr ( { case QSE_NWAD_IN4: { - struct sockaddr_in* in; + struct sockaddr_in* in; in = (struct sockaddr_in*)addr; addrsize = QSE_SIZEOF(*in); @@ -295,7 +296,7 @@ static int nwad_to_sockaddr ( case QSE_NWAD_IN6: { #if defined(AF_INET6) - struct sockaddr_in6* in; + struct sockaddr_in6* in; in = (struct sockaddr_in6*)addr; 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)); #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. */ /*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 (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 ( - qse_httpd_t* httpd, + qse_httpd_t* httpd, qse_httpd_server_t* server, qse_httpd_client_t* client) { struct sockaddr_storage addr; @@ -396,7 +397,7 @@ static int server_accept ( addrlen = QSE_SIZEOF(addr); fd = accept (server->handle.i, (struct sockaddr*)&addr, &addrlen); - if (fd <= -1) + if (fd <= -1) { qse_httpd_seterrnum (httpd, syserr_to_errnum (errno)); return -1; @@ -472,7 +473,7 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) flag = fcntl (fd, F_GETFL); 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; 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 (ret == EINPROGRESS) return 0; - if (ret != 0) + if (ret != 0) { qse_httpd_seterrnum (httpd, syserr_to_errnum (ret)); return -1; @@ -552,7 +553,7 @@ struct mux_t struct { struct epoll_event* ptr; - qse_size_t len; + qse_size_t len; qse_size_t capa; } ee; @@ -579,7 +580,7 @@ static void* mux_open (qse_httpd_t* httpd) #else mux->fd = epoll_create (100); #endif - if (mux->fd <= -1) + if (mux->fd <= -1) { qse_httpd_freemem (httpd, mux); 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; if (mux->ee.ptr) qse_httpd_freemem (httpd, mux->ee.ptr); - if (mux->mev.ptr) + if (mux->mev.ptr) { qse_size_t 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 ( - 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) { struct mux_t* mux = (struct mux_t*)vmux; @@ -635,12 +636,12 @@ static int mux_addhnd ( { 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_httpd_reallocmem ( - httpd, mux->mev.ptr, - QSE_SIZEOF(*mux->mev.ptr) * tmpcapa); + httpd, 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; @@ -648,7 +649,7 @@ static int mux_addhnd ( 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() * 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 ( httpd, 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_httpd_reallocmem ( - httpd, mux->ee.ptr, + httpd, mux->ee.ptr, QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2); 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; - if (mux->ee.ptr[i].events & EPOLLIN) + if (mux->ee.ptr[i].events & EPOLLIN) mask |= QSE_HTTPD_MUX_READ; if (mux->ee.ptr[i].events & EPOLLOUT) 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; if (mev->reqmask & 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_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) @@ -800,9 +801,9 @@ static int file_stat ( { qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); 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. */ 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); fd = open (path, flags, 0); - if (fd <= -1) + if (fd <= -1) { qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); return -1; @@ -857,7 +858,7 @@ qse_printf (QSE_T("opened file %hs\n"), path); } 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) { int fd; @@ -870,7 +871,7 @@ static int file_wopen ( qse_printf (QSE_T("opening file [%hs] for writing\n"), path); fd = open (path, flags, 0644); - if (fd <= -1) + if (fd <= -1) { qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); return -1; @@ -887,14 +888,14 @@ qse_printf (QSE_T("closing file %d\n"), handle.i); } 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) { return read (handle.i, buf, len); } 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) { return write (handle.i, buf, len); @@ -916,7 +917,7 @@ static void client_shutdown ( shutdown (client->handle.i, 2); #endif } - + static qse_ssize_t client_recv ( qse_httpd_t* httpd, qse_httpd_client_t* client, 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); 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); else 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); 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); else 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); 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 */ return -1; } @@ -1012,7 +1013,7 @@ qse_fflush (QSE_STDOUT); ret = SSL_accept (ssl); 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. */ 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 ( - qse_httpd_t* httpd, qse_httpd_client_t* client, + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek) { int method; @@ -1065,12 +1066,12 @@ static int process_request ( content_received = (qse_htre_getcontentlen(req) > 0); /* 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 */ qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req)); 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), (peek? QSE_MT("PEEK"): QSE_MT("HANDLE")), 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_getqmethodname(req) ); -if (qse_htre_getqparam(req)) +if (qse_htre_getqparam(req)) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparam(req)); 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)); } @@ -1091,21 +1092,21 @@ if (qse_htre_getcontentlen(req) > 0) if (peek) { 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 */ qse_httpd_discardcontent (httpd, req); } if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) && - (req->version.major > 1 || - (req->version.major == 1 && req->version.minor >= 1)) && + (req->version.major > 1 || + (req->version.major == 1 && req->version.minor >= 1)) && !content_received) { /* 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 */ - + /* TODO: determine if to return 100-continue or other errors */ { 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)); } @@ -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); #endif } - else + else #endif /*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_CHUNKED)) { @@ -1191,7 +1192,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); auth = qse_htre_getheaderval (req, QSE_MT("Authorization")); if (auth) { - /* TODO: PERFORM authorization... */ + /* TODO: PERFORM authorization... */ /* BASE64 decode... */ while (auth->next) auth = auth->next; authorized = 1; @@ -1276,7 +1277,7 @@ qse_printf (QSE_T("Host not included....\n")); goto oops; } } - else + else { const qse_mchar_t* host; qse_parseuri (); @@ -1287,15 +1288,15 @@ qse_printf (QSE_T("Host not included....\n")); #if 0 if (peek) { - if (req->attr.expect && - (req->version.major > 1 || - (req->version.major == 1 && req->version.minor >= 1)) && + if (req->attr.expect && + (req->version.major > 1 || + (req->version.major == 1 && req->version.minor >= 1)) && !content_received) { /* 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 */ - + if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0) { if (qse_httpd_entaskerror ( @@ -1385,7 +1386,7 @@ static qse_httpd_cbs_t httpd_cbs = /* server */ { server_open, server_close, server_accept }, - { peer_open, + { peer_open, peer_close, peer_connected, peer_recv, @@ -1397,9 +1398,9 @@ static qse_httpd_cbs_t httpd_cbs = mux_addhnd, mux_delhnd, mux_poll, - + mux_readable, - mux_writable + mux_writable }, /* file operation */ @@ -1414,11 +1415,11 @@ static qse_httpd_cbs_t httpd_cbs = /* client connection */ { client_close, - client_shutdown, - client_recv, - client_send, + client_shutdown, + client_recv, + client_send, client_sendfile, - client_accepted, + client_accepted, client_closed }, /* http request */ @@ -1468,7 +1469,7 @@ int httpd_main (int argc, qse_char_t* argv[]) { 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]); goto oops; } @@ -1497,7 +1498,7 @@ int qse_main (int argc, qse_achar_t* argv[]) { #if defined(_WIN32) char locale[100]; - UINT codepage = GetConsoleOutputCP(); + UINT codepage = GetConsoleOutputCP(); if (codepage == CP_UTF8) { /*SetConsoleOUtputCP (CP_UTF8);*/ diff --git a/qse/samples/net/upxd01.c b/qse/samples/net/upxd01.c new file mode 100644 index 00000000..3cd48a04 --- /dev/null +++ b/qse/samples/net/upxd01.c @@ -0,0 +1,598 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(_WIN32) +# include +#else +# include +# include +# include +# include +# include +# include +#endif + +#if defined(HAVE_EPOLL) +# if defined(HAVE_SYS_EPOLL_H) +# include +# 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 ...\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); +} +