Statistics
| Branch: | Revision:

janus-gateway / transports / janus_http.c @ 892e86ab

History | View | Annotate | Download (74.1 KB)

1
/*! \file   janus_http.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU General Public License v3
4
 * \brief  Janus RESTs transport plugin
5
 * \details  This is an implementation of a RESTs transport for the
6
 * Janus API, using the libmicrohttpd library (http://www.gnu.org/software/libmicrohttpd/).
7
 * This module allows browsers to make use of HTTP to talk to the gateway.
8
 * Since the gateway may be deployed on a different domain than the web
9
 * server hosting the web applications using it, the gateway automatically
10
 * handles OPTIONS request to comply with the CORS specification.
11
 * POST requests can be used to ask for the management of a session with
12
 * the gateway, to attach to a plugin, to send messages to the plugin
13
 * itself and so on. GET requests instead are used for getting events
14
 * associated to a gateway session (and as such to all its plugin handles
15
 * and the events plugins push in the session itself), using a long poll
16
 * approach. A JavaScript library (janus.js) implements all of this on
17
 * the client side automatically.
18
 * \note There's a well known bug in libmicrohttpd that may cause it to
19
 * spike to 100% of the CPU when using HTTPS on some distributions. In
20
 * case you're interested in HTTPS support, it's better to just rely on
21
 * HTTP in Janus, and put a frontend like Apache HTTPD or nginx to take
22
 * care of securing the traffic. More details are available in \ref deploy.
23
 * 
24
 * \ingroup transports
25
 * \ref transports
26
 */
27

    
28
#include "transport.h"
29

    
30
#include <arpa/inet.h>
31
#include <ifaddrs.h>
32
#include <net/if.h>
33
#include <sys/socket.h>
34
#include <sys/types.h>
35
#include <netdb.h>
36

    
37
#include <microhttpd.h>
38

    
39
#include "../debug.h"
40
#include "../apierror.h"
41
#include "../config.h"
42
#include "../mutex.h"
43
#include "../ip-utils.h"
44
#include "../utils.h"
45

    
46

    
47
/* Transport plugin information */
48
#define JANUS_REST_VERSION                        2
49
#define JANUS_REST_VERSION_STRING        "0.0.2"
50
#define JANUS_REST_DESCRIPTION                "This transport plugin adds REST (HTTP/HTTPS) support to the Janus API via libmicrohttpd."
51
#define JANUS_REST_NAME                                "JANUS REST (HTTP/HTTPS) transport plugin"
52
#define JANUS_REST_AUTHOR                        "Meetecho s.r.l."
53
#define JANUS_REST_PACKAGE                        "janus.transport.http"
54

    
55
/* Transport methods */
56
janus_transport *create(void);
57
int janus_http_init(janus_transport_callbacks *callback, const char *config_path);
58
void janus_http_destroy(void);
59
int janus_http_get_api_compatibility(void);
60
int janus_http_get_version(void);
61
const char *janus_http_get_version_string(void);
62
const char *janus_http_get_description(void);
63
const char *janus_http_get_name(void);
64
const char *janus_http_get_author(void);
65
const char *janus_http_get_package(void);
66
gboolean janus_http_is_janus_api_enabled(void);
67
gboolean janus_http_is_admin_api_enabled(void);
68
int janus_http_send_message(void *transport, void *request_id, gboolean admin, json_t *message);
69
void janus_http_session_created(void *transport, guint64 session_id);
70
void janus_http_session_over(void *transport, guint64 session_id, gboolean timeout);
71

    
72

    
73
/* Transport setup */
74
static janus_transport janus_http_transport =
75
        JANUS_TRANSPORT_INIT (
76
                .init = janus_http_init,
77
                .destroy = janus_http_destroy,
78

    
79
                .get_api_compatibility = janus_http_get_api_compatibility,
80
                .get_version = janus_http_get_version,
81
                .get_version_string = janus_http_get_version_string,
82
                .get_description = janus_http_get_description,
83
                .get_name = janus_http_get_name,
84
                .get_author = janus_http_get_author,
85
                .get_package = janus_http_get_package,
86

    
87
                .is_janus_api_enabled = janus_http_is_janus_api_enabled,
88
                .is_admin_api_enabled = janus_http_is_admin_api_enabled,
89

    
90
                .send_message = janus_http_send_message,
91
                .session_created = janus_http_session_created,
92
                .session_over = janus_http_session_over,
93
        );
94

    
95
/* Transport creator */
96
janus_transport *create(void) {
97
        JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_REST_NAME);
98
        return &janus_http_transport;
99
}
100

    
101

    
102
/* Useful stuff */
103
static gint initialized = 0, stopping = 0;
104
static janus_transport_callbacks *gateway = NULL;
105
static gboolean http_janus_api_enabled = FALSE;
106
static gboolean http_admin_api_enabled = FALSE;
107
static gboolean notify_events = TRUE;
108

    
109
/* JSON serialization options */
110
static size_t json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
111

    
112

    
113
/* Incoming HTTP message */
114
typedef struct janus_http_msg {
115
        struct MHD_Connection *connection;        /* The MHD connection this message came from */
116
        gchar *acrh;                                                /* Value of the Access-Control-Request-Headers HTTP header, if any (needed for CORS) */
117
        gchar *acrm;                                                /* Value of the Access-Control-Request-Method HTTP header, if any (needed for CORS) */
118
        gchar *contenttype;                                        /* Content-Type of the payload */
119
        gchar *payload;                                                /* Payload of the message */
120
        size_t len;                                                        /* Length of the message in octets */
121
        gint64 session_id;                                        /* Gateway-Client session identifier this message belongs to */
122
        janus_mutex wait_mutex;                                /* Mutex to wait on the response condition */
123
        janus_condition wait_cond;                        /* Response condition */
124
        gboolean got_response;                                /* Whether this message got a response from the core */
125
        json_t *response;                                        /* The response from the core */
126
} janus_http_msg;
127
static GHashTable *messages = NULL;
128
static janus_mutex messages_mutex;
129

    
130

    
131
/* Helper for long poll: HTTP events to push per session */
132
typedef struct janus_http_session {
133
        GAsyncQueue *events;        /* Events to notify for this session */
134
        gint64 destroyed;                /* Whether this session has been destroyed */
135
} janus_http_session;
136
/* We keep track of created sessions as we handle long polls */
137
const char *keepalive_id = "keepalive";
138
GHashTable *sessions = NULL;
139
GList *old_sessions = NULL;
140
GThread *sessions_watchdog = NULL;
141
janus_mutex sessions_mutex;
142

    
143

    
144
/* Callback (libmicrohttpd) invoked when a new connection is attempted on the REST API */
145
int janus_http_client_connect(void *cls, const struct sockaddr *addr, socklen_t addrlen);
146
/* Callback (libmicrohttpd) invoked when a new connection is attempted on the admin/monitor webserver */
147
int janus_http_admin_client_connect(void *cls, const struct sockaddr *addr, socklen_t addrlen);
148
/* Callback (libmicrohttpd) invoked when an HTTP message (GET, POST, OPTIONS, etc.) is available */
149
int janus_http_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr);
150
/* Callback (libmicrohttpd) invoked when an admin/monitor HTTP message (GET, POST, OPTIONS, etc.) is available */
151
int janus_http_admin_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr);
152
/* Callback (libmicrohttpd) invoked when headers of an incoming HTTP message have been parsed */
153
int janus_http_headers(void *cls, enum MHD_ValueKind kind, const char *key, const char *value);
154
/* Callback (libmicrohttpd) invoked when a request has been processed and can be freed */
155
void janus_http_request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe);
156
/* Worker to handle requests that are actually long polls */
157
int janus_http_notifier(janus_http_msg *msg, int max_events);
158
/* Helper to quickly send a success response */
159
int janus_http_return_success(janus_http_msg *msg, char *payload);
160
/* Helper to quickly send an error response */
161
int janus_http_return_error(janus_http_msg *msg, uint64_t session_id, const char *transaction, gint error, const char *format, ...) G_GNUC_PRINTF(5, 6);
162

    
163

    
164
/* MHD Web Server */
165
static struct MHD_Daemon *ws = NULL, *sws = NULL;
166
static char *ws_path = NULL;
167
static char *cert_pem_bytes = NULL, *cert_key_bytes = NULL; 
168

    
169
/* Admin/Monitor MHD Web Server */
170
static struct MHD_Daemon *admin_ws = NULL, *admin_sws = NULL;
171
static char *admin_ws_path = NULL;
172

    
173

    
174
/* REST and Admin/Monitor ACL list */
175
GList *janus_http_access_list = NULL, *janus_http_admin_access_list = NULL;
176
janus_mutex access_list_mutex;
177
static void janus_http_allow_address(const char *ip, gboolean admin) {
178
        if(ip == NULL)
179
                return;
180
        /* Is this an IP or an interface? */
181
        janus_mutex_lock(&access_list_mutex);
182
        if(!admin)
183
                janus_http_access_list = g_list_append(janus_http_access_list, (gpointer)ip);
184
        else
185
                janus_http_admin_access_list = g_list_append(janus_http_admin_access_list, (gpointer)ip);
186
        janus_mutex_unlock(&access_list_mutex);
187
}
188
static gboolean janus_http_is_allowed(const char *ip, gboolean admin) {
189
        if(ip == NULL)
190
                return FALSE;
191
        if(!admin && janus_http_access_list == NULL)
192
                return TRUE;
193
        if(admin && janus_http_admin_access_list == NULL)
194
                return TRUE;
195
        janus_mutex_lock(&access_list_mutex);
196
        GList *temp = admin ? janus_http_admin_access_list : janus_http_access_list;
197
        while(temp) {
198
                const char *allowed = (const char *)temp->data;
199
                if(allowed != NULL && strstr(ip, allowed)) {
200
                        janus_mutex_unlock(&access_list_mutex);
201
                        return TRUE;
202
                }
203
                temp = temp->next;
204
        }
205
        janus_mutex_unlock(&access_list_mutex);
206
        return FALSE;
207
}
208

    
209
/* Helper method to get the port from a struct sockaddr */
210
static uint16_t janus_http_sockaddr_to_port(struct sockaddr *address) {
211
        if(address == NULL)
212
                return 0;
213
        struct sockaddr_in *sin = NULL;
214
        struct sockaddr_in6 *sin6 = NULL;
215

    
216
        switch(address->sa_family) {
217
                case AF_INET:
218
                        sin = (struct sockaddr_in *)address;
219
                        return ntohs(sin->sin_port);
220
                case AF_INET6:
221
                        sin6 = (struct sockaddr_in6 *)address;
222
                        return ntohs(sin6->sin6_port);
223
                default:
224
                        /* Unknown family */
225
                        break;
226
        }
227
        return 0;
228
}
229

    
230
/* Random string helper (for transactions) */
231
static char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
232
static void janus_http_random_string(int length, char *buffer) {
233
        if(length > 0 && buffer) {
234
                int l = (int)(sizeof(charset)-1);
235
                int i=0;
236
                for(i=0; i<length; i++) {
237
                        int key = rand() % l;
238
                        buffer[i] = charset[key];
239
                }
240
                buffer[length-1] = '\0';
241
        }
242
}
243

    
244

    
245
/* Helper to create a MHD daemon */
246
static struct MHD_Daemon *janus_http_create_daemon(gboolean admin, char *path,
247
                const char *interface, const char *ip, int port,
248
                gint64 threads, const char *server_pem, const char *server_key) {
249
        struct MHD_Daemon *daemon = NULL;
250
        gboolean secure = server_pem && server_key;
251
        /* Any interface or IP address we need to limit ourselves to?
252
         * NOTE WELL: specifying an interface does NOT bind to all IPs associated
253
         * with that interface, but only to the first one that's detected */
254
        static struct sockaddr_in addr;
255
        struct sockaddr_in6 addr6;
256
        gboolean ipv6 = FALSE;
257
        if(ip && strstr(ip, ":"))
258
                ipv6 = TRUE;
259
        gboolean found = FALSE;
260
        if(ip) {
261
                /* Do a quick check to see if we need to bind on all addresses of a specific family */
262
                if(!strcasecmp(ip, "0.0.0.0")) {
263
                        /* Bind on all IPv4 addresses */
264
                        found = TRUE;
265
                        memset(&addr, 0, sizeof (struct sockaddr_in));
266
                        addr.sin_family = AF_INET;
267
                        addr.sin_port = htons(port);
268
                        addr.sin_addr.s_addr = INADDR_ANY;
269
                } else if(!strcasecmp(ip, "::")) {
270
                        /* Bind on all IPv6 addresses */
271
                        found = TRUE;
272
                        memset(&addr6, 0, sizeof (struct sockaddr_in6));
273
                        addr6.sin6_family = AF_INET6;
274
                        addr6.sin6_port = htons(port);
275
                        addr6.sin6_addr = in6addr_any;
276
                }
277
        }
278
        if(!found && (ip || interface)) {
279
                struct ifaddrs *ifaddr = NULL, *ifa = NULL;
280
                int family = 0, s = 0, n = 0;
281
                char host[NI_MAXHOST];
282
                if(getifaddrs(&ifaddr) == -1) {
283
                        JANUS_LOG(LOG_ERR, "Error getting list of interfaces to bind %s API %s webserver...\n",
284
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
285
                        return NULL;
286
                } else {
287
                        for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
288
                                family = ifa->ifa_addr->sa_family;
289
                                if(interface && strcasecmp(ifa->ifa_name, interface))
290
                                        continue;
291
                                if(ifa->ifa_addr == NULL)
292
                                        continue;
293
                                /* Skip interfaces which are not up and running */
294
                                if(!((ifa->ifa_flags & IFF_UP) && (ifa->ifa_flags & IFF_RUNNING)))
295
                                        continue;
296
                                /* FIXME When being explicit about the interface only, we only bind IPv4 for now:
297
                                 * specifying or adding a precise IPv6 address gets you an IPv6 binding instead */
298
                                if(!ipv6 && family == AF_INET) {
299
                                        s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
300
                                        if(s != 0) {
301
                                                JANUS_LOG(LOG_ERR, "Error doing a getnameinfo() to bind %s API %s webserver to '%s'...\n",
302
                                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP", ip ? ip : interface);
303
                                                return NULL;
304
                                        }
305
                                        if(ip && strcmp(host, ip))
306
                                                continue;
307
                                        found = TRUE;
308
                                        break;
309
                                } else if(ipv6 && family == AF_INET6) {
310
                                        s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
311
                                        if(s != 0) {
312
                                                JANUS_LOG(LOG_ERR, "Error doing a getnameinfo() to bind %s API %s webserver to '%s'...\n",
313
                                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP", ip ? ip : interface);
314
                                                return NULL;
315
                                        }
316
                                        if(ip && strcmp(host, ip))
317
                                                continue;
318
                                        found = TRUE;
319
                                        break;
320
                                }
321
                        }
322
                        freeifaddrs(ifaddr);
323
                }
324
                if(!found) {
325
                        JANUS_LOG(LOG_ERR, "Error binding to %s '%s' for %s API %s webserver...\n",
326
                                ip ? "IP" : "interface", ip ? ip : interface,
327
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
328
                        return NULL;
329
                }
330
                JANUS_LOG(LOG_VERB, "Going to bind the %s API %s webserver to %s (asked for %s)\n",
331
                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP", host, ip ? ip : interface);
332
                if(!ipv6) {
333
                        memset(&addr, 0, sizeof (struct sockaddr_in));
334
                        addr.sin_family = AF_INET;
335
                        addr.sin_port = htons(port);
336
                        int res = inet_pton(AF_INET, host, &addr.sin_addr);
337
                        if(res != 1) {
338
                                JANUS_LOG(LOG_ERR, "Failed to convert address '%s' (%d)\n", host, res);
339
                                return NULL;
340
                        }
341
                } else {
342
                        memset(&addr6, 0, sizeof (struct sockaddr_in6));
343
                        addr6.sin6_family = AF_INET6;
344
                        addr6.sin6_port = htons(port);
345
                        int res = inet_pton(AF_INET6, host, &addr6.sin6_addr);
346
                        if(res != 1) {
347
                                JANUS_LOG(LOG_ERR, "Failed to convert address '%s' (%d)\n", host, res);
348
                                return NULL;
349
                        }
350
                }
351
        }
352

    
353
        if(!secure) {
354
                /* HTTP web server */
355
                if(threads == 0) {
356
                        JANUS_LOG(LOG_VERB, "Using a thread per connection for the %s API %s webserver\n",
357
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
358
                        if(!interface && !ip) {
359
                                JANUS_LOG(LOG_VERB, "Binding to all interfaces for the %s API %s webserver\n",
360
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
361
                                /* Bind to all interfaces */
362
                                daemon = MHD_start_daemon(
363
#if MHD_VERSION >= 0x00095208
364
                                        MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_AUTO | MHD_USE_DUAL_STACK,
365
#else
366
                                        MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_POLL | MHD_USE_DUAL_STACK,
367
#endif
368
                                        port,
369
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
370
                                        NULL,
371
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
372
                                        path,
373
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
374
                                        MHD_OPTION_END);
375
                        } else {
376
                                /* Bind to the interface that was specified */
377
                                JANUS_LOG(LOG_VERB, "Binding to %s '%s' for the %s API %s webserver\n",
378
                                        ip ? "IP" : "interface", ip ? ip : interface,
379
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
380
                                daemon = MHD_start_daemon(
381
#if MHD_VERSION >= 0x00095208
382
                                        MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_AUTO | (ipv6 ? MHD_USE_IPv6 : 0),
383
#else
384
                                        MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_POLL | (ipv6 ? MHD_USE_IPv6 : 0),
385
#endif
386
                                        port,
387
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
388
                                        NULL,
389
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
390
                                        path,
391
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
392
                                        MHD_OPTION_SOCK_ADDR, ipv6 ? (struct sockaddr *)&addr6 : (struct sockaddr *)&addr,
393
                                        MHD_OPTION_END);
394
                        }
395
                } else {
396
                        JANUS_LOG(LOG_VERB, "Using a thread pool of size %"SCNi64" the %s API %s webserver\n", threads,
397
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
398
                        if(!interface && !ip) {
399
                                /* Bind to all interfaces */
400
                                JANUS_LOG(LOG_VERB, "Binding to all interfaces for the %s API %s webserver\n",
401
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
402
                                daemon = MHD_start_daemon(
403
                                        MHD_USE_SELECT_INTERNALLY | MHD_USE_DUAL_STACK,
404
                                        port,
405
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
406
                                        NULL,
407
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
408
                                        path,
409
                                        MHD_OPTION_THREAD_POOL_SIZE, threads,
410
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
411
                                        MHD_OPTION_END);
412
                        } else {
413
                                /* Bind to the interface that was specified */
414
                                JANUS_LOG(LOG_VERB, "Binding to %s '%s' for the %s API %s webserver\n",
415
                                        ip ? "IP" : "interface", ip ? ip : interface,
416
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
417
                                daemon = MHD_start_daemon(
418
                                        MHD_USE_SELECT_INTERNALLY | (ipv6 ? MHD_USE_IPv6 : 0),
419
                                        port,
420
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
421
                                        NULL,
422
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
423
                                        path,
424
                                        MHD_OPTION_THREAD_POOL_SIZE, threads,
425
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
426
                                        MHD_OPTION_SOCK_ADDR, ipv6 ? (struct sockaddr *)&addr6 : (struct sockaddr *)&addr,
427
                                        MHD_OPTION_END);
428
                        }
429
                }
430
        } else {
431
                /* HTTPS web server, read certificate and key */
432
                g_file_get_contents(server_pem, &cert_pem_bytes, NULL, NULL);
433
                g_file_get_contents(server_key, &cert_key_bytes, NULL, NULL);
434

    
435
                /* Start webserver */
436
                if(threads == 0) {
437
                        JANUS_LOG(LOG_VERB, "Using a thread per connection for the %s API %s webserver\n",
438
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
439
                        if(!interface && !ip) {
440
                                /* Bind to all interfaces */
441
                                JANUS_LOG(LOG_VERB, "Binding to all interfaces for the %s API %s webserver\n",
442
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
443
                                daemon = MHD_start_daemon(
444
#if MHD_VERSION >= 0x00095208
445
                                        MHD_USE_SSL | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_AUTO | MHD_USE_DUAL_STACK,
446
#else
447
                                        MHD_USE_SSL | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_POLL | MHD_USE_DUAL_STACK,
448
#endif
449
                                        port,
450
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
451
                                        NULL,
452
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
453
                                        path,
454
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
455
                                        MHD_OPTION_HTTPS_MEM_CERT, cert_pem_bytes,
456
                                        MHD_OPTION_HTTPS_MEM_KEY, cert_key_bytes,
457
                                        MHD_OPTION_END);
458
                        } else {
459
                                /* Bind to the interface that was specified */
460
                                JANUS_LOG(LOG_VERB, "Binding to %s '%s' for the %s API %s webserver\n",
461
                                        ip ? "IP" : "interface", ip ? ip : interface,
462
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
463
                                daemon = MHD_start_daemon(
464
#if MHD_VERSION >= 0x00095208
465
                                        MHD_USE_SSL | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_AUTO | (ipv6 ? MHD_USE_IPv6 : 0),
466
#else
467
                                        MHD_USE_SSL | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_POLL | (ipv6 ? MHD_USE_IPv6 : 0),
468
#endif
469
                                        port,
470
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
471
                                        NULL,
472
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
473
                                        path,
474
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
475
                                        MHD_OPTION_HTTPS_MEM_CERT, cert_pem_bytes,
476
                                        MHD_OPTION_HTTPS_MEM_KEY, cert_key_bytes,
477
                                        MHD_OPTION_SOCK_ADDR, ipv6 ? (struct sockaddr *)&addr6 : (struct sockaddr *)&addr,
478
                                        MHD_OPTION_END);
479
                        }
480
                } else {
481
                        JANUS_LOG(LOG_VERB, "Using a thread pool of size %"SCNi64" the %s API %s webserver\n", threads,
482
                                admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
483
                        if(!interface && !ip) {
484
                                /* Bind to all interfaces */
485
                                JANUS_LOG(LOG_VERB, "Binding to all interfaces for the %s API %s webserver\n",
486
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
487
                                daemon = MHD_start_daemon(
488
                                        MHD_USE_SSL | MHD_USE_SELECT_INTERNALLY | MHD_USE_DUAL_STACK,
489
                                        port,
490
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
491
                                        NULL,
492
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
493
                                        path,
494
                                        MHD_OPTION_THREAD_POOL_SIZE, threads,
495
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
496
                                        MHD_OPTION_HTTPS_MEM_CERT, cert_pem_bytes,
497
                                        MHD_OPTION_HTTPS_MEM_KEY, cert_key_bytes,
498
                                        MHD_OPTION_END);
499
                        } else {
500
                                /* Bind to the interface that was specified */
501
                                JANUS_LOG(LOG_VERB, "Binding to %s '%s' for the %s API %s webserver\n",
502
                                        ip ? "IP" : "interface", ip ? ip : interface,
503
                                        admin ? "Admin" : "Janus", secure ? "HTTPS" : "HTTP");
504
                                daemon = MHD_start_daemon(
505
                                        MHD_USE_SSL | MHD_USE_SELECT_INTERNALLY | (ipv6 ? MHD_USE_IPv6 : 0),
506
                                        port,
507
                                        admin ? janus_http_admin_client_connect : janus_http_client_connect,
508
                                        NULL,
509
                                        admin ? &janus_http_admin_handler : &janus_http_handler,
510
                                        path,
511
                                        MHD_OPTION_THREAD_POOL_SIZE, threads,
512
                                        MHD_OPTION_NOTIFY_COMPLETED, &janus_http_request_completed, NULL,
513
                                        MHD_OPTION_HTTPS_MEM_CERT, cert_pem_bytes,
514
                                        MHD_OPTION_HTTPS_MEM_KEY, cert_key_bytes,
515
                                        MHD_OPTION_SOCK_ADDR, ipv6 ? (struct sockaddr *)&addr6 : (struct sockaddr *)&addr,
516
                                        MHD_OPTION_END);
517
                        }
518
                }
519
        }
520
        return daemon;
521
}
522

    
523

    
524
/* HTTP/Janus sessions watchdog/garbage collector (sort of) */
525
static void *janus_http_sessions_watchdog(void *data) {
526
        JANUS_LOG(LOG_INFO, "HTTP/Janus sessions watchdog started\n");
527
        gint64 now = 0;
528
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
529
                janus_mutex_lock(&sessions_mutex);
530
                /* Iterate on all the sessions */
531
                now = janus_get_monotonic_time();
532
                if(old_sessions != NULL) {
533
                        GList *sl = old_sessions;
534
                        JANUS_LOG(LOG_HUGE, "Checking %d old HTTP/Janus sessions sessions...\n", g_list_length(old_sessions));
535
                        while(sl) {
536
                                janus_http_session *session = (janus_http_session *)sl->data;
537
                                if(!session) {
538
                                        sl = sl->next;
539
                                        continue;
540
                                }
541
                                if(now-session->destroyed >= G_USEC_PER_SEC) {
542
                                        /* We're lazy and actually get rid of the stuff only after a few seconds */
543
                                        JANUS_LOG(LOG_VERB, "Freeing old HTTP/Janus session\n");
544
                                        GList *rm = sl->next;
545
                                        old_sessions = g_list_delete_link(old_sessions, sl);
546
                                        sl = rm;
547
                                        /* Remove all events */
548
                                        json_t *event = NULL;
549
                                        while((event = g_async_queue_try_pop(session->events)) != NULL)
550
                                                json_decref(event);
551
                                        g_async_queue_unref(session->events);
552
                                        g_free(session);
553
                                        continue;
554
                                }
555
                                sl = sl->next;
556
                        }
557
                }
558
                janus_mutex_unlock(&sessions_mutex);
559
                g_usleep(500000);
560
        }
561
        JANUS_LOG(LOG_INFO, "HTTP/Janus sessions watchdog stopped\n");
562
        return NULL;
563
}
564

    
565

    
566
/* Transport implementation */
567
int janus_http_init(janus_transport_callbacks *callback, const char *config_path) {
568
        if(g_atomic_int_get(&stopping)) {
569
                /* Still stopping from before */
570
                return -1;
571
        }
572
        if(callback == NULL || config_path == NULL) {
573
                /* Invalid arguments */
574
                return -1;
575
        }
576

    
577
#if MHD_VERSION >= 0x00095208
578
        JANUS_LOG(LOG_VERB, "The installed libmicrohttpd version supports MHD_USE_AUTO\n");
579
#endif
580

    
581
        /* This is the callback we'll need to invoke to contact the gateway */
582
        gateway = callback;
583

    
584
        /* Read configuration */
585
        char filename[255];
586
        g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_REST_PACKAGE);
587
        JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
588
        janus_config *config = janus_config_parse(filename);
589
        if(config != NULL) {
590
                janus_config_print(config);
591

    
592
                /* Handle configuration */
593
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json");
594
                if(item && item->value) {
595
                        /* Check how we need to format/serialize the JSON output */
596
                        if(!strcasecmp(item->value, "indented")) {
597
                                /* Default: indented, we use three spaces for that */
598
                                json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
599
                        } else if(!strcasecmp(item->value, "plain")) {
600
                                /* Not indented and no new lines, but still readable */
601
                                json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER;
602
                        } else if(!strcasecmp(item->value, "compact")) {
603
                                /* Compact, so no spaces between separators */
604
                                json_format = JSON_COMPACT | JSON_PRESERVE_ORDER;
605
                        } else {
606
                                JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value);
607
                                json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
608
                        }
609
                }
610

    
611
                /* Check if we need to send events to handlers */
612
                janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
613
                if(events != NULL && events->value != NULL)
614
                        notify_events = janus_is_true(events->value);
615
                if(!notify_events && callback->events_is_enabled()) {
616
                        JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_REST_NAME);
617
                }
618

    
619
                /* Check the base paths */
620
                item = janus_config_get_item_drilldown(config, "general", "base_path");
621
                if(item && item->value) {
622
                        if(item->value[0] != '/') {
623
                                JANUS_LOG(LOG_FATAL, "Invalid base path %s (it should start with a /, e.g., /janus\n", item->value);
624
                                return -1;
625
                        }
626
                        ws_path = g_strdup(item->value);
627
                        if(strlen(ws_path) > 1 && ws_path[strlen(ws_path)-1] == '/') {
628
                                /* Remove the trailing slash, it makes things harder when we parse requests later */
629
                                ws_path[strlen(ws_path)-1] = '\0';
630
                        }
631
                } else {
632
                        ws_path = g_strdup("/janus");
633
                }
634
                /* Do the same for the admin/monitor interface */
635
                item = janus_config_get_item_drilldown(config, "admin", "admin_base_path");
636
                if(item && item->value) {
637
                        if(item->value[0] != '/') {
638
                                JANUS_LOG(LOG_FATAL, "Invalid admin/monitor base path %s (it should start with a /, e.g., /admin\n", item->value);
639
                                return -1;
640
                        }
641
                        admin_ws_path = g_strdup(item->value);
642
                        if(strlen(admin_ws_path) > 1 && admin_ws_path[strlen(admin_ws_path)-1] == '/') {
643
                                /* Remove the trailing slash, it makes things harder when we parse requests later */
644
                                admin_ws_path[strlen(admin_ws_path)-1] = '\0';
645
                        }
646
                } else {
647
                        admin_ws_path = g_strdup("/admin");
648
                }
649

    
650
                /* Any ACL for either the Janus or Admin API? */
651
                item = janus_config_get_item_drilldown(config, "general", "acl");
652
                if(item && item->value) {
653
                        gchar **list = g_strsplit(item->value, ",", -1);
654
                        gchar *index = list[0];
655
                        if(index != NULL) {
656
                                int i=0;
657
                                while(index != NULL) {
658
                                        if(strlen(index) > 0) {
659
                                                JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index);
660
                                                janus_http_allow_address(g_strdup(index), FALSE);
661
                                        }
662
                                        i++;
663
                                        index = list[i];
664
                                }
665
                        }
666
                        g_strfreev(list);
667
                        list = NULL;
668
                }
669
                item = janus_config_get_item_drilldown(config, "admin", "admin_acl");
670
                if(item && item->value) {
671
                        gchar **list = g_strsplit(item->value, ",", -1);
672
                        gchar *index = list[0];
673
                        if(index != NULL) {
674
                                int i=0;
675
                                while(index != NULL) {
676
                                        if(strlen(index) > 0) {
677
                                                JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index);
678
                                                janus_http_allow_address(g_strdup(index), TRUE);
679
                                        }
680
                                        i++;
681
                                        index = list[i];
682
                                }
683
                        }
684
                        g_strfreev(list);
685
                        list = NULL;
686
                }
687

    
688
                /* Start with the Janus API web server now */
689
                gint64 threads = 0;
690
                item = janus_config_get_item_drilldown(config, "general", "threads");
691
                if(item && item->value) {
692
                        if(!strcasecmp(item->value, "unlimited")) {
693
                                /* No limit on threads, use a thread per connection */
694
                                threads = 0;
695
                        } else {
696
                                /* Use a thread pool */
697
                                threads = atoll(item->value);
698
                                if(threads == 0) {
699
                                        JANUS_LOG(LOG_WARN, "Chose '0' as size for the thread pool, which is equivalent to 'unlimited'\n");
700
                                } else if(threads < 0) {
701
                                        JANUS_LOG(LOG_WARN, "Invalid value '%"SCNi64"' as size for the thread pool, falling back to to 'unlimited'\n", threads);
702
                                        threads = 0;
703
                                }
704
                        }
705
                }
706
                item = janus_config_get_item_drilldown(config, "general", "http");
707
                if(!item || !item->value || !janus_is_true(item->value)) {
708
                        JANUS_LOG(LOG_WARN, "HTTP webserver disabled\n");
709
                } else {
710
                        int wsport = 8088;
711
                        item = janus_config_get_item_drilldown(config, "general", "port");
712
                        if(item && item->value)
713
                                wsport = atoi(item->value);
714
                        const char *interface = NULL;
715
                        item = janus_config_get_item_drilldown(config, "general", "interface");
716
                        if(item && item->value)
717
                                interface = item->value;
718
                        const char *ip = NULL;
719
                        item = janus_config_get_item_drilldown(config, "general", "ip");
720
                        if(item && item->value)
721
                                ip = item->value;
722
                        ws = janus_http_create_daemon(FALSE, ws_path, interface, ip, wsport, threads, NULL, NULL);
723
                        if(ws == NULL) {
724
                                JANUS_LOG(LOG_FATAL, "Couldn't start webserver on port %d...\n", wsport);
725
                        } else {
726
                                JANUS_LOG(LOG_INFO, "HTTP webserver started (port %d, %s path listener)...\n", wsport, ws_path);
727
                        }
728
                }
729
                /* Do we also have to provide an HTTPS one? */
730
                char *server_pem = NULL;
731
                item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
732
                if(item && item->value)
733
                        server_pem = (char *)item->value;
734
                char *server_key = NULL;
735
                item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
736
                if(item && item->value)
737
                        server_key = (char *)item->value;
738
                if(server_key)
739
                        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
740
                item = janus_config_get_item_drilldown(config, "general", "https");
741
                if(!item || !item->value || !janus_is_true(item->value)) {
742
                        JANUS_LOG(LOG_WARN, "HTTPS webserver disabled\n");
743
                } else {
744
                        if(!server_key || !server_pem) {
745
                                JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
746
                        } else {
747
                                int swsport = 8089;
748
                                item = janus_config_get_item_drilldown(config, "general", "secure_port");
749
                                if(item && item->value)
750
                                        swsport = atoi(item->value);
751
                                const char *interface = NULL;
752
                                item = janus_config_get_item_drilldown(config, "general", "secure_interface");
753
                                if(item && item->value)
754
                                        interface = item->value;
755
                                const char *ip = NULL;
756
                                item = janus_config_get_item_drilldown(config, "general", "secure_ip");
757
                                if(item && item->value)
758
                                        ip = item->value;
759
                                sws = janus_http_create_daemon(FALSE, ws_path, interface, ip, swsport, threads, server_pem, server_key);
760
                                if(sws == NULL) {
761
                                        JANUS_LOG(LOG_FATAL, "Couldn't start secure webserver on port %d...\n", swsport);
762
                                } else {
763
                                        JANUS_LOG(LOG_INFO, "HTTPS webserver started (port %d, %s path listener)...\n", swsport, ws_path);
764
                                }
765
                        }
766
                }
767
                /* Admin/monitor time: start web server, if enabled */
768
                threads = 0;
769
                item = janus_config_get_item_drilldown(config, "admin", "admin_threads");
770
                if(item && item->value) {
771
                        if(!strcasecmp(item->value, "unlimited")) {
772
                                /* No limit on threads, use a thread per connection */
773
                                threads = 0;
774
                        } else {
775
                                /* Use a thread pool */
776
                                threads = atoll(item->value);
777
                                if(threads == 0) {
778
                                        JANUS_LOG(LOG_WARN, "Chose '0' as size for the admin/monitor thread pool, which is equivalent to 'unlimited'\n");
779
                                } else if(threads < 0) {
780
                                        JANUS_LOG(LOG_WARN, "Invalid value '%"SCNi64"' as size for the admin/monitor thread pool, falling back to to 'unlimited'\n", threads);
781
                                        threads = 0;
782
                                }
783
                        }
784
                }
785
                item = janus_config_get_item_drilldown(config, "admin", "admin_http");
786
                if(!item || !item->value || !janus_is_true(item->value)) {
787
                        JANUS_LOG(LOG_WARN, "Admin/monitor HTTP webserver disabled\n");
788
                } else {
789
                        int wsport = 7088;
790
                        item = janus_config_get_item_drilldown(config, "admin", "admin_port");
791
                        if(item && item->value)
792
                                wsport = atoi(item->value);
793
                        const char *interface = NULL;
794
                        item = janus_config_get_item_drilldown(config, "admin", "admin_interface");
795
                        if(item && item->value)
796
                                interface = item->value;
797
                        const char *ip = NULL;
798
                        item = janus_config_get_item_drilldown(config, "admin", "admin_ip");
799
                        if(item && item->value)
800
                                ip = item->value;
801
                        admin_ws = janus_http_create_daemon(TRUE, admin_ws_path, interface, ip, wsport, threads, NULL, NULL);
802
                        if(admin_ws == NULL) {
803
                                JANUS_LOG(LOG_FATAL, "Couldn't start admin/monitor webserver on port %d...\n", wsport);
804
                        } else {
805
                                JANUS_LOG(LOG_INFO, "Admin/monitor HTTP webserver started (port %d, %s path listener)...\n", wsport, admin_ws_path);
806
                        }
807
                }
808
                /* Do we also have to provide an HTTPS one? */
809
                item = janus_config_get_item_drilldown(config, "admin", "admin_https");
810
                if(!item || !item->value || !janus_is_true(item->value)) {
811
                        JANUS_LOG(LOG_WARN, "Admin/monitor HTTPS webserver disabled\n");
812
                } else {
813
                        if(!server_key) {
814
                                JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
815
                        } else {
816
                                int swsport = 7889;
817
                                item = janus_config_get_item_drilldown(config, "admin", "admin_secure_port");
818
                                if(item && item->value)
819
                                        swsport = atoi(item->value);
820
                                const char *interface = NULL;
821
                                item = janus_config_get_item_drilldown(config, "admin", "admin_secure_interface");
822
                                if(item && item->value)
823
                                        interface = item->value;
824
                                const char *ip = NULL;
825
                                item = janus_config_get_item_drilldown(config, "admin", "admin_secure_ip");
826
                                if(item && item->value)
827
                                        ip = item->value;
828
                                admin_sws = janus_http_create_daemon(TRUE, admin_ws_path, interface, ip, swsport, threads, server_pem, server_key);
829
                                if(admin_sws == NULL) {
830
                                        JANUS_LOG(LOG_FATAL, "Couldn't start secure admin/monitor webserver on port %d...\n", swsport);
831
                                } else {
832
                                        JANUS_LOG(LOG_INFO, "Admin/monitor HTTPS webserver started (port %d, %s path listener)...\n", swsport, admin_ws_path);
833
                                }
834
                        }
835
                }
836
        }
837
        janus_config_destroy(config);
838
        config = NULL;
839
        if(!ws && !sws && !admin_ws && !admin_sws) {
840
                JANUS_LOG(LOG_WARN, "No HTTP/HTTPS server started, giving up...\n");
841
                return -1;        /* No point in keeping the plugin loaded */
842
        }
843
        http_janus_api_enabled = ws || sws;
844
        http_admin_api_enabled = admin_ws || admin_sws;
845

    
846
        messages = g_hash_table_new(NULL, NULL);
847
        janus_mutex_init(&messages_mutex);
848
        sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
849
        old_sessions = NULL;
850
        janus_mutex_init(&sessions_mutex);
851
        GError *error = NULL;
852
        /* Start the HTTP/Janus sessions watchdog */
853
        sessions_watchdog = g_thread_try_new("http watchdog", &janus_http_sessions_watchdog, NULL, &error);
854
        if(error != NULL) {
855
                g_atomic_int_set(&initialized, 0);
856
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the HTTP/Janus sessions watchdog thread...\n", error->code, error->message ? error->message : "??");
857
                return -1;
858
        }
859
        
860
        /* Done */
861
        g_atomic_int_set(&initialized, 1);
862
        JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_REST_NAME);
863
        return 0;
864
}
865

    
866
void janus_http_destroy(void) {
867
        if(!g_atomic_int_get(&initialized))
868
                return;
869
        g_atomic_int_set(&stopping, 1);
870

    
871
        JANUS_LOG(LOG_INFO, "Stopping webserver(s)...\n");
872
        if(ws)
873
                MHD_stop_daemon(ws);
874
        ws = NULL;
875
        if(sws)
876
                MHD_stop_daemon(sws);
877
        sws = NULL;
878
        if(admin_ws)
879
                MHD_stop_daemon(admin_ws);
880
        admin_ws = NULL;
881
        if(admin_sws)
882
                MHD_stop_daemon(admin_sws);
883
        admin_sws = NULL;
884
        if(cert_pem_bytes != NULL)
885
                g_free((gpointer)cert_pem_bytes);
886
        cert_pem_bytes = NULL;
887
        if(cert_key_bytes != NULL)
888
                g_free((gpointer)cert_key_bytes);
889
        cert_key_bytes = NULL;
890

    
891
        g_hash_table_destroy(messages);
892
        if(sessions_watchdog != NULL) {
893
                g_thread_join(sessions_watchdog);
894
                sessions_watchdog = NULL;
895
        }
896

    
897
        g_atomic_int_set(&initialized, 0);
898
        g_atomic_int_set(&stopping, 0);
899
        JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_REST_NAME);
900
}
901

    
902
int janus_http_get_api_compatibility(void) {
903
        /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
904
        return JANUS_TRANSPORT_API_VERSION;
905
}
906

    
907
int janus_http_get_version(void) {
908
        return JANUS_REST_VERSION;
909
}
910

    
911
const char *janus_http_get_version_string(void) {
912
        return JANUS_REST_VERSION_STRING;
913
}
914

    
915
const char *janus_http_get_description(void) {
916
        return JANUS_REST_DESCRIPTION;
917
}
918

    
919
const char *janus_http_get_name(void) {
920
        return JANUS_REST_NAME;
921
}
922

    
923
const char *janus_http_get_author(void) {
924
        return JANUS_REST_AUTHOR;
925
}
926

    
927
const char *janus_http_get_package(void) {
928
        return JANUS_REST_PACKAGE;
929
}
930

    
931
gboolean janus_http_is_janus_api_enabled(void) {
932
        return http_janus_api_enabled;
933
}
934

    
935
gboolean janus_http_is_admin_api_enabled(void) {
936
        return http_admin_api_enabled;
937
}
938

    
939
int janus_http_send_message(void *transport, void *request_id, gboolean admin, json_t *message) {
940
        JANUS_LOG(LOG_HUGE, "Got a %s API %s to send (%p)\n", admin ? "admin" : "Janus", request_id ? "response" : "event", transport);
941
        if(message == NULL) {
942
                JANUS_LOG(LOG_ERR, "No message...\n");
943
                return -1;
944
        }
945
        if(request_id == NULL) {
946
                /* This is an event, add to the session queue */
947
                json_t *s = json_object_get(message, "session_id");
948
                if(!s || !json_is_integer(s)) {
949
                        JANUS_LOG(LOG_ERR, "Can't notify event, no session_id...\n");
950
                        json_decref(message);
951
                        return -1;
952
                }
953
                guint64 session_id = json_integer_value(s);
954
                janus_mutex_lock(&sessions_mutex);
955
                janus_http_session *session = g_hash_table_lookup(sessions, &session_id);
956
                if(session == NULL || session->destroyed) {
957
                        JANUS_LOG(LOG_ERR, "Can't notify event, no session object...\n");
958
                        janus_mutex_unlock(&sessions_mutex);
959
                        json_decref(message);
960
                        return -1;
961
                }
962
                g_async_queue_push(session->events, message);
963
                janus_mutex_unlock(&sessions_mutex);
964
        } else {
965
                if(request_id == keepalive_id) {
966
                        /* It's a response from our fake long-poll related keepalive, ignore */
967
                        json_decref(message);
968
                        return 0;
969
                }
970
                /* This is a response, we need a valid transport instance */
971
                if(transport == NULL) {
972
                        JANUS_LOG(LOG_ERR, "Invalid HTTP instance...\n");
973
                        json_decref(message);
974
                        return -1;
975
                }
976
                /* We have a response */
977
                janus_http_msg *msg = (janus_http_msg *)transport;
978
                janus_mutex_lock(&messages_mutex);
979
                if(g_hash_table_lookup(messages, msg) == NULL) {
980
                        janus_mutex_unlock(&messages_mutex);
981
                        JANUS_LOG(LOG_ERR, "Invalid HTTP connection...\n");
982
                        json_decref(message);
983
                        return -1;
984
                }
985
                janus_mutex_unlock(&messages_mutex);
986
                if(!msg->connection) {
987
                        JANUS_LOG(LOG_ERR, "Invalid HTTP connection...\n");
988
                        json_decref(message);
989
                        return -1;
990
                }
991
                janus_mutex_lock(&msg->wait_mutex);
992
                msg->response = message;
993
                msg->got_response = TRUE;
994
                janus_condition_signal(&msg->wait_cond);
995
                janus_mutex_unlock(&msg->wait_mutex);
996
        }
997
        return 0;
998
}
999

    
1000
void janus_http_session_created(void *transport, guint64 session_id) {
1001
        if(transport == NULL)
1002
                return;
1003
        JANUS_LOG(LOG_VERB, "Session created (%"SCNu64"), create a queue for the long poll\n", session_id);
1004
        /* Create a queue of events for this session */
1005
        janus_mutex_lock(&sessions_mutex);
1006
        if(g_hash_table_lookup(sessions, &session_id) != NULL) {
1007
                JANUS_LOG(LOG_WARN, "Ignoring created session, apparently we're already handling it?\n");
1008
                janus_mutex_unlock(&sessions_mutex);
1009
                return;
1010
        }
1011
        janus_http_session *session = g_malloc0(sizeof(janus_http_session));
1012
        session->events = g_async_queue_new();
1013
        session->destroyed = 0;
1014
        g_hash_table_insert(sessions, janus_uint64_dup(session_id), session);
1015
        janus_mutex_unlock(&sessions_mutex);
1016
}
1017

    
1018
void janus_http_session_over(void *transport, guint64 session_id, gboolean timeout) {
1019
        if(transport == NULL)
1020
                return;
1021
        JANUS_LOG(LOG_VERB, "Session %s (%"SCNu64"), getting rid of the queue for the long poll\n",
1022
                timeout ? "has timed out" : "is over", session_id);
1023
        /* Get rid of the session's queue of events */
1024
        janus_mutex_lock(&sessions_mutex);
1025
        janus_http_session *session = g_hash_table_lookup(sessions, &session_id);
1026
        if(session == NULL || session->destroyed) {
1027
                /* Nothing to do */
1028
                janus_mutex_unlock(&sessions_mutex);
1029
                return;
1030
        }
1031
        g_hash_table_remove(sessions, &session_id);
1032
        /* We leave it to the watchdog to remove the session */
1033
        session->destroyed = janus_get_monotonic_time();
1034
        old_sessions = g_list_append(old_sessions, session);
1035
        janus_mutex_unlock(&sessions_mutex);
1036
}
1037

    
1038
/* Connection notifiers */
1039
int janus_http_client_connect(void *cls, const struct sockaddr *addr, socklen_t addrlen) {
1040
        janus_network_address naddr;
1041
        janus_network_address_string_buffer naddr_buf;
1042
        if(janus_network_address_from_sockaddr((struct sockaddr *)addr, &naddr) != 0 ||
1043
                        janus_network_address_to_string_buffer(&naddr, &naddr_buf) != 0) {
1044
                JANUS_LOG(LOG_WARN, "Error trying to resolve connection address...\n");
1045
                /* Should this be MHD_NO instead? */
1046
                return MHD_YES;
1047
        }
1048
        const char *ip = janus_network_address_string_from_buffer(&naddr_buf);
1049
        JANUS_LOG(LOG_HUGE, "New connection on REST API: %s\n", ip);
1050
        /* Any access limitation based on this IP address? */
1051
        if(!janus_http_is_allowed(ip, FALSE)) {
1052
                JANUS_LOG(LOG_ERR, "IP %s is unauthorized to connect to the Janus API interface\n", ip);
1053
                return MHD_NO;
1054
        }
1055
        return MHD_YES;
1056
}
1057

    
1058
int janus_http_admin_client_connect(void *cls, const struct sockaddr *addr, socklen_t addrlen) {
1059
        janus_network_address naddr;
1060
        janus_network_address_string_buffer naddr_buf;
1061
        if(janus_network_address_from_sockaddr((struct sockaddr *)addr, &naddr) != 0 ||
1062
                        janus_network_address_to_string_buffer(&naddr, &naddr_buf) != 0) {
1063
                JANUS_LOG(LOG_WARN, "Error trying to resolve Admin connection address...\n");
1064
                /* Should this be MHD_NO instead? */
1065
                return MHD_YES;
1066
        }
1067
        const char *ip = janus_network_address_string_from_buffer(&naddr_buf);
1068
        JANUS_LOG(LOG_HUGE, "New connection on admin/monitor: %s\n", ip);
1069
        /* Any access limitation based on this IP address? */
1070
        if(!janus_http_is_allowed(ip, TRUE)) {
1071
                JANUS_LOG(LOG_ERR, "IP %s is unauthorized to connect to the admin/monitor interface\n", ip);
1072
                return MHD_NO;
1073
        }
1074
        return MHD_YES;
1075
}
1076

    
1077

    
1078
/* WebServer requests handler */
1079
int janus_http_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr)
1080
{
1081
        char *payload = NULL;
1082
        json_t *root = NULL;
1083
        struct MHD_Response *response = NULL;
1084
        int ret = MHD_NO;
1085
        gchar *session_path = NULL, *handle_path = NULL;
1086
        gchar **basepath = NULL, **path = NULL;
1087
        guint64 session_id = 0, handle_id = 0;
1088

    
1089
        /* Is this the first round? */
1090
        int firstround = 0;
1091
        janus_http_msg *msg = (janus_http_msg *)*ptr;
1092
        if (msg == NULL) {
1093
                firstround = 1;
1094
                JANUS_LOG(LOG_DBG, "Got a HTTP %s request on %s...\n", method, url);
1095
                JANUS_LOG(LOG_DBG, " ... Just parsing headers for now...\n");
1096
                msg = g_malloc0(sizeof(janus_http_msg));
1097
                if(msg == NULL) {
1098
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1099
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1100
                        MHD_destroy_response(response);
1101
                        goto done;
1102
                }
1103
                msg->connection = connection;
1104
                msg->acrh = NULL;
1105
                msg->acrm = NULL;
1106
                msg->payload = NULL;
1107
                msg->len = 0;
1108
                msg->session_id = 0;
1109
                msg->got_response = FALSE;
1110
                msg->response = NULL;
1111
                janus_mutex_init(&msg->wait_mutex);
1112
                janus_condition_init(&msg->wait_cond);
1113
                janus_mutex_lock(&messages_mutex);
1114
                g_hash_table_insert(messages, msg, msg);
1115
                janus_mutex_unlock(&messages_mutex);
1116
                *ptr = msg;
1117
                MHD_get_connection_values(connection, MHD_HEADER_KIND, &janus_http_headers, msg);
1118
                ret = MHD_YES;
1119
                /* Notify handlers about this new transport instance */
1120
                if(notify_events && gateway->events_is_enabled()) {
1121
                        json_t *info = json_object();
1122
                        json_object_set_new(info, "event", json_string("request"));
1123
                        json_object_set_new(info, "admin_api", json_false());
1124
                        const union MHD_ConnectionInfo *conninfo = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1125
                        if(conninfo != NULL) {
1126
                                janus_network_address addr;
1127
                                janus_network_address_string_buffer addr_buf;
1128
                                if(janus_network_address_from_sockaddr((struct sockaddr *)conninfo->client_addr, &addr) == 0 &&
1129
                                                janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
1130
                                        const char *ip = janus_network_address_string_from_buffer(&addr_buf);
1131
                                        json_object_set_new(info, "ip", json_string(ip));
1132
                                }
1133
                                uint16_t port = janus_http_sockaddr_to_port((struct sockaddr *)conninfo->client_addr);
1134
                                json_object_set_new(info, "port", json_integer(port));
1135
                        }
1136
                        gateway->notify_event(&janus_http_transport, msg, info);
1137
                }
1138
        } else {
1139
                JANUS_LOG(LOG_DBG, "Processing HTTP %s request on %s...\n", method, url);
1140
        }
1141
        /* Parse request */
1142
        if (strcasecmp(method, "GET") && strcasecmp(method, "POST") && strcasecmp(method, "OPTIONS")) {
1143
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_TRANSPORT_SPECIFIC, "Unsupported method %s", method);
1144
                goto done;
1145
        }
1146
        if (!strcasecmp(method, "OPTIONS")) {
1147
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1148
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1149
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1150
                if(msg->acrm)
1151
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1152
                if(msg->acrh)
1153
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1154
                ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
1155
                MHD_destroy_response(response);
1156
        }
1157
        /* Get path components */
1158
        if(strcasecmp(url, ws_path)) {
1159
                if(strlen(ws_path) > 1) {
1160
                        basepath = g_strsplit(url, ws_path, -1);
1161
                } else {
1162
                        /* The base path is the web server too itself, we process the url itself */
1163
                        basepath = g_malloc0(3);
1164
                        basepath[0] = g_strdup("/");
1165
                        basepath[1] = g_strdup(url);
1166
                }
1167
                if(basepath[0] == NULL || basepath[1] == NULL || basepath[1][0] != '/') {
1168
                        JANUS_LOG(LOG_ERR, "Invalid url %s\n", url);
1169
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1170
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1171
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1172
                        if(msg->acrm)
1173
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1174
                        if(msg->acrh)
1175
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1176
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1177
                        MHD_destroy_response(response);
1178
                }
1179
                if(firstround) {
1180
                        g_strfreev(basepath);
1181
                        return ret;
1182
                }
1183
                path = g_strsplit(basepath[1], "/", -1);
1184
                if(path == NULL || path[1] == NULL) {
1185
                        JANUS_LOG(LOG_ERR, "Invalid path %s (%s)\n", basepath[1], path[1]);
1186
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1187
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1188
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1189
                        if(msg->acrm)
1190
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1191
                        if(msg->acrh)
1192
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1193
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1194
                        MHD_destroy_response(response);
1195
                }
1196
        }
1197
        if(firstround)
1198
                return ret;
1199
        JANUS_LOG(LOG_DBG, " ... parsing request...\n");
1200
        if(path != NULL && path[1] != NULL && strlen(path[1]) > 0) {
1201
                session_path = g_strdup(path[1]);
1202
                if(session_path == NULL) {
1203
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1204
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1205
                        MHD_destroy_response(response);
1206
                        goto done;
1207
                }
1208
                JANUS_LOG(LOG_HUGE, "Session: %s\n", session_path);
1209
        }
1210
        if(session_path != NULL && path[2] != NULL && strlen(path[2]) > 0) {
1211
                handle_path = g_strdup(path[2]);
1212
                if(handle_path == NULL) {
1213
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1214
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1215
                        MHD_destroy_response(response);
1216
                        goto done;
1217
                }
1218
                JANUS_LOG(LOG_HUGE, "Handle: %s\n", handle_path);
1219
        }
1220
        if(session_path != NULL && handle_path != NULL && path[3] != NULL && strlen(path[3]) > 0) {
1221
                JANUS_LOG(LOG_ERR, "Too many components...\n");
1222
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1223
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1224
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1225
                if(msg->acrm)
1226
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1227
                if(msg->acrh)
1228
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1229
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1230
                MHD_destroy_response(response);
1231
                goto done;
1232
        }
1233
        /* Get payload, if any */
1234
        if(!strcasecmp(method, "POST")) {
1235
                JANUS_LOG(LOG_HUGE, "Processing POST data (%s) (%zu bytes)...\n", msg->contenttype, *upload_data_size);
1236
                if(*upload_data_size != 0) {
1237
                        if(msg->payload == NULL)
1238
                                msg->payload = g_malloc0(*upload_data_size+1);
1239
                        else
1240
                                msg->payload = g_realloc(msg->payload, msg->len+*upload_data_size+1);
1241
                        if(msg->payload == NULL) {
1242
                                JANUS_LOG(LOG_FATAL, "Memory error!\n");
1243
                                ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1244
                                MHD_destroy_response(response);
1245
                                goto done;
1246
                        }
1247
                        memcpy(msg->payload+msg->len, upload_data, *upload_data_size);
1248
                        msg->len += *upload_data_size;
1249
                        memset(msg->payload + msg->len, '\0', 1);
1250
                        JANUS_LOG(LOG_DBG, "  -- Data we have now (%zu bytes)\n", msg->len);
1251
                        *upload_data_size = 0;        /* Go on */
1252
                        ret = MHD_YES;
1253
                        goto done;
1254
                }
1255
                JANUS_LOG(LOG_DBG, "Done getting payload, we can answer\n");
1256
                if(msg->payload == NULL) {
1257
                        JANUS_LOG(LOG_ERR, "No payload :-(\n");
1258
                        ret = MHD_NO;
1259
                        goto done;
1260
                }
1261
                payload = msg->payload;
1262
                JANUS_LOG(LOG_HUGE, "%s\n", payload);
1263
        }
1264

    
1265
        /* Is this a generic request for info? */
1266
        if(session_path != NULL && !strcmp(session_path, "info")) {
1267
                /* The info REST endpoint, if contacted through a GET, provides information on the gateway */
1268
                if(strcasecmp(method, "GET")) {
1269
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1270
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1271
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1272
                        if(msg->acrm)
1273
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1274
                        if(msg->acrh)
1275
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1276
                        ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, response);
1277
                        MHD_destroy_response(response);
1278
                        goto done;
1279
                }
1280
                /* Turn this into a fake "info" request */
1281
                method = "POST";
1282
                char tr[12];
1283
                janus_http_random_string(12, (char *)&tr);                
1284
                root = json_object();
1285
                json_object_set_new(root, "janus", json_string("info"));
1286
                json_object_set_new(root, "transaction", json_string(tr));
1287
                goto parsingdone;
1288
        }
1289
        
1290
        /* Or maybe a long poll */
1291
        if(!strcasecmp(method, "GET") || !payload) {
1292
                session_id = session_path ? g_ascii_strtoull(session_path, NULL, 10) : 0;
1293
                if(session_id < 1) {
1294
                        JANUS_LOG(LOG_ERR, "Invalid session %s\n", session_path);
1295
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1296
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1297
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1298
                        if(msg->acrm)
1299
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1300
                        if(msg->acrh)
1301
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1302
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1303
                        MHD_destroy_response(response);
1304
                        goto done;
1305
                }
1306
                msg->session_id = session_id;
1307

    
1308
                /* Since we handle long polls ourselves, the core isn't involved (if not for providing us with events)
1309
                 * A long poll, though, can act as a keepalive, so we pass a fake one to the core to avoid undesirable timeouts */
1310

    
1311
                /* First of all, though, API secret and token based authentication may be enabled in the core, so since
1312
                 * we're bypassing it for notifications we'll have to check those ourselves */
1313
                const char *secret = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "apisecret");
1314
                const char *token = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "token");
1315
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
1316
                if(!gateway->is_api_secret_needed(&janus_http_transport) && !gateway->is_auth_token_needed(&janus_http_transport)) {
1317
                        /* Nothing to check */
1318
                        secret_authorized = TRUE;
1319
                        token_authorized = TRUE;
1320
                } else {
1321
                        if(gateway->is_api_secret_valid(&janus_http_transport, secret)) {
1322
                                /* API secret is valid */
1323
                                secret_authorized = TRUE;
1324
                        }
1325
                        if(gateway->is_auth_token_valid(&janus_http_transport, token)) {
1326
                                /* Token is valid */
1327
                                token_authorized = TRUE;
1328
                        }
1329
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
1330
                        if(!secret_authorized && !token_authorized) {
1331
                                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1332
                                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1333
                                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1334
                                if(msg->acrm)
1335
                                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1336
                                if(msg->acrh)
1337
                                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1338
                                ret = MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, response);
1339
                                MHD_destroy_response(response);
1340
                                goto done;
1341
                        }
1342
                }
1343
                /* Ok, go on with the keepalive */
1344
                char tr[12];
1345
                janus_http_random_string(12, (char *)&tr);                
1346
                root = json_object();
1347
                json_object_set_new(root, "janus", json_string("keepalive"));
1348
                json_object_set_new(root, "session_id", json_integer(session_id));
1349
                json_object_set_new(root, "transaction", json_string(tr));
1350
                if(secret)
1351
                        json_object_set_new(root, "apisecret", json_string(secret));
1352
                if(token)
1353
                        json_object_set_new(root, "token", json_string(token));
1354
                gateway->incoming_request(&janus_http_transport, msg, (void *)keepalive_id, FALSE, root, NULL);
1355
                /* Ok, go on */
1356
                if(handle_path) {
1357
                        char *location = (char *)g_malloc0(strlen(ws_path) + strlen(session_path) + 2);
1358
                        g_sprintf(location, "%s/%s", ws_path, session_path);
1359
                        JANUS_LOG(LOG_ERR, "Invalid GET to %s, redirecting to %s\n", url, location);
1360
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1361
                        MHD_add_response_header(response, "Location", location);
1362
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1363
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1364
                        if(msg->acrm)
1365
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1366
                        if(msg->acrh)
1367
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1368
                        ret = MHD_queue_response(connection, 302, response);
1369
                        MHD_destroy_response(response);
1370
                        g_free(location);
1371
                        goto done;
1372
                }
1373
                janus_mutex_lock(&sessions_mutex);
1374
                janus_http_session *session = g_hash_table_lookup(sessions, &session_id);
1375
                janus_mutex_unlock(&sessions_mutex);
1376
                if(!session || session->destroyed) {
1377
                        JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
1378
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1379
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1380
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1381
                        if(msg->acrm)
1382
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1383
                        if(msg->acrh)
1384
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1385
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1386
                        MHD_destroy_response(response);
1387
                        goto done;
1388
                }
1389
                /* How many messages can we send back in a single response? (just one by default) */
1390
                int max_events = 1;
1391
                const char *maxev = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "maxev");
1392
                if(maxev != NULL) {
1393
                        max_events = atoi(maxev);
1394
                        if(max_events < 1) {
1395
                                JANUS_LOG(LOG_WARN, "Invalid maxev parameter passed (%d), defaulting to 1\n", max_events);
1396
                                max_events = 1;
1397
                        }
1398
                }
1399
                JANUS_LOG(LOG_VERB, "Session %"SCNu64" found... returning up to %d messages\n", session_id, max_events);
1400
                /* Handle GET, taking the first message from the list */
1401
                json_t *event = g_async_queue_try_pop(session->events);
1402
                if(event != NULL) {
1403
                        if(max_events == 1) {
1404
                                /* Return just this message and leave */
1405
                                gchar *event_text = json_dumps(event, json_format);
1406
                                json_decref(event);
1407
                                ret = janus_http_return_success(msg, event_text);
1408
                        } else {
1409
                                /* The application is willing to receive more events at the same time, anything to report? */
1410
                                json_t *list = json_array();
1411
                                json_array_append_new(list, event);
1412
                                int events = 1;
1413
                                while(events < max_events) {
1414
                                        event = g_async_queue_try_pop(session->events);
1415
                                        if(event == NULL)
1416
                                                break;
1417
                                        json_array_append_new(list, event);
1418
                                        events++;
1419
                                }
1420
                                /* Return the array of messages and leave */
1421
                                gchar *list_text = json_dumps(list, json_format);
1422
                                json_decref(list);
1423
                                ret = janus_http_return_success(msg, list_text);
1424
                        }
1425
                } else {
1426
                        /* Still no message, wait */
1427
                        ret = janus_http_notifier(msg, max_events);
1428
                }
1429
                goto done;
1430
        }
1431
        
1432
        json_error_t error;
1433
        /* Parse the JSON payload */
1434
        root = json_loads(payload, 0, &error);
1435
        if(!root) {
1436
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_INVALID_JSON, "JSON error: on line %d: %s", error.line, error.text);
1437
                goto done;
1438
        }
1439
        if(!json_is_object(root)) {
1440
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_INVALID_JSON_OBJECT, "JSON error: not an object");
1441
                json_decref(root);
1442
                goto done;
1443
        }
1444

    
1445
parsingdone:
1446
        /* Check if we have session and handle identifiers */
1447
        session_id = session_path ? g_ascii_strtoull(session_path, NULL, 10) : 0;
1448
        handle_id = handle_path ? g_ascii_strtoull(handle_path, NULL, 10) : 0;
1449
        if(session_id > 0)
1450
                json_object_set_new(root, "session_id", json_integer(session_id));
1451
        if(handle_id > 0)
1452
                json_object_set_new(root, "handle_id", json_integer(handle_id));
1453

    
1454
        /* Suspend the connection and pass the ball to the core */
1455
        JANUS_LOG(LOG_HUGE, "Forwarding request to the core (%p)\n", msg);
1456
        gateway->incoming_request(&janus_http_transport, msg, msg, FALSE, root, &error);
1457
        /* Wait for a response (but not forever) */
1458
        struct timeval now;
1459
        gettimeofday(&now, NULL);
1460
        struct timespec wakeup;
1461
        wakeup.tv_sec = now.tv_sec+10;        /* Wait at max 10 seconds for a response */
1462
        wakeup.tv_nsec = now.tv_usec*1000UL;
1463
        pthread_mutex_lock(&msg->wait_mutex);
1464
        while(!msg->got_response) {
1465
                int res = pthread_cond_timedwait(&msg->wait_cond, &msg->wait_mutex, &wakeup);
1466
                if(msg->got_response || res == ETIMEDOUT)
1467
                        break;
1468
        }
1469
        pthread_mutex_unlock(&msg->wait_mutex);
1470
        if(!msg->response) {
1471
                ret = MHD_NO;
1472
        } else {
1473
                char *response_text = json_dumps(msg->response, json_format);
1474
                json_decref(msg->response);
1475
                msg->response = NULL;
1476
                ret = janus_http_return_success(msg, response_text);
1477
        }
1478

    
1479
done:
1480
        g_strfreev(basepath);
1481
        g_strfreev(path);
1482
        g_free(session_path);
1483
        g_free(handle_path);
1484
        return ret;
1485
}
1486

    
1487
/* Admin/monitor WebServer requests handler */
1488
int janus_http_admin_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr)
1489
{
1490
        char *payload = NULL;
1491
        json_t *root = NULL;
1492
        struct MHD_Response *response = NULL;
1493
        int ret = MHD_NO;
1494
        gchar *session_path = NULL, *handle_path = NULL;
1495
        gchar **basepath = NULL, **path = NULL;
1496
        guint64 session_id = 0, handle_id = 0;
1497

    
1498
        /* Is this the first round? */
1499
        int firstround = 0;
1500
        janus_http_msg *msg = (janus_http_msg *)*ptr;
1501
        if (msg == NULL) {
1502
                firstround = 1;
1503
                JANUS_LOG(LOG_VERB, "Got an admin/monitor HTTP %s request on %s...\n", method, url);
1504
                JANUS_LOG(LOG_DBG, " ... Just parsing headers for now...\n");
1505
                msg = g_malloc0(sizeof(janus_http_msg));
1506
                if(msg == NULL) {
1507
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1508
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1509
                        MHD_destroy_response(response);
1510
                        goto done;
1511
                }
1512
                msg->connection = connection;
1513
                msg->acrh = NULL;
1514
                msg->acrm = NULL;
1515
                msg->payload = NULL;
1516
                msg->len = 0;
1517
                msg->session_id = 0;
1518
                msg->got_response = FALSE;
1519
                msg->response = NULL;
1520
                janus_mutex_init(&msg->wait_mutex);
1521
                janus_condition_init(&msg->wait_cond);
1522
                janus_mutex_lock(&messages_mutex);
1523
                g_hash_table_insert(messages, msg, msg);
1524
                janus_mutex_unlock(&messages_mutex);
1525
                *ptr = msg;
1526
                MHD_get_connection_values(connection, MHD_HEADER_KIND, &janus_http_headers, msg);
1527
                ret = MHD_YES;
1528
                /* Notify handlers about this new transport instance */
1529
                if(notify_events && gateway->events_is_enabled()) {
1530
                        json_t *info = json_object();
1531
                        json_object_set_new(info, "event", json_string("request"));
1532
                        json_object_set_new(info, "admin_api", json_true());
1533
                        const union MHD_ConnectionInfo *conninfo = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1534
                        if(conninfo != NULL) {
1535
                                janus_network_address addr;
1536
                                janus_network_address_string_buffer addr_buf;
1537
                                if(janus_network_address_from_sockaddr((struct sockaddr *)conninfo->client_addr, &addr) == 0 &&
1538
                                                janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
1539
                                        const char *ip = janus_network_address_string_from_buffer(&addr_buf);
1540
                                        json_object_set_new(info, "ip", json_string(ip));
1541
                                }
1542
                                uint16_t port = janus_http_sockaddr_to_port((struct sockaddr *)conninfo->client_addr);
1543
                                json_object_set_new(info, "port", json_integer(port));
1544
                        }
1545
                        gateway->notify_event(&janus_http_transport, msg, info);
1546
                }
1547
        }
1548
        /* Parse request */
1549
        if (strcasecmp(method, "GET") && strcasecmp(method, "POST") && strcasecmp(method, "OPTIONS")) {
1550
                JANUS_LOG(LOG_ERR, "Unsupported method...\n");
1551
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1552
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1553
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1554
                if(msg->acrm)
1555
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1556
                if(msg->acrh)
1557
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1558
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_IMPLEMENTED, response);
1559
                MHD_destroy_response(response);
1560
                return ret;
1561
        }
1562
        if (!strcasecmp(method, "OPTIONS")) {
1563
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1564
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1565
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1566
                if(msg->acrm)
1567
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1568
                if(msg->acrh)
1569
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1570
                ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
1571
                MHD_destroy_response(response);
1572
        }
1573
        /* Get path components */
1574
        if(strcasecmp(url, admin_ws_path)) {
1575
                if(strlen(admin_ws_path) > 1) {
1576
                        basepath = g_strsplit(url, admin_ws_path, -1);
1577
                } else {
1578
                        /* The base path is the web server too itself, we process the url itself */
1579
                        basepath = g_malloc0(3);
1580
                        basepath[0] = g_strdup("/");
1581
                        basepath[1] = g_strdup(url);
1582
                }
1583
                if(basepath[0] == NULL || basepath[1] == NULL || basepath[1][0] != '/') {
1584
                        JANUS_LOG(LOG_ERR, "Invalid url %s\n", url);
1585
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1586
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1587
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1588
                        if(msg->acrm)
1589
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1590
                        if(msg->acrh)
1591
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1592
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1593
                        MHD_destroy_response(response);
1594
                }
1595
                if(firstround) {
1596
                        g_strfreev(basepath);
1597
                        return ret;
1598
                }
1599
                path = g_strsplit(basepath[1], "/", -1);
1600
                if(path == NULL || path[1] == NULL) {
1601
                        JANUS_LOG(LOG_ERR, "Invalid path %s (%s)\n", basepath[1], path[1]);
1602
                        response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1603
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1604
                        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1605
                        if(msg->acrm)
1606
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1607
                        if(msg->acrh)
1608
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1609
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1610
                        MHD_destroy_response(response);
1611
                }
1612
        }
1613
        if(firstround)
1614
                return ret;
1615
        JANUS_LOG(LOG_DBG, " ... parsing request...\n");
1616
        if(path != NULL && path[1] != NULL && strlen(path[1]) > 0) {
1617
                session_path = g_strdup(path[1]);
1618
                if(session_path == NULL) {
1619
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1620
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1621
                        MHD_destroy_response(response);
1622
                        goto done;
1623
                }
1624
                JANUS_LOG(LOG_HUGE, "Session: %s\n", session_path);
1625
        }
1626
        if(session_path != NULL && path[2] != NULL && strlen(path[2]) > 0) {
1627
                handle_path = g_strdup(path[2]);
1628
                if(handle_path == NULL) {
1629
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
1630
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1631
                        MHD_destroy_response(response);
1632
                        goto done;
1633
                }
1634
                JANUS_LOG(LOG_HUGE, "Handle: %s\n", handle_path);
1635
        }
1636
        if(session_path != NULL && handle_path != NULL && path[3] != NULL && strlen(path[3]) > 0) {
1637
                JANUS_LOG(LOG_ERR, "Too many components...\n");
1638
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1639
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1640
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1641
                if(msg->acrm)
1642
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1643
                if(msg->acrh)
1644
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1645
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1646
                MHD_destroy_response(response);
1647
                goto done;
1648
        }
1649
        /* Get payload, if any */
1650
        if(!strcasecmp(method, "POST")) {
1651
                JANUS_LOG(LOG_HUGE, "Processing POST data (%s) (%zu bytes)...\n", msg->contenttype, *upload_data_size);
1652
                if(*upload_data_size != 0) {
1653
                        if(msg->payload == NULL)
1654
                                msg->payload = g_malloc0(*upload_data_size+1);
1655
                        else
1656
                                msg->payload = g_realloc(msg->payload, msg->len+*upload_data_size+1);
1657
                        if(msg->payload == NULL) {
1658
                                JANUS_LOG(LOG_FATAL, "Memory error!\n");
1659
                                ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
1660
                                MHD_destroy_response(response);
1661
                                goto done;
1662
                        }
1663
                        memcpy(msg->payload+msg->len, upload_data, *upload_data_size);
1664
                        msg->len += *upload_data_size;
1665
                        memset(msg->payload + msg->len, '\0', 1);
1666
                        JANUS_LOG(LOG_DBG, "  -- Data we have now (%zu bytes)\n", msg->len);
1667
                        *upload_data_size = 0;        /* Go on */
1668
                        ret = MHD_YES;
1669
                        goto done;
1670
                }
1671
                JANUS_LOG(LOG_DBG, "Done getting payload, we can answer\n");
1672
                if(msg->payload == NULL) {
1673
                        JANUS_LOG(LOG_ERR, "No payload :-(\n");
1674
                        ret = MHD_NO;
1675
                        goto done;
1676
                }
1677
                payload = msg->payload;
1678
                JANUS_LOG(LOG_HUGE, "%s\n", payload);
1679
        }
1680

    
1681
        /* Is this a generic request for info? */
1682
        if(session_path != NULL && !strcmp(session_path, "info")) {
1683
                /* The info REST endpoint, if contacted through a GET, provides information on the gateway */
1684
                if(strcasecmp(method, "GET")) {
1685
                        ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_TRANSPORT_SPECIFIC, "Use GET for the info endpoint");
1686
                        goto done;
1687
                }
1688
                /* Turn this into a fake "info" request */
1689
                method = "POST";
1690
                char tr[12];
1691
                janus_http_random_string(12, (char *)&tr);                
1692
                root = json_object();
1693
                json_object_set_new(root, "janus", json_string("info"));
1694
                json_object_set_new(root, "transaction", json_string(tr));
1695
                goto parsingdone;
1696
        }
1697
        
1698
        /* Without a payload we don't know what to do */
1699
        if(!payload) {
1700
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_INVALID_JSON, "Request payload missing");
1701
                goto done;
1702
        }
1703
        json_error_t error;
1704
        /* Parse the JSON payload */
1705
        root = json_loads(payload, 0, &error);
1706
        if(!root) {
1707
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_INVALID_JSON, "JSON error: on line %d: %s", error.line, error.text);
1708
                goto done;
1709
        }
1710
        if(!json_is_object(root)) {
1711
                ret = janus_http_return_error(msg, 0, NULL, JANUS_ERROR_INVALID_JSON_OBJECT, "JSON error: not an object");
1712
                json_decref(root);
1713
                goto done;
1714
        }
1715

    
1716
parsingdone:
1717
        /* Check if we have session and handle identifiers */
1718
        session_id = session_path ? g_ascii_strtoull(session_path, NULL, 10) : 0;
1719
        handle_id = handle_path ? g_ascii_strtoull(handle_path, NULL, 10) : 0;
1720
        if(session_id > 0)
1721
                json_object_set_new(root, "session_id", json_integer(session_id));
1722
        if(handle_id > 0)
1723
                json_object_set_new(root, "handle_id", json_integer(handle_id));
1724

    
1725
        /* Suspend the connection and pass the ball to the core */
1726
        JANUS_LOG(LOG_HUGE, "Forwarding admin request to the core (%p)\n", msg);
1727
        gateway->incoming_request(&janus_http_transport, msg, msg, TRUE, root, &error);
1728
        /* Wait for a response (but not forever) */
1729
        struct timeval now;
1730
        gettimeofday(&now, NULL);
1731
        struct timespec wakeup;
1732
        wakeup.tv_sec = now.tv_sec+10;        /* Wait at max 10 seconds for a response */
1733
        wakeup.tv_nsec = now.tv_usec*1000UL;
1734
        pthread_mutex_lock(&msg->wait_mutex);
1735
        while(!msg->got_response) {
1736
                int res = pthread_cond_timedwait(&msg->wait_cond, &msg->wait_mutex, &wakeup);
1737
                if(msg->got_response || res == ETIMEDOUT)
1738
                        break;
1739
        }
1740
        pthread_mutex_unlock(&msg->wait_mutex);
1741
        if(!msg->response) {
1742
                ret = MHD_NO;
1743
        } else {
1744
                char *response_text = json_dumps(msg->response, json_format);
1745
                json_decref(msg->response);
1746
                msg->response = NULL;
1747
                ret = janus_http_return_success(msg, response_text);
1748
        }
1749

    
1750
done:
1751
        g_strfreev(basepath);
1752
        g_strfreev(path);
1753
        g_free(session_path);
1754
        g_free(handle_path);
1755
        return ret;
1756
}
1757

    
1758
int janus_http_headers(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) {
1759
        janus_http_msg *request = cls;
1760
        JANUS_LOG(LOG_DBG, "%s: %s\n", key, value);
1761
        if(!strcasecmp(key, MHD_HTTP_HEADER_CONTENT_TYPE)) {
1762
                if(request)
1763
                        request->contenttype = strdup(value);
1764
        } else if(!strcasecmp(key, "Access-Control-Request-Method")) {
1765
                if(request)
1766
                        request->acrm = strdup(value);
1767
        } else if(!strcasecmp(key, "Access-Control-Request-Headers")) {
1768
                if(request)
1769
                        request->acrh = strdup(value);
1770
        }
1771
        return MHD_YES;
1772
}
1773

    
1774
void janus_http_request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) {
1775
        JANUS_LOG(LOG_DBG, "Request completed, freeing data\n");
1776
        janus_http_msg *request = *con_cls;
1777
        if(!request)
1778
                return;
1779
        janus_mutex_lock(&messages_mutex);
1780
        g_hash_table_remove(messages, request);
1781
        janus_mutex_unlock(&messages_mutex);
1782
        if(request->payload != NULL)
1783
                g_free(request->payload);
1784
        if(request->contenttype != NULL)
1785
                free(request->contenttype);
1786
        if(request->acrh != NULL)
1787
                g_free(request->acrh);
1788
        if(request->acrm != NULL)
1789
                g_free(request->acrm);
1790
        g_free(request);
1791
        *con_cls = NULL;   
1792
}
1793

    
1794
/* Worker to handle notifications */
1795
int janus_http_notifier(janus_http_msg *msg, int max_events) {
1796
        if(!msg || !msg->connection)
1797
                return MHD_NO;
1798
        struct MHD_Connection *connection = msg->connection;
1799
        if(max_events < 1)
1800
                max_events = 1;
1801
        JANUS_LOG(LOG_DBG, "... handling long poll...\n");
1802
        struct MHD_Response *response = NULL;
1803
        int ret = MHD_NO;
1804
        guint64 session_id = msg->session_id;
1805
        janus_mutex_lock(&sessions_mutex);
1806
        janus_http_session *session = g_hash_table_lookup(sessions, &session_id);
1807
        janus_mutex_unlock(&sessions_mutex);
1808
        if(!session || session->destroyed) {
1809
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
1810
                response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
1811
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1812
                MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1813
                if(msg->acrm)
1814
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1815
                if(msg->acrh)
1816
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1817
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
1818
                MHD_destroy_response(response);
1819
                return ret;
1820
        }
1821
        gint64 start = janus_get_monotonic_time();
1822
        gint64 end = 0;
1823
        json_t *event = NULL, *list = NULL;
1824
        gboolean found = FALSE;
1825
        /* We have a timeout for the long poll: 30 seconds */
1826
        while(end-start < 30*G_USEC_PER_SEC) {
1827
                if(session->destroyed)
1828
                        break;
1829
                event = g_async_queue_try_pop(session->events);
1830
                if(session->destroyed || g_atomic_int_get(&stopping) || event != NULL) {
1831
                        if(event == NULL)
1832
                                break;
1833
                        /* Gotcha! */
1834
                        found = TRUE;
1835
                        if(max_events == 1) {
1836
                                break;
1837
                        } else {
1838
                                /* The application is willing to receive more events at the same time, anything to report? */
1839
                                list = json_array();
1840
                                json_array_append_new(list, event);
1841
                                int events = 1;
1842
                                while(events < max_events) {
1843
                                        event = g_async_queue_try_pop(session->events);
1844
                                        if(event == NULL)
1845
                                                break;
1846
                                        json_array_append_new(list, event);
1847
                                        events++;
1848
                                }
1849
                                break;
1850
                        }
1851
                }
1852
                /* Sleep 100ms */
1853
                g_usleep(100000);
1854
                end = janus_get_monotonic_time();
1855
        }
1856
        if((max_events == 1 && event == NULL) || (max_events > 1 && list == NULL))
1857
                found = FALSE;
1858
        if(!found) {
1859
                JANUS_LOG(LOG_VERB, "Long poll time out for session %"SCNu64"...\n", session_id);
1860
                /* Turn this into a "keepalive" response */
1861
                char tr[12];
1862
                janus_http_random_string(12, (char *)&tr);
1863
                if(max_events == 1) {
1864
                        event = json_object();
1865
                        json_object_set_new(event, "janus", json_string("keepalive"));
1866
                } else {
1867
                        list = json_array();
1868
                        event = json_object();
1869
                        json_object_set_new(event, "janus", json_string("keepalive"));
1870
                        json_array_append_new(list, event);
1871
                }
1872
                /* FIXME Improve the Janus protocol keep-alive mechanism in JavaScript */
1873
        }
1874
        char *payload_text = json_dumps(max_events == 1 ? event : list, json_format);
1875
        json_decref(max_events == 1 ? event : list);
1876
        /* Finish the request by sending the response */
1877
        JANUS_LOG(LOG_HUGE, "We have a message to serve...\n\t%s\n", payload_text);
1878
        /* Send event */
1879
        ret = janus_http_return_success(msg, payload_text);
1880
        return ret;
1881
}
1882

    
1883
/* Helper to quickly send a success response */
1884
int janus_http_return_success(janus_http_msg *msg, char *payload) {
1885
        if(!msg || !msg->connection) {
1886
                if(payload)
1887
                        free(payload);
1888
                return MHD_NO;
1889
        }
1890
        struct MHD_Response *response = MHD_create_response_from_buffer(
1891
                payload ? strlen(payload) : 0,
1892
                (void*)payload,
1893
                MHD_RESPMEM_MUST_FREE);
1894
        MHD_add_response_header(response, "Content-Type", "application/json");
1895
        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
1896
        MHD_add_response_header(response, "Access-Control-Max-Age", "86400");
1897
        if(msg->acrm)
1898
                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
1899
        if(msg->acrh)
1900
                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
1901
        int ret = MHD_queue_response(msg->connection, MHD_HTTP_OK, response);
1902
        MHD_destroy_response(response);
1903
        return ret;
1904
}
1905

    
1906
/* Helper to quickly send an error response */
1907
int janus_http_return_error(janus_http_msg *msg, uint64_t session_id, const char *transaction, gint error, const char *format, ...) {
1908
        gchar *error_string = NULL;
1909
        gchar error_buf[512];
1910
        if(format == NULL) {
1911
                /* No error string provided, use the default one */
1912
                error_string = (gchar *)janus_get_api_error(error);
1913
        } else {
1914
                /* This callback has variable arguments (error string) */
1915
                va_list ap;
1916
                va_start(ap, format);
1917
                g_vsnprintf(error_buf, 512, format, ap);
1918
                va_end(ap);
1919
                error_string = error_buf;
1920
        }
1921
        /* Done preparing error */
1922
        JANUS_LOG(LOG_VERB, "[%s] Returning error %d (%s)\n", transaction, error, error_string ? error_string : "no text");
1923
        /* Prepare JSON error */
1924
        json_t *reply = json_object();
1925
        json_object_set_new(reply, "janus", json_string("error"));
1926
        if(session_id > 0)
1927
                json_object_set_new(reply, "session_id", json_integer(session_id));
1928
        if(transaction != NULL)
1929
                json_object_set_new(reply, "transaction", json_string(transaction));
1930
        json_t *error_data = json_object();
1931
        json_object_set_new(error_data, "code", json_integer(error));
1932
        json_object_set_new(error_data, "reason", json_string(error_string));
1933
        json_object_set_new(reply, "error", error_data);
1934
        gchar *reply_text = json_dumps(reply, json_format);
1935
        json_decref(reply);
1936
        /* Use janus_http_return_error to send the error response */
1937
        return janus_http_return_success(msg, reply_text);
1938
}