mirror of
https://github.com/eddyem/onionserver.git
synced 2025-12-06 02:35:11 +03:00
Add websocket example
This commit is contained in:
parent
6cccee95ca
commit
cf158b6dac
@ -30,6 +30,9 @@ else()
|
|||||||
set(CMAKE_BUILD_TYPE RELEASE)
|
set(CMAKE_BUILD_TYPE RELEASE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
#pthreads
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
###### pkgconfig ######
|
###### pkgconfig ######
|
||||||
# pkg-config modules (for pkg-check-modules)
|
# pkg-config modules (for pkg-check-modules)
|
||||||
set(MODULES usefull_macros sqlite3)
|
set(MODULES usefull_macros sqlite3)
|
||||||
@ -62,7 +65,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
|||||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||||
|
|
||||||
# -l
|
# -l
|
||||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -lm)
|
||||||
|
|
||||||
# Installation of the program
|
# Installation of the program
|
||||||
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||||
|
|||||||
@ -31,6 +31,7 @@ static int help;
|
|||||||
// global parameters (init with default):
|
// global parameters (init with default):
|
||||||
glob_pars G = {
|
glob_pars G = {
|
||||||
.port = "8080",
|
.port = "8080",
|
||||||
|
.wsport = "8081",
|
||||||
.certfile = "cert.pem",
|
.certfile = "cert.pem",
|
||||||
.keyfile = "cert.key",
|
.keyfile = "cert.key",
|
||||||
};
|
};
|
||||||
@ -46,7 +47,8 @@ static myoption cmdlnopts[] = {
|
|||||||
{"dumpusers", NO_ARGS, NULL, 'U', arg_int, APTR(&G.dumpUserDB),_("dump users database")},
|
{"dumpusers", NO_ARGS, NULL, 'U', arg_int, APTR(&G.dumpUserDB),_("dump users database")},
|
||||||
{"dumpsess", NO_ARGS, NULL, 'S', arg_int, APTR(&G.dumpSessDB),_("dump session database")},
|
{"dumpsess", NO_ARGS, NULL, 'S', arg_int, APTR(&G.dumpSessDB),_("dump session database")},
|
||||||
{"server", NO_ARGS, NULL, 'r', arg_int, APTR(&G.runServer), _("run server process")},
|
{"server", NO_ARGS, NULL, 'r', arg_int, APTR(&G.runServer), _("run server process")},
|
||||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to listen")},
|
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("server port to listen")},
|
||||||
|
{"wsport", NEED_ARG, NULL, 'P', arg_string, APTR(&G.wsport), _("websocket port to listen (!= server port!)")},
|
||||||
{"certfile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.certfile), _("file with SSL certificate")},
|
{"certfile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.certfile), _("file with SSL certificate")},
|
||||||
{"keyfile", NEED_ARG, NULL, 'k', arg_string, APTR(&G.keyfile), _("file with SSL key")},
|
{"keyfile", NEED_ARG, NULL, 'k', arg_string, APTR(&G.keyfile), _("file with SSL key")},
|
||||||
{"usersdb", NEED_ARG, NULL, 'u', arg_string, APTR(&G.usersdb), _("users database filename")},
|
{"usersdb", NEED_ARG, NULL, 'u', arg_string, APTR(&G.usersdb), _("users database filename")},
|
||||||
@ -55,6 +57,7 @@ static myoption cmdlnopts[] = {
|
|||||||
{"useradd", NO_ARGS, NULL, 'a', arg_int, APTR(&G.useradd), _("add user[s] interactively")},
|
{"useradd", NO_ARGS, NULL, 'a', arg_int, APTR(&G.useradd), _("add user[s] interactively")},
|
||||||
{"sdatime", NEED_ARG, NULL, 'A', arg_longlong,APTR(&G.delatime), _("minimal atime to delete sessions from DB (-1 for >year)")},
|
{"sdatime", NEED_ARG, NULL, 'A', arg_longlong,APTR(&G.delatime), _("minimal atime to delete sessions from DB (-1 for >year)")},
|
||||||
{"sessdel", NEED_ARG, NULL, 'l', arg_string, APTR(&G.delsession),_("delete session by sessID or sockID")},
|
{"sessdel", NEED_ARG, NULL, 'l', arg_string, APTR(&G.delsession),_("delete session by sessID or sockID")},
|
||||||
|
{"logfile", NEED_ARG, NULL, 'L', arg_string, APTR(&G.logfilename),_("log file name")},
|
||||||
end_option
|
end_option
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,8 @@ typedef struct{
|
|||||||
int dumpSessDB; // dump session database
|
int dumpSessDB; // dump session database
|
||||||
int runServer; // run as server
|
int runServer; // run as server
|
||||||
int useradd; // add user[s]
|
int useradd; // add user[s]
|
||||||
char *port; // port to listen
|
char *port; // server port to listen
|
||||||
|
char *wsport; // websocket port to listen (!= port !!!)
|
||||||
char *certfile; // file with SSL certificate
|
char *certfile; // file with SSL certificate
|
||||||
char *keyfile; // file with SSL key
|
char *keyfile; // file with SSL key
|
||||||
char *usersdb; // users database name
|
char *usersdb; // users database name
|
||||||
@ -39,6 +40,7 @@ typedef struct{
|
|||||||
char **userdel; // user names to delete
|
char **userdel; // user names to delete
|
||||||
long long delatime; // minimal atime to delete sessions from DB
|
long long delatime; // minimal atime to delete sessions from DB
|
||||||
char *delsession; // delete session by sessID or sockID
|
char *delsession; // delete session by sessID or sockID
|
||||||
|
char *logfilename; // name of log file
|
||||||
int rest_pars_num; // number of rest parameters
|
int rest_pars_num; // number of rest parameters
|
||||||
char** rest_pars; // the rest parameters: array of char*
|
char** rest_pars; // the rest parameters: array of char*
|
||||||
} glob_pars;
|
} glob_pars;
|
||||||
|
|||||||
112
main.c
112
main.c
@ -32,6 +32,10 @@
|
|||||||
|
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
#include "cmdlnopts.h"
|
#include "cmdlnopts.h"
|
||||||
|
#include "websockets.h"
|
||||||
|
|
||||||
|
// temporary
|
||||||
|
#define putlog(...)
|
||||||
|
|
||||||
onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){
|
onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){
|
||||||
sessinfo *session = qookieSession(req);
|
sessinfo *session = qookieSession(req);
|
||||||
@ -52,36 +56,113 @@ onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_resp
|
|||||||
return OCS_CLOSE_CONNECTION;
|
return OCS_CLOSE_CONNECTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
static onion *o = NULL;
|
static onion *os = NULL, *ow = NULL;
|
||||||
void signals(int signo){
|
void signals(int signo){
|
||||||
if(o) onion_free(o);
|
|
||||||
closeSQLite();
|
closeSQLite();
|
||||||
|
if(os) onion_free(os);
|
||||||
|
if(ow) onion_free(ow);
|
||||||
exit(signo);
|
exit(signo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runServer(){
|
// POST/GET server
|
||||||
o = onion_new(O_POOL);
|
static void *runPostGet(_U_ void *data){
|
||||||
if(!(onion_flags(o) & O_SSL_AVAILABLE)){
|
os = onion_new(O_THREADED);
|
||||||
|
if(!(onion_flags(os) & O_SSL_AVAILABLE)){
|
||||||
ONION_ERROR("SSL support is not available");
|
ONION_ERROR("SSL support is not available");
|
||||||
signals(1);
|
signals(1);
|
||||||
}
|
}
|
||||||
int error = onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
|
int error = onion_set_certificate(os, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
|
||||||
if(error){
|
if(error){
|
||||||
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
|
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
|
||||||
signals(1);
|
signals(1);
|
||||||
}
|
}
|
||||||
onion_set_port(o, G.port);
|
onion_set_port(os, G.port);
|
||||||
onion_url *url = onion_root_url(o);
|
onion_url *url = onion_root_url(os);
|
||||||
onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
|
onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
|
||||||
onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);
|
onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);
|
||||||
onion_url_add(url, "^auth/", auth);
|
onion_url_add(url, "^auth/", auth);
|
||||||
onion_url_add(url, "^get/", get);
|
onion_url_add(url, "^get/", get);
|
||||||
signal(SIGTERM, signals);
|
error = onion_listen(os);
|
||||||
error = onion_listen(o);
|
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
|
||||||
if(error){
|
onion_free(os);
|
||||||
ONION_ERROR("Cant create the server: %s", strerror(errno));
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Websocket server
|
||||||
|
static void *runWS(_U_ void *data){
|
||||||
|
ow = onion_new(O_THREADED);
|
||||||
|
if(!(onion_flags(ow) & O_SSL_AVAILABLE)){
|
||||||
|
ONION_ERROR("SSL support is not available");
|
||||||
|
signals(1);
|
||||||
}
|
}
|
||||||
onion_free(o);
|
int error = onion_set_certificate(ow, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
|
||||||
|
if(error){
|
||||||
|
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
|
||||||
|
signals(1);
|
||||||
|
}
|
||||||
|
onion_set_port(ow, G.wsport);
|
||||||
|
onion_url *url = onion_root_url(ow);
|
||||||
|
onion_url_add(url, "", websocket_run);
|
||||||
|
DBG("Listen websocket");
|
||||||
|
error = onion_listen(ow);
|
||||||
|
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
|
||||||
|
onion_free(ow);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runServer(){
|
||||||
|
// if(G.logfilename) Cl_createlog();
|
||||||
|
signal(SIGTERM, signals);
|
||||||
|
signal(SIGINT, signals);
|
||||||
|
signal(SIGQUIT, signals);
|
||||||
|
signal(SIGTSTP, SIG_IGN);
|
||||||
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
|
||||||
|
pthread_t pg_thread, ws_thread;
|
||||||
|
if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
do{
|
||||||
|
if(pthread_kill(pg_thread, 0) == ESRCH){ // POST/GET died
|
||||||
|
WARNX("POST/GET server thread died");
|
||||||
|
putlog("POST/GET server thread died");
|
||||||
|
pthread_join(pg_thread, NULL);
|
||||||
|
if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){
|
||||||
|
putlog("pthread_create() failed");
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((pthread_kill(pg_thread, 0) == ESRCH) || (pthread_kill(ws_thread, 0) == ESRCH)){ // died
|
||||||
|
WARNX("Websocket server thread died");
|
||||||
|
putlog("Websocket server thread died");
|
||||||
|
pthread_join(ws_thread, NULL);
|
||||||
|
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
|
||||||
|
putlog("pthread_create() failed");
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
usleep(1000); // sleep a little or thread's won't be able to lock mutex
|
||||||
|
if(dtime() - tgot < T_INTERVAL) continue;
|
||||||
|
tgot = dtime();
|
||||||
|
/*
|
||||||
|
* INSERT CODE HERE
|
||||||
|
* Gather data (poll_device)
|
||||||
|
*/
|
||||||
|
// copy temporary buffers to main
|
||||||
|
pthread_mutex_lock(&mutex);
|
||||||
|
/*
|
||||||
|
* INSERT CODE HERE
|
||||||
|
* fill global data buffers
|
||||||
|
*/
|
||||||
|
pthread_mutex_unlock(&mutex);
|
||||||
|
#endif
|
||||||
|
}while(1);
|
||||||
|
putlog("Unreaceable code reached!");
|
||||||
|
ERRX("Unreaceable code reached!");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *getl(char *msg, int noempty){
|
static char *getl(char *msg, int noempty){
|
||||||
@ -178,7 +259,10 @@ int main(int argc, char **argv){
|
|||||||
if(!deleteOldSessions((int64_t)G.delatime))
|
if(!deleteOldSessions((int64_t)G.delatime))
|
||||||
green("All sessions with atime<%lld deleted\n", G.delatime);
|
green("All sessions with atime<%lld deleted\n", G.delatime);
|
||||||
}
|
}
|
||||||
if(G.runServer) runServer();
|
if(G.runServer){
|
||||||
|
if(strcmp(G.port, G.wsport) == 0) ERRX("Server port ans websocket port should be different!");
|
||||||
|
runServer();
|
||||||
|
}
|
||||||
closeSQLite();
|
closeSQLite();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,9 @@ auth = function(){
|
|||||||
$("inout").onclick = auth.logout;
|
$("inout").onclick = auth.logout;
|
||||||
}
|
}
|
||||||
function _wsk(request){
|
function _wsk(request){
|
||||||
var wsKey = request.responseText;
|
wsKey = request.responseText;
|
||||||
if(wsKey) console.log("Web key received: " + wsKey);
|
if(wsKey) console.log("Web key received: " + wsKey);
|
||||||
|
wsinit();
|
||||||
}
|
}
|
||||||
function reqAuth(request){
|
function reqAuth(request){
|
||||||
var txt = request.responseText;
|
var txt = request.responseText;
|
||||||
@ -62,10 +63,34 @@ auth = function(){
|
|||||||
var str = "auth/?login=" + l + "&passwd=" + p;
|
var str = "auth/?login=" + l + "&passwd=" + p;
|
||||||
sendrequest(str, reqAuth);
|
sendrequest(str, reqAuth);
|
||||||
}
|
}
|
||||||
|
// websockets
|
||||||
|
var ws;
|
||||||
|
function wsinit(){
|
||||||
|
delete(ws);
|
||||||
|
ws = new WebSocket('wss://localhost:8081');
|
||||||
|
ws.onopen = function(){ws.send("Akey="+wsKey);}; // send key after init
|
||||||
|
ws.onclose = function(evt){
|
||||||
|
var text = "WebSocket closed: ";
|
||||||
|
if(evt.wasClean) text += "by remote side";
|
||||||
|
else text += "connection lost"
|
||||||
|
$('wsmsgs').innerHTML = text;
|
||||||
|
};
|
||||||
|
ws.onmessage = function(evt){
|
||||||
|
$('wsmsgs').innerHTML = evt.data;
|
||||||
|
}
|
||||||
|
ws.onerror = function(err){
|
||||||
|
parseErr("WebSocket error " + err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function wssend(txt){
|
||||||
|
ws.send(txt);
|
||||||
|
}
|
||||||
return{
|
return{
|
||||||
init: init1,
|
init: init1,
|
||||||
login: login1,
|
login: login1,
|
||||||
logout: logout1,
|
logout: logout1,
|
||||||
send: sendlogpass
|
send: sendlogpass,
|
||||||
|
wssend: wssend,
|
||||||
|
wsinit: wsinit
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
|||||||
@ -7,7 +7,11 @@
|
|||||||
<body onload="auth.init();">
|
<body onload="auth.init();">
|
||||||
<p>Text
|
<p>Text
|
||||||
<p>More text
|
<p>More text
|
||||||
|
<button onclick="auth.wsinit();">Push me</button>
|
||||||
<p>
|
<p>
|
||||||
|
<div id="wsmsgs"></div>
|
||||||
<div id="errmsg" style='background-color: red;'></div>
|
<div id="errmsg" style='background-color: red;'></div>
|
||||||
|
<p>
|
||||||
|
<input type="text" id="wssnd" onchange="auth.wssend($('wssnd').value);">
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
59
websockets.c
Normal file
59
websockets.c
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Onion_test project.
|
||||||
|
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "websockets.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <onion/log.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#define BUFLEN 255
|
||||||
|
|
||||||
|
static onion_connection_status websocket_cont(_U_ void *data, onion_websocket *ws, ssize_t dlen){
|
||||||
|
FNAME();
|
||||||
|
char tmp[BUFLEN+1];
|
||||||
|
if(dlen > BUFLEN) dlen = BUFLEN;
|
||||||
|
|
||||||
|
int len = onion_websocket_read(ws, tmp, dlen);
|
||||||
|
if(len <= 0){
|
||||||
|
ONION_ERROR("Error reading data: %d: %s (%d)", errno, strerror(errno), dlen);
|
||||||
|
return OCS_NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
tmp[len] = 0;
|
||||||
|
DBG("WS: got %s", tmp);
|
||||||
|
onion_websocket_printf(ws, "Echo: %s", tmp);
|
||||||
|
ONION_INFO("Read from websocket: %d: %s", len, tmp);
|
||||||
|
return OCS_NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
onion_connection_status websocket_run(_U_ void *data, onion_request *req, onion_response *res){
|
||||||
|
FNAME();
|
||||||
|
onion_websocket *ws = onion_websocket_new(req, res);
|
||||||
|
if (!ws){
|
||||||
|
green("PROC\n");
|
||||||
|
DBG("Processed");
|
||||||
|
return OCS_PROCESSED;
|
||||||
|
}
|
||||||
|
DBG("WS ready");
|
||||||
|
green("RDY\n");
|
||||||
|
onion_websocket_printf(ws, "Hello from server. Write something to echo it");
|
||||||
|
onion_websocket_set_callback(ws, websocket_cont);
|
||||||
|
return OCS_WEBSOCKET;
|
||||||
|
}
|
||||||
26
websockets.h
Normal file
26
websockets.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Onion_test project.
|
||||||
|
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef WEBSOCKETS_H__
|
||||||
|
#define WEBSOCKETS_H__
|
||||||
|
#include <onion/onion.h>
|
||||||
|
#include <onion/websocket.h>
|
||||||
|
|
||||||
|
onion_connection_status websocket_run(void *data, onion_request *req, onion_response *res);
|
||||||
|
|
||||||
|
#endif // WEBSOCKETS_H__
|
||||||
Loading…
x
Reference in New Issue
Block a user