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)
|
||||
endif()
|
||||
|
||||
#pthreads
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
###### pkgconfig ######
|
||||
# pkg-config modules (for pkg-check-modules)
|
||||
set(MODULES usefull_macros sqlite3)
|
||||
@ -62,7 +65,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||
|
||||
# -l
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -lm)
|
||||
|
||||
# Installation of the program
|
||||
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||
|
||||
@ -31,6 +31,7 @@ static int help;
|
||||
// global parameters (init with default):
|
||||
glob_pars G = {
|
||||
.port = "8080",
|
||||
.wsport = "8081",
|
||||
.certfile = "cert.pem",
|
||||
.keyfile = "cert.key",
|
||||
};
|
||||
@ -46,7 +47,8 @@ static myoption cmdlnopts[] = {
|
||||
{"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")},
|
||||
{"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")},
|
||||
{"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")},
|
||||
@ -55,6 +57,7 @@ static myoption cmdlnopts[] = {
|
||||
{"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)")},
|
||||
{"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
|
||||
};
|
||||
|
||||
|
||||
@ -31,7 +31,8 @@ typedef struct{
|
||||
int dumpSessDB; // dump session database
|
||||
int runServer; // run as server
|
||||
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 *keyfile; // file with SSL key
|
||||
char *usersdb; // users database name
|
||||
@ -39,6 +40,7 @@ typedef struct{
|
||||
char **userdel; // user names to delete
|
||||
long long delatime; // minimal atime to delete sessions from DB
|
||||
char *delsession; // delete session by sessID or sockID
|
||||
char *logfilename; // name of log file
|
||||
int rest_pars_num; // number of rest parameters
|
||||
char** rest_pars; // the rest parameters: array of char*
|
||||
} glob_pars;
|
||||
|
||||
112
main.c
112
main.c
@ -32,6 +32,10 @@
|
||||
|
||||
#include "auth.h"
|
||||
#include "cmdlnopts.h"
|
||||
#include "websockets.h"
|
||||
|
||||
// temporary
|
||||
#define putlog(...)
|
||||
|
||||
onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){
|
||||
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;
|
||||
}
|
||||
|
||||
static onion *o = NULL;
|
||||
static onion *os = NULL, *ow = NULL;
|
||||
void signals(int signo){
|
||||
if(o) onion_free(o);
|
||||
closeSQLite();
|
||||
if(os) onion_free(os);
|
||||
if(ow) onion_free(ow);
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
static void runServer(){
|
||||
o = onion_new(O_POOL);
|
||||
if(!(onion_flags(o) & O_SSL_AVAILABLE)){
|
||||
// POST/GET server
|
||||
static void *runPostGet(_U_ void *data){
|
||||
os = onion_new(O_THREADED);
|
||||
if(!(onion_flags(os) & O_SSL_AVAILABLE)){
|
||||
ONION_ERROR("SSL support is not available");
|
||||
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){
|
||||
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
|
||||
signals(1);
|
||||
}
|
||||
onion_set_port(o, G.port);
|
||||
onion_url *url = onion_root_url(o);
|
||||
onion_set_port(os, G.port);
|
||||
onion_url *url = onion_root_url(os);
|
||||
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(url, "^auth/", auth);
|
||||
onion_url_add(url, "^get/", get);
|
||||
signal(SIGTERM, signals);
|
||||
error = onion_listen(o);
|
||||
if(error){
|
||||
ONION_ERROR("Cant create the server: %s", strerror(errno));
|
||||
error = onion_listen(os);
|
||||
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
|
||||
onion_free(os);
|
||||
return NULL;
|
||||
}
|
||||
onion_free(o);
|
||||
|
||||
// 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);
|
||||
}
|
||||
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){
|
||||
@ -178,7 +259,10 @@ int main(int argc, char **argv){
|
||||
if(!deleteOldSessions((int64_t)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();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -10,8 +10,9 @@ auth = function(){
|
||||
$("inout").onclick = auth.logout;
|
||||
}
|
||||
function _wsk(request){
|
||||
var wsKey = request.responseText;
|
||||
wsKey = request.responseText;
|
||||
if(wsKey) console.log("Web key received: " + wsKey);
|
||||
wsinit();
|
||||
}
|
||||
function reqAuth(request){
|
||||
var txt = request.responseText;
|
||||
@ -62,10 +63,34 @@ auth = function(){
|
||||
var str = "auth/?login=" + l + "&passwd=" + p;
|
||||
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{
|
||||
init: init1,
|
||||
login: login1,
|
||||
logout: logout1,
|
||||
send: sendlogpass
|
||||
send: sendlogpass,
|
||||
wssend: wssend,
|
||||
wsinit: wsinit
|
||||
};
|
||||
}();
|
||||
|
||||
@ -7,7 +7,11 @@
|
||||
<body onload="auth.init();">
|
||||
<p>Text
|
||||
<p>More text
|
||||
<button onclick="auth.wsinit();">Push me</button>
|
||||
<p>
|
||||
<div id="wsmsgs"></div>
|
||||
<div id="errmsg" style='background-color: red;'></div>
|
||||
<p>
|
||||
<input type="text" id="wssnd" onchange="auth.wssend($('wssnd').value);">
|
||||
</body>
|
||||
</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