IN PROGRESS: add C++ websocket client API
[src/app-framework-binder.git] / src / afb-wsj1.hpp
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author Gabin Fodop
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #pragma once
20
21 #define _GNU_SOURCE
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <assert.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <pthread.h>
30
31 extern "C" {
32 #include "json-c/json.h"
33 #include "afb-wsj1.h"
34 #include "afb-ws-client.h"
35 }
36
37 #include <iostream>
38 #include <string>
39 #include <functional>
40 #include <thread>
41 #include <mutex>
42
43 #include <systemd/sd-event.h>
44
45 namespace afb {
46
47 class wsj1 {
48 public:
49         wsj1() noexcept = default;
50         wsj1(const wsj1 &other) = delete;
51         wsj1(wsj1 &&other) noexcept { wsj1_ = other.wsj1_; other.wsj1_ = nullptr; }
52         wsj1(const std::string &uri) { connect(uri); }
53         wsj1(const char *uri) { connect(uri); }
54         ~wsj1() { if (wsj1_) disconnect(); }
55         void connect(const std::string &uri) { connect(uri.c_str()); }
56         void connect(const char *uri);
57         void disconnect();
58         void call(const char *api, const char *verb, char *request, std::function<void(const char*)> onreply);
59 #if 0
60         void call(const char *api, const char *verb, json_object *request, std::function<void(json_object*)> &onreply)
61         {
62                 call(api,verb,json_object_to_json_string_ext(request,JSON_C_TO_STRING_PLAIN),[](const char *x){onreply(std::string(x));});
63         };
64         void call(const std::string &api, const std::string &verb, const std::string &request, std::function<void(std::string&)> &onreply)
65         {
66                 call(api.c_str(), verb.c_str(), request.c_str(), [=](const char *x){onreply(std::string(x));});
67         }
68 #endif
69 public:
70         class msg {
71                 friend class wsj1;
72                 msg(afb_wsj1_msg *msg) : msg_{msg} {}
73                 afb_wsj1_msg *msg_;
74         public:
75         };
76 private:
77         afb_wsj1 *wsj1_ = nullptr;
78 private:
79         static struct afb_wsj1_itf wsj1_itf;
80         static void itf_hangup_(void *closure, struct afb_wsj1 *wsj1){};
81         static void itf_on_call_(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg){};
82         static void itf_on_event_(void *closure, const char *event, struct afb_wsj1_msg *msg);
83 private:
84         void on_hangup_(struct afb_wsj1 *wsj1);
85         void on_call_(const char *api, const char *verb, struct afb_wsj1_msg *msg);
86         void on_event_(const char *event, struct afb_wsj1_msg *msg);
87 private:
88         static sd_event *eloop_();
89 private:
90         static void on_reply_(void *closure, struct afb_wsj1_msg *msg);
91 };
92
93 struct afb_wsj1_itf wsj1::wsj1_itf = {
94         wsj1::itf_hangup_,
95         wsj1::itf_on_call_,
96         wsj1::itf_on_event_
97 };
98
99 void wsj1::connect(const char *uri) {
100         if (wsj1_)
101                 throw std::runtime_error("already-connected");
102         wsj1_ = afb_ws_client_connect_wsj1(eloop_(), uri, &wsj1_itf, reinterpret_cast<void*>(this));
103         if (!wsj1_)
104                 throw std::runtime_error("connection-failed");
105 }
106
107 void wsj1::disconnect() {
108         if (!wsj1_)
109                 throw std::runtime_error("not-connected");
110         afb_wsj1_unref(wsj1_);
111         wsj1_ = nullptr;
112 }
113
114 void wsj1::itf_on_event_(void *closure, const char *event, struct afb_wsj1_msg *msg) {
115         reinterpret_cast<wsj1*>(closure)->on_event_(event, msg);
116 }
117
118 void wsj1::on_event_(const char *event, struct afb_wsj1_msg *msg) {
119
120 }
121
122 void wsj1::call(const char *api, const char *verb, char *request, std::function<void(const char*)> onreply) {
123         if (!wsj1_)
124                 throw std::runtime_error("not-connected");
125         std::function<void(const char*)> *onrep = new std::function<void(const char*)>(onreply);
126         afb_wsj1_call_s(wsj1_,api,verb,request,on_reply_,reinterpret_cast<void*>(onrep));
127 }
128
129 void wsj1::on_reply_(void *closure, struct afb_wsj1_msg *msg) {
130         std::function<void(const char*)> *onreply = reinterpret_cast<std::function<void(const char*)>*>(closure);
131         (*onreply)(afb_wsj1_msg_object_s(msg));
132         afb_wsj1_msg_unref(msg);
133         delete onreply;
134 }
135
136 sd_event *wsj1::eloop_() {
137         static sd_event *el = nullptr;
138         static std::mutex lock;
139         std::lock_guard<std::mutex> guard(lock);
140         if (!el) {
141                 if (sd_event_new(&el) == 0) {
142                     std::thread t(sd_event_loop, el);
143                     t.detach();
144                 }
145         }
146         return el;
147 }
148 void wsj1::on_hangup_(struct afb_wsj1 *wsj1) {
149
150 };
151 void wsj1::on_call_(const char *api, const char *verb, struct afb_wsj1_msg *msg) {
152
153 };
154
155
156 }
157 // faire un test qui verifi le fonctionnement de la classe,
158 // il faut faire bien attention avec la fonction call qui doit se terminer      avan tle programme, un get char ou un cin 
159 // ecrire lma fonction on reply qui affichhe le resultat de la requete à l'ecran