Преглед изворни кода

Implemented client events.

silvioprog пре 6 година
родитељ
комит
7ae73f7e23
4 измењених фајлова са 87 додато и 2 уклоњено
  1. 22 2
      include/sagui.h
  2. 46 0
      src/sg_httpsrv.c
  3. 2 0
      src/sg_httpsrv.h
  4. 17 0
      test/test_httpsrv.c

+ 22 - 2
include/sagui.h

@@ -73,8 +73,8 @@ extern "C" {
 #endif
 
 #define SG_VERSION_MAJOR 2
-#define SG_VERSION_MINOR 4
-#define SG_VERSION_PATCH 7
+#define SG_VERSION_MINOR 5
+#define SG_VERSION_PATCH 0
 #define SG_VERSION_HEX                                                         \
   ((SG_VERSION_MAJOR << 16) | (SG_VERSION_MINOR << 8) | (SG_VERSION_PATCH))
 
@@ -578,6 +578,15 @@ struct sg_httpres;
  */
 struct sg_httpsrv;
 
+/**
+ * Callback signature used to handle client events.
+ * \param[out] cls User-defined closure.
+ * \param[out] client Socket handle of the client.
+ * \param[in,out] closed Indicates if the client is connected allowing to
+ * close it.
+ */
+typedef void (*sg_httpsrv_cli_cb)(void *cls, const void *client, bool *closed);
+
 /**
  * Callback signature used to grant or deny the user access to the server
  * resources.
@@ -1418,6 +1427,17 @@ SG_EXTERN uint16_t sg_httpsrv_port(struct sg_httpsrv *srv);
  */
 SG_EXTERN bool sg_httpsrv_is_threaded(struct sg_httpsrv *srv);
 
+/**
+ * Sets the server callback for client events.
+ * \param[in] srv Server handle.
+ * \param[in] cb Callback to handle client events.
+ * \param[in] cls User-defined closure.
+ * \retval 0 Success.
+ * \retval EINVAL Invalid argument.
+ */
+SG_EXTERN int sg_httpsrv_set_cli_cb(struct sg_httpsrv *srv,
+                                    sg_httpsrv_cli_cb cb, void *cls);
+
 /**
  * Sets the server uploading callbacks.
  * \param[in] srv Server handle.

+ 46 - 0
src/sg_httpsrv.c

@@ -54,6 +54,17 @@ static int sg__httpsrv_ahc(void *cls, struct MHD_Connection *con,
                            size_t *upld_data_size, void **con_cls) {
   struct sg_httpsrv *srv = cls;
   struct sg_httpreq *req = *con_cls;
+  const union MHD_ConnectionInfo *info;
+#ifdef SG_TESTING
+  if (con) {
+#endif
+    info =
+      MHD_get_connection_info(con, MHD_CONNECTION_INFO_SOCKET_CONTEXT, NULL);
+    if (info && info->socket_context)
+      return MHD_NO;
+#ifdef SG_TESTING
+  }
+#endif
   if (!req) {
     req = sg__httpreq_new(con, version, method, url);
     if (!req)
@@ -85,6 +96,30 @@ static void sg__httpsrv_rcc(void *cls, __SG_UNUSED struct MHD_Connection *con,
   *con_cls = NULL;
 }
 
+static void sg__httpsrv_ncc(void *cls, __SG_UNUSED struct MHD_Connection *con,
+                            __SG_UNUSED void **socket_ctx,
+                            enum MHD_ConnectionNotificationCode toe) {
+  struct sg_httpsrv *srv = cls;
+  const union MHD_ConnectionInfo *info;
+  bool closed;
+  if (!srv->cli_cb)
+    return;
+  info = MHD_get_connection_info(con, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
+  switch (toe) {
+    case MHD_CONNECTION_NOTIFY_STARTED:
+      closed = false;
+      srv->cli_cb(srv->cli_cls, info->client_addr, &closed);
+      *((bool *) socket_ctx) = closed;
+      break;
+    case MHD_CONNECTION_NOTIFY_CLOSED:
+      closed = true;
+      srv->cli_cb(srv->cli_cls, info->client_addr, &closed);
+      break;
+    default:
+      break;
+  }
+}
+
 static void sg__httpsrv_addopt(struct MHD_OptionItem ops[8], unsigned char *pos,
                                enum MHD_OPTION opt, intptr_t val, void *ptr) {
   ops[*pos].option = opt;
@@ -113,6 +148,8 @@ static bool sg__httpsrv_listen(struct sg_httpsrv *srv, const char *key,
                      (intptr_t) sg__httpsrv_oel, srv);
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_COMPLETED,
                      (intptr_t) sg__httpsrv_rcc, srv);
+  sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_CONNECTION,
+                     (intptr_t) sg__httpsrv_ncc, srv);
   if (srv->con_limit > 0)
     sg__httpsrv_addopt(ops, &pos, MHD_OPTION_CONNECTION_LIMIT, srv->con_limit,
                        NULL);
@@ -263,6 +300,15 @@ bool sg_httpsrv_is_threaded(struct sg_httpsrv *srv) {
   return false;
 }
 
+int sg_httpsrv_set_cli_cb(struct sg_httpsrv *srv, sg_httpsrv_cli_cb cb,
+                          void *cls) {
+  if (!srv || !cb)
+    return EINVAL;
+  srv->cli_cb = cb;
+  srv->cli_cls = cls;
+  return 0;
+}
+
 int sg_httpsrv_set_upld_cbs(struct sg_httpsrv *srv, sg_httpupld_cb cb,
                             void *cls, sg_write_cb write_cb, sg_free_cb free_cb,
                             sg_save_cb save_cb, sg_save_as_cb save_as_cb) {

+ 2 - 0
src/sg_httpsrv.h

@@ -33,6 +33,7 @@
 
 struct sg_httpsrv {
   struct MHD_Daemon *handle;
+  sg_httpsrv_cli_cb cli_cb;
   sg_httpauth_cb auth_cb;
   sg_httpupld_cb upld_cb;
   sg_write_cb upld_write_cb;
@@ -41,6 +42,7 @@ struct sg_httpsrv {
   sg_save_as_cb upld_save_as_cb;
   sg_httpreq_cb req_cb;
   sg_err_cb err_cb;
+  void *cli_cls;
   void *upld_cls;
   void *cls;
   char *uplds_dir;

+ 17 - 0
test/test_httpsrv.c

@@ -147,6 +147,12 @@ static bool dummy_httpreq_httpauth_cb(void *cls, struct sg_httpauth *auth,
   return true;
 }
 
+static void dummy_httpsrv_cli_cb(void *cls, const void *client, bool *closed) {
+  (void) cls;
+  (void) client;
+  (void) closed;
+}
+
 static int dummy_httpupld_cb(void *cls, void **handle, const char *dir,
                              const char *field, const char *name,
                              const char *mime, const char *encoding) {
@@ -557,6 +563,16 @@ static void test_httpsrv_is_threaded(struct sg_httpsrv *srv) {
   ASSERT(errno == 0);
 }
 
+static void test__httpsrv_set_cli_cb(struct sg_httpsrv *srv) {
+  int dummy = 123;
+  ASSERT(sg_httpsrv_set_cli_cb(NULL, dummy_httpsrv_cli_cb, &dummy) == EINVAL);
+  ASSERT(sg_httpsrv_set_cli_cb(srv, NULL, &dummy) == EINVAL);
+
+  ASSERT(sg_httpsrv_set_cli_cb(srv, dummy_httpsrv_cli_cb, &dummy) == 0);
+  ASSERT(srv->cli_cb == dummy_httpsrv_cli_cb);
+  ASSERT(*((int *) srv->cli_cls) == 123);
+}
+
 static void test__httpsrv_set_upld_cbs(struct sg_httpsrv *srv) {
   int dummy = 123;
 
@@ -734,6 +750,7 @@ int main(void) {
   test_httpsrv_shutdown(srv);
   test_httpsrv_port(srv);
   test_httpsrv_is_threaded(srv);
+  test__httpsrv_set_cli_cb(srv);
   test__httpsrv_set_upld_cbs(srv);
   test_httpsrv_set_upld_dir(srv);
   test_httpsrv_upld_dir(srv);