mirror of
https://github.com/GOSTSec/ccminer
synced 2025-01-10 14:57:53 +00:00
api: add support for websocket calls
Allow to directly get api data in HTML5 Tested on Chrome... IE>=10 required, not tested IE11 seems buggy on connection close... todo Signed-off-by: Tanguy Pruvot <tanguy.pruvot@gmail.com>
This commit is contained in:
parent
2dcf983290
commit
543de0a73c
173
api.cpp
173
api.cpp
@ -80,7 +80,7 @@ static int ips = 1;
|
|||||||
static struct IP4ACCESS *ipaccess = NULL;
|
static struct IP4ACCESS *ipaccess = NULL;
|
||||||
|
|
||||||
#define MYBUFSIZ 16384
|
#define MYBUFSIZ 16384
|
||||||
#define SOCK_REC_BUFSZ 256
|
#define SOCK_REC_BUFSZ 1024
|
||||||
#define QUEUE 10
|
#define QUEUE 10
|
||||||
|
|
||||||
#define ALLIP4 "0.0.0.0"
|
#define ALLIP4 "0.0.0.0"
|
||||||
@ -411,6 +411,150 @@ static int send_result(SOCKETTYPE c, char *result)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Base64 Encoding/Decoding Table --- */
|
||||||
|
static const char table64[]=
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
static size_t base64_encode(const uchar *indata, size_t insize, char *outptr, size_t outlen)
|
||||||
|
{
|
||||||
|
uchar ibuf[3];
|
||||||
|
uchar obuf[4];
|
||||||
|
int i, inputparts, inlen = (int) insize;
|
||||||
|
size_t len = 0;
|
||||||
|
char *output, *outbuf;
|
||||||
|
|
||||||
|
memset(outptr, 0, outlen);
|
||||||
|
|
||||||
|
outbuf = output = (char*)calloc(1, inlen * 4 / 3 + 4);
|
||||||
|
if (outbuf == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (inlen > 0) {
|
||||||
|
for (i = inputparts = 0; i < 3; i++) {
|
||||||
|
if (inlen > 0) {
|
||||||
|
inputparts++;
|
||||||
|
ibuf[i] = (uchar) *indata;
|
||||||
|
indata++; inlen--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ibuf[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
obuf[0] = (uchar) ((ibuf[0] & 0xFC) >> 2);
|
||||||
|
obuf[1] = (uchar) (((ibuf[0] & 0x03) << 4) | ((ibuf[1] & 0xF0) >> 4));
|
||||||
|
obuf[2] = (uchar) (((ibuf[1] & 0x0F) << 2) | ((ibuf[2] & 0xC0) >> 6));
|
||||||
|
obuf[3] = (uchar) (ibuf[2] & 0x3F);
|
||||||
|
|
||||||
|
switch(inputparts) {
|
||||||
|
case 1: /* only one byte read */
|
||||||
|
snprintf(output, 5, "%c%c==",
|
||||||
|
table64[obuf[0]],
|
||||||
|
table64[obuf[1]]);
|
||||||
|
break;
|
||||||
|
case 2: /* two bytes read */
|
||||||
|
snprintf(output, 5, "%c%c%c=",
|
||||||
|
table64[obuf[0]],
|
||||||
|
table64[obuf[1]],
|
||||||
|
table64[obuf[2]]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
snprintf(output, 5, "%c%c%c%c",
|
||||||
|
table64[obuf[0]],
|
||||||
|
table64[obuf[1]],
|
||||||
|
table64[obuf[2]],
|
||||||
|
table64[obuf[3]] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((len+4) > outlen)
|
||||||
|
break;
|
||||||
|
output += 4; len += 4;
|
||||||
|
}
|
||||||
|
len = snprintf(outptr, len, "%s", outbuf);
|
||||||
|
// todo: seems to be missing on linux
|
||||||
|
if (strlen(outptr) == 27)
|
||||||
|
strcat(outptr, "=");
|
||||||
|
free(outbuf);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "compat/curl-for-windows/openssl/openssl/crypto/sha/sha.h"
|
||||||
|
|
||||||
|
/* websocket handshake (tested in Chrome) */
|
||||||
|
static int websocket_handshake(SOCKETTYPE c, char *result, char *clientkey)
|
||||||
|
{
|
||||||
|
char answer[256];
|
||||||
|
char inpkey[128] = { 0 };
|
||||||
|
char seckey[64];
|
||||||
|
uchar sha1[20];
|
||||||
|
SHA_CTX ctx;
|
||||||
|
|
||||||
|
if (opt_protocol)
|
||||||
|
applog(LOG_DEBUG, "clientkey: %s", clientkey);
|
||||||
|
|
||||||
|
sprintf(inpkey, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", clientkey);
|
||||||
|
|
||||||
|
// SHA-1 test from rfc, returns in base64 "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
|
||||||
|
//sprintf(inpkey, "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||||
|
|
||||||
|
SHA1_Init(&ctx);
|
||||||
|
SHA1_Update(&ctx, inpkey, strlen(inpkey));
|
||||||
|
SHA1_Final(sha1, &ctx);
|
||||||
|
|
||||||
|
base64_encode(sha1, 20, seckey, sizeof(seckey));
|
||||||
|
|
||||||
|
sprintf(answer,
|
||||||
|
"HTTP/1.1 101 Switching Protocol\r\n"
|
||||||
|
"Upgrade: WebSocket\r\nConnection: Upgrade\r\n"
|
||||||
|
"Sec-WebSocket-Accept: %s\r\n"
|
||||||
|
"Sec-WebSocket-Protocol: text\r\n"
|
||||||
|
"\r\n", seckey);
|
||||||
|
|
||||||
|
// data result as tcp frame
|
||||||
|
|
||||||
|
uchar hd[10] = { 0 };
|
||||||
|
hd[0] = 129; // 0x1 text frame (FIN + opcode)
|
||||||
|
uint64_t datalen = (uint64_t) strlen(result);
|
||||||
|
uint8_t frames = 2;
|
||||||
|
if (datalen <= 125) {
|
||||||
|
hd[1] = (uchar) (datalen);
|
||||||
|
} else if (datalen <= 65535) {
|
||||||
|
hd[1] = (uchar) 126;
|
||||||
|
hd[2] = (uchar) (datalen >> 8);
|
||||||
|
hd[3] = (uchar) (datalen);
|
||||||
|
frames = 4;
|
||||||
|
} else {
|
||||||
|
hd[1] = (uchar) 127;
|
||||||
|
hd[2] = (uchar) (datalen >> 56);
|
||||||
|
hd[3] = (uchar) (datalen >> 48);
|
||||||
|
hd[4] = (uchar) (datalen >> 40);
|
||||||
|
hd[5] = (uchar) (datalen >> 32);
|
||||||
|
hd[6] = (uchar) (datalen >> 24);
|
||||||
|
hd[7] = (uchar) (datalen >> 16);
|
||||||
|
hd[8] = (uchar) (datalen >> 8);
|
||||||
|
hd[9] = (uchar) (datalen);
|
||||||
|
frames = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t handlen = strlen(answer);
|
||||||
|
uchar *data = (uchar*) calloc(1, handlen + frames + (size_t) datalen + 1);
|
||||||
|
if (data == NULL)
|
||||||
|
return -1;
|
||||||
|
else {
|
||||||
|
uchar *p = data;
|
||||||
|
// HTTP header 101
|
||||||
|
memcpy(p, answer, handlen);
|
||||||
|
p += handlen;
|
||||||
|
// WebSocket Frame - Header + Data
|
||||||
|
memcpy(p, hd, frames);
|
||||||
|
memcpy(p + frames, result, (size_t)datalen);
|
||||||
|
send(c, (const char*)data, strlen(answer) + frames + (size_t)datalen + 1, 0);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
|
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
|
||||||
*/
|
*/
|
||||||
@ -649,6 +793,7 @@ static void api()
|
|||||||
|
|
||||||
if (addrok) {
|
if (addrok) {
|
||||||
bool fail;
|
bool fail;
|
||||||
|
char *wskey = NULL;
|
||||||
n = recv(c, &buf[0], SOCK_REC_BUFSZ, 0);
|
n = recv(c, &buf[0], SOCK_REC_BUFSZ, 0);
|
||||||
|
|
||||||
fail = SOCKETFAIL(n);
|
fail = SOCKETFAIL(n);
|
||||||
@ -666,6 +811,28 @@ static void api()
|
|||||||
// applog(LOG_DEBUG, "API: recv command: (%d) '%s'+char(%x)", n, buf, buf[n-1]);
|
// applog(LOG_DEBUG, "API: recv command: (%d) '%s'+char(%x)", n, buf, buf[n-1]);
|
||||||
|
|
||||||
if (!fail) {
|
if (!fail) {
|
||||||
|
char *msg = NULL;
|
||||||
|
/* Websocket requests compat. */
|
||||||
|
if ((msg = strstr(buf, "GET /")) && strlen(msg) > 5) {
|
||||||
|
char cmd[256] = { 0 };
|
||||||
|
sscanf(&msg[5], "%s\n", cmd);
|
||||||
|
params = strchr(cmd, '/');
|
||||||
|
if (params)
|
||||||
|
*(params++) = '|';
|
||||||
|
params = strchr(cmd, '/');
|
||||||
|
if (params)
|
||||||
|
*(params++) = '\0';
|
||||||
|
wskey = strstr(msg, "Sec-WebSocket-Key");
|
||||||
|
if (wskey) {
|
||||||
|
char *eol = strchr(wskey, '\r');
|
||||||
|
if (eol) *eol = '\0';
|
||||||
|
wskey = strchr(wskey, ':');
|
||||||
|
wskey++;
|
||||||
|
while ((*wskey) == ' ') wskey++; // ltrim
|
||||||
|
}
|
||||||
|
n = sprintf(buf, "%s", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
params = strchr(buf, '|');
|
params = strchr(buf, '|');
|
||||||
if (params != NULL)
|
if (params != NULL)
|
||||||
*(params++) = '\0';
|
*(params++) = '\0';
|
||||||
@ -676,6 +843,10 @@ static void api()
|
|||||||
for (i = 0; i < CMDMAX; i++) {
|
for (i = 0; i < CMDMAX; i++) {
|
||||||
if (strcmp(buf, cmds[i].name) == 0 && strlen(buf)) {
|
if (strcmp(buf, cmds[i].name) == 0 && strlen(buf)) {
|
||||||
result = (cmds[i].func)(params);
|
result = (cmds[i].func)(params);
|
||||||
|
if (wskey) {
|
||||||
|
websocket_handshake(c, result, wskey);
|
||||||
|
break;
|
||||||
|
}
|
||||||
send_result(c, result);
|
send_result(c, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
121
api/websocket.htm
Normal file
121
api/websocket.htm
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||||
|
<script src="http://code.highcharts.com/highcharts.js"></script>
|
||||||
|
<script src="http://code.highcharts.com/modules/exporting.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var hashrates = [];
|
||||||
|
var timestamps = [];
|
||||||
|
var timeline = [];
|
||||||
|
|
||||||
|
function drawChart(ip) {
|
||||||
|
|
||||||
|
$('#container').highcharts({
|
||||||
|
title: {
|
||||||
|
text: 'CCMiner WebSocket Sample - ' + ip,
|
||||||
|
x: -20 //center
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
text: 'By tpruvot@gibhub 2014',
|
||||||
|
x: -20
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'Age'
|
||||||
|
},
|
||||||
|
valueSuffix: ' s',
|
||||||
|
categories: timestamps[0]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'Hash Rate (KH/s)'
|
||||||
|
},
|
||||||
|
plotLines: [{
|
||||||
|
value: 0,
|
||||||
|
width: 1,
|
||||||
|
color: '#808080'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueSuffix: ' KH'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
layout: 'vertical',
|
||||||
|
align: 'right',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
borderWidth: 0
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'GPU0',
|
||||||
|
data: hashrates[0]
|
||||||
|
}/*,
|
||||||
|
{
|
||||||
|
name: 'GPU1',
|
||||||
|
data: hashrates[1]
|
||||||
|
}*/
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(ip, port) {
|
||||||
|
if ("WebSocket" in window) {
|
||||||
|
var ws = new WebSocket('ws://'+ip+':'+port+'/histo','text');
|
||||||
|
for (var gpu=0; gpu<8; gpu++) {
|
||||||
|
timestamps[gpu] = [];
|
||||||
|
hashrates[gpu] = [];
|
||||||
|
}
|
||||||
|
ws.onmessage = function (evt) {
|
||||||
|
var now = new Date();
|
||||||
|
var ts = Math.round(now/1000);
|
||||||
|
var data = evt.data.split('|');
|
||||||
|
for (n in data) {
|
||||||
|
var map = data[n].split(';');
|
||||||
|
var gpu = 0;
|
||||||
|
var plot = {};
|
||||||
|
for (k in map) {
|
||||||
|
var kv = map[k].split('=');
|
||||||
|
if (kv.length == 1)
|
||||||
|
continue;
|
||||||
|
if (kv[0] === 'GPU')
|
||||||
|
gpu = parseInt(kv[1], 10);
|
||||||
|
else if (kv[0] === 'TS')
|
||||||
|
plot.timestamp = ts - parseInt(kv[1], 10);
|
||||||
|
else if (kv[0] === 'KHS')
|
||||||
|
plot.hashrate = parseInt(kv[1], 10) / 1000.0;
|
||||||
|
//console.log('Data received: #GPU'+gpu+': '+kv[0]+' = '+kv[1]);
|
||||||
|
}
|
||||||
|
timeline[n] = plot.timestamp;
|
||||||
|
timestamps[gpu][n] = plot.timestamp;
|
||||||
|
hashrates[gpu][n] = plot.hashrate;
|
||||||
|
}
|
||||||
|
drawChart(ip);
|
||||||
|
};
|
||||||
|
ws.onerror = function (evt) {
|
||||||
|
var w = evt.target;
|
||||||
|
console.log('Error! readyState=' + w.readyState); //log errors
|
||||||
|
$('#container').html('Error! Unable to get WebSocket data from '+ip); //log errors
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ws.onclose = function() {
|
||||||
|
// websocket is closed.
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// The browser doesn't support WebSocket
|
||||||
|
alert("WebSocket NOT supported by your Browser!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
//getData('192.168.0.110', 4068);
|
||||||
|
getData('localhost', 4068);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user