Commit b7a07483 authored by Sergey Lyubka's avatar Sergey Lyubka Committed by Cesanta Bot

Ditch JSON-RPC from mongoose

PUBLISHED_FROM=89b978c02be2f10eb930ff13673d45249fd67763
parent 421b420a
......@@ -36,7 +36,6 @@ If you are looking for a complete solution with firmware and cloud components, c
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
- HTTP client, HTTP server
- WebSocket client, WebSocket server
- JSON-RPC client, JSON-RPC server
- MQTT client, MQTT broker
- CoAP client, CoAP server
- DNS client, DNS server, async DNS resolver
......
......@@ -7,6 +7,5 @@ title: Disabling flags
- `MG_DISABLE_MQTT` disable MQTT support
- `MG_DISABLE_SHA1` disable SHA1 support (used by Websocket)
- `MG_DISABLE_MD5` disable MD5 support (used by HTTP auth)
- `MG_DISABLE_JSON_RPC` disable JSON-RPC support
- `MG_DISABLE_SOCKETPAIR` disable `mg_broadcast()` API
- `MG_DISABLE_HTTP_KEEP_ALIVE` useful for embedded systems to save resources
......@@ -14,9 +14,9 @@ flags. Also, some preprocessor flags can be used to tune internal Mongoose
parameters.
To set a preprocessor flag during compile time, use `-D <PREPROCESSOR_FLAG>`
compiler option. For example, to disable both MQTT and JSON-RPC,
compiler option. For example, to disable both MQTT and COAP,
compile the application `my_app.c` like this (assumed UNIX system):
```
$ cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_JSON_RPC
$ cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_COAP
```
......@@ -3,7 +3,6 @@ items:
- { type: dir, name: mbuf.h }
- { type: dir, name: net.h }
- { type: dir, name: http.h }
- { type: dir, name: json-rpc.h }
- { type: dir, name: dns.h }
- { type: dir, name: dns-server.h }
- { type: dir, name: mqtt.h }
......
---
title: "JSON-RPC"
symbol_kind: "intro"
decl_name: "json-rpc.h"
items:
- { type: file, name: mg_rpc_parse_reply.md }
- { type: file, name: mg_rpc_create_request.md }
- { type: file, name: mg_rpc_create_reply.md }
- { type: file, name: mg_rpc_create_error.md }
- { type: file, name: mg_rpc_create_std_error.md }
- { type: file, name: mg_rpc_dispatch.md }
- { type: file, name: struct_mg_rpc_request.md }
- { type: file, name: struct_mg_rpc_reply.md }
- { type: file, name: struct_mg_rpc_error.md }
---
---
title: "mg_rpc_create_error()"
decl_name: "mg_rpc_create_error"
symbol_kind: "func"
signature: |
int mg_rpc_create_error(char *buf, int len, struct mg_rpc_request *req,
int code, const char *message, const char *fmt, ...);
---
Create JSON-RPC error reply in a given buffer.
Return length of the error, which
can be larger then `len` that indicates an overflow.
`fmt` format string should conform to `json_emit()` API,
see https://github.com/cesanta/frozen
---
title: "mg_rpc_create_reply()"
decl_name: "mg_rpc_create_reply"
symbol_kind: "func"
signature: |
int mg_rpc_create_reply(char *buf, int len, const struct mg_rpc_request *req,
const char *result_fmt, ...);
---
Create JSON-RPC reply in a given buffer.
Return length of the reply, which
can be larger then `len` that indicates an overflow.
`result_fmt` format string should conform to `json_emit()` API,
see https://github.com/cesanta/frozen
---
title: "mg_rpc_create_request()"
decl_name: "mg_rpc_create_request"
symbol_kind: "func"
signature: |
int mg_rpc_create_request(char *buf, int len, const char *method,
const char *id, const char *params_fmt, ...);
---
Create JSON-RPC request in a given buffer.
Return length of the request, which
can be larger then `len` that indicates an overflow.
`params_fmt` format string should conform to `json_emit()` API,
see https://github.com/cesanta/frozen
---
title: "mg_rpc_create_std_error()"
decl_name: "mg_rpc_create_std_error"
symbol_kind: "func"
signature: |
int mg_rpc_create_std_error(char *buf, int len, struct mg_rpc_request *req,
int code);
---
Create JSON-RPC error in a given buffer.
Return length of the error, which
can be larger then `len` that indicates an overflow. See
JSON_RPC_*_ERROR definitions for standard error values:
- `#define JSON_RPC_PARSE_ERROR (-32700)`
- `#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)`
- `#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)`
- `#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)`
- `#define JSON_RPC_INTERNAL_ERROR (-32603)`
- `#define JSON_RPC_SERVER_ERROR (-32000)`
---
title: "mg_rpc_dispatch()"
decl_name: "mg_rpc_dispatch"
symbol_kind: "func"
signature: |
int mg_rpc_dispatch(const char *buf, int, char *dst, int dst_len,
const char **methods, mg_rpc_handler_t *handlers);
---
Dispatches a JSON-RPC request.
Parses JSON-RPC request contained in `buf`, `len`.
Then, dispatches the request to the correct handler method.
Valid method names should be specified in NULL
terminated array `methods`, and corresponding handlers in `handlers`.
Result is put in `dst`, `dst_len`. Return: length of the result, which
can be larger then `dst_len` that indicates an overflow.
Overflown bytes are not written to the buffer.
If method is not found, an error is automatically generated.
---
title: "mg_rpc_parse_reply()"
decl_name: "mg_rpc_parse_reply"
symbol_kind: "func"
signature: |
int mg_rpc_parse_reply(const char *buf, int len, struct json_token *toks,
int max_toks, struct mg_rpc_reply *,
struct mg_rpc_error *);
---
Parse JSON-RPC reply contained in `buf`, `len` into JSON tokens array
`toks`, `max_toks`. If buffer contains valid reply, `reply` structure is
populated. The result of RPC call is located in `reply.result`. On error,
`error` structure is populated. Returns: the result of calling
`parse_json(buf, len, toks, max_toks)`:
On success, an offset inside `json_string` is returned
where parsing has finished. On failure, a negative number is
returned, one of:
- `#define JSON_STRING_INVALID -1`
- `#define JSON_STRING_INCOMPLETE -2`
- `#define JSON_TOKEN_ARRAY_TOO_SMALL -3`
---
title: "struct mg_rpc_error"
decl_name: "struct mg_rpc_error"
symbol_kind: "struct"
signature: |
struct mg_rpc_error {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *error_code; /* error.code */
struct json_token *error_message; /* error.message */
struct json_token *error_data; /* error.data, can be NULL */
};
---
JSON-RPC error
---
title: "struct mg_rpc_reply"
decl_name: "struct mg_rpc_reply"
symbol_kind: "struct"
signature: |
struct mg_rpc_reply {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *result; /* Remote call result */
};
---
JSON-RPC response
---
title: "struct mg_rpc_request"
decl_name: "struct mg_rpc_request"
symbol_kind: "struct"
signature: |
struct mg_rpc_request {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *method; /* Method name */
struct json_token *params; /* Method params */
};
---
JSON-RPC request
......@@ -4,7 +4,7 @@ title: Overview
Mongoose is a swiss army knife for embedded network programming.
It implements event-driven non-blocking API for TCP, UDP, HTTP,
WebSocket, CoAP, MQTT, JSON-RPC for both client and server mode.
WebSocket, CoAP, MQTT for both client and server mode.
Features include:
- Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android,
......@@ -16,7 +16,6 @@ Features include:
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
- HTTP client and server
- WebSocket client and server
- JSON-RPC client and server
- MQTT client and server
- CoAP client and server
- DNS client and server
......
......@@ -95,27 +95,19 @@ static double send_acc_data_since(struct mg_connection *nc,
static void process_command(struct mg_connection *nc, unsigned char *data,
size_t len) {
struct json_token *toks = parse_json2((const char *) data, len);
if (toks == NULL) {
// TODO(lsm): use proper JSON parser
int cmd, n, val;
double t;
if (sscanf((char *) data, "{\t\": %d, \"ts\": %lf, %n", &cmd, &t, &n) != 2) {
LOG(LL_ERROR, ("Invalid command: %.*s", (int) len, data));
return;
}
struct json_token *t = find_json_token(toks, "t");
if (t == NULL) {
LOG(LL_ERROR, ("Missing type field: %.*s", (int) len, data));
goto out_free;
}
if (t->len == 1 && *t->ptr == '1') {
struct json_token *v = find_json_token(toks, "v");
if (v == NULL) {
if (t == 1) {
if (sscanf((char *) data + n, "\"v\": %d", &val) != 1) {
LOG(LL_ERROR, ("Missing value: %.*s", (int) len, data));
goto out_free;
}
if (v->len != 1) {
LOG(LL_ERROR, ("Invalid value: %.*s", (int) len, data));
goto out_free;
return;
}
switch (*v->ptr) {
switch (val) {
case '0': {
GPIO_IF_LedOff(MCU_RED_LED_GPIO);
break;
......@@ -130,15 +122,13 @@ static void process_command(struct mg_connection *nc, unsigned char *data,
}
default: {
LOG(LL_ERROR, ("Invalid value: %.*s", (int) len, data));
goto out_free;
return;
}
}
} else {
LOG(LL_ERROR, ("Unknown command: %.*s", (int) t->len, t->ptr));
goto out_free;
LOG(LL_ERROR, ("Unknown command: %.*s", (int) len, data));
return;
}
out_free:
free(toks);
}
void data_conn_handler(struct mg_connection *nc, int ev, void *ev_data) {
......
PROG = json_rpc_server
include ../examples.mk
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*
* To test this server, do
* $ curl -d '{"id":1,method:"sum",params:[22,33]}' 127.0.0.1:8000
*/
#include "mongoose.h"
static const char *s_http_port = "8000";
static int rpc_sum(char *buf, int len, struct mg_rpc_request *req) {
double sum = 0;
int i;
if (req->params[0].type != JSON_TYPE_ARRAY) {
return mg_rpc_create_std_error(buf, len, req,
JSON_RPC_INVALID_PARAMS_ERROR);
}
for (i = 0; i < req->params[0].num_desc; i++) {
if (req->params[i + 1].type != JSON_TYPE_NUMBER) {
return mg_rpc_create_std_error(buf, len, req,
JSON_RPC_INVALID_PARAMS_ERROR);
}
sum += strtod(req->params[i + 1].ptr, NULL);
}
return mg_rpc_create_reply(buf, len, req, "f", sum);
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
static const char *methods[] = {"sum", NULL};
static mg_rpc_handler_t handlers[] = {rpc_sum, NULL};
char buf[100];
switch (ev) {
case MG_EV_HTTP_REQUEST:
mg_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), methods,
handlers);
mg_printf(nc,
"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
"Content-Type: application/json\r\n\r\n%s",
(int) strlen(buf), buf);
nc->flags |= MG_F_SEND_AND_CLOSE;
break;
default:
break;
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
mg_set_protocol_http_websocket(nc);
printf("Starting JSON-RPC server on port %s\n", s_http_port);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
NS=../../mongoose.c
FLAGS = ../../mongoose.c -I../..
CFLAGS=-W -Wall -DMG_ENABLE_THREADS -pthread $(CFLAGS_EXTRA)
PROGS = device_side cloud_side
all: $(PROGS)
device_side: Makefile device_side.c $(NS)
$(CC) device_side.c $(FLAGS) -o $@ $(CFLAGS)
cloud_side: Makefile cloud_side.c $(NS)
$(CC) cloud_side.c $(FLAGS) -o $@ $(CFLAGS)
device_side.exe: Makefile device_side.c $(NS)
cl device_side.c $(FLAGS) /MD /Fe$@
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROGS)
= Raspberry Pi camera/LED demo
== Overview
The link:/[demo] consists of web app providing access to a webcam and a LED attached to a RaspberryPi.
The device is assumed to have a limited bandwidth towards the server hosting the web app.
== Objective
The demo shows how to use websockets to communicate bidirectionally with an embedded device using standard protocols.
It also shows that it's possible to use Smart.c to develop also the cloud endpoint and expose WebSocket and RESTful APIs
easy to integreate with modern web stacks.
== How it works
image::docs/arch.png[]
There are two components, once with runs on the device (`device_side`) and one that runs on a stronger machine
and with more bandwidth (`cloud_side`).
The device app connects to the cloud app via websocket and sends a new jpeg frame as fast as the underlying `raspistill` camera
grabbing application can handle. The device automatically attempts reconnecting.
The cloud side serves the webapp static pages and serves an MPJEG image on `/mpjg`.
The MPJEG image handler blocks all the clients until a JPEG frame arrives via websocket
and then every client will receive a copy of the frame.
The web app can turn on and off the LED via a RESTful api accessible via the `/api` handler.
== Installation
=== Server side
----
git clone https://github.com/cesanta/mongoose
cd mongoose/examples/web_demo
make cloud_side && ./cloud_side 0.0.0.0:8080
----
=== Raspberry Pi
The instructions provided here are tailored for the Raspbian distribution.
==== Dependencies
jpegoptim::
apt-get install jpegoptim
camera::
run raspi-config and enable camera
==== LED
In order to access the led on your link:http://www.qdh.org.uk/wordpress/?page_id=15[HotPi]
board you need to export the gpio pins:
----
for i in 22 23 24; do
echo $i >/sys/class/gpio/export
echo out >/sys/class/gpio/gpio$i/direction
chgrp pi /sys/class/gpio/gpio$i/value
done
----
==== Build and run
----
git clone https://github.com/cesanta/mongoose
cd mongoose/examples/web_demo
make device_side && ./device_side yourserver:8080
----
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* This is the cloud endpoint of the Raspberry Pi camera/LED example
* of the Mongoose networking library.
* It is a simple web server, serving both static files, a REST API handler,
* and a WebSocket handler.
*/
#include "mongoose.h"
static struct mg_serve_http_opts web_root_opts;
/*
* Forwards the jpeg frame data to all open mjpeg connections.
*
* Incoming messages follow a very simple binary frame format:
* 4 bytes: timestamp (in network byte order)
* n bytes: jpeg payload
*
* The timestamp is used to compute a lag.
* It's done in a quite stupid way as it requires the device clock
* to be synchronized with the cloud endpoint.
*/
static void push_frame_to_clients(struct mg_mgr *mgr,
const struct websocket_message *wm) {
struct mg_connection *nc;
/*
* mjpeg connections are tagged with the MG_F_USER_2 flag so we can find them
* my scanning the connection list provided by the mongoose manager.
*/
for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
if (!(nc->flags & MG_F_USER_2)) continue; // Ignore un-marked requests
mg_printf(nc,
"--w00t\r\nContent-Type: image/jpeg\r\n"
"Content-Length: %lu\r\n\r\n",
(unsigned long) wm->size);
mg_send(nc, wm->data, wm->size);
mg_send(nc, "\r\n", 2);
printf("Image pushed to %p\n", nc);
}
}
/*
* Forwards API payload to the device, by scanning through
* all the connections to find those that are tagged as WebSocket.
*/
static void send_command_to_the_device(struct mg_mgr *mgr,
const struct mg_str *cmd) {
struct mg_connection *nc;
for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
if (!(nc->flags & MG_F_IS_WEBSOCKET))
continue; // Ignore non-websocket requests
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, cmd->p, cmd->len);
printf("Sent API command [%.*s] to %p\n", (int) cmd->len, cmd->p, nc);
}
}
/*
* Main event handler. Receives data events and dispatches to
* the appropriate handler function.
*
* 1. RESTful API requests are handled by send_command_to_the_device.
* 2. requests to /mpeg are established and left open waiting for data to arrive
* from WebSocket.
* 3. WebSocket frames are handled by push_frame_to_clients.
* 4. All other connections are passed to the mg_serve_http handler
* which serves static files.
*/
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct websocket_message *wm = (struct websocket_message *) ev_data;
struct http_message *hm = (struct http_message *) ev_data;
switch (ev) {
case MG_EV_HTTP_REQUEST:
if (mg_vcmp(&hm->uri, "/mjpg") == 0) {
nc->flags |= MG_F_USER_2; /* Set a mark on image requests */
mg_printf(nc, "%s",
"HTTP/1.0 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\n"
"Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Connection: close\r\n"
"Content-Type: multipart/x-mixed-replace; "
"boundary=--w00t\r\n\r\n");
} else if (mg_vcmp(&hm->uri, "/api") == 0 && hm->body.len > 0) {
/*
* RESTful API call. HTTP message body should be a JSON message.
* We should parse it and take appropriate action.
* In our case, simply forward that call to the device.
*/
printf("API CALL: [%.*s] [%.*s]\n", (int) hm->method.len, hm->method.p,
(int) hm->body.len, hm->body.p);
send_command_to_the_device(nc->mgr, &hm->body);
mg_printf(nc, "HTTP/1.0 200 OK\nContent-Length: 0\n\n");
} else {
/* Delegate to the static web server handler for all other paths. */
mg_serve_http(nc, hm, web_root_opts);
}
break;
case MG_EV_WEBSOCKET_FRAME:
printf("Got websocket frame, size %lu\n", (unsigned long) wm->size);
push_frame_to_clients(nc->mgr, wm);
break;
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
struct mg_connection *nc;
if (argc != 2) {
fprintf(stderr, "Usage: %s <listening_addr>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Listening on: [%s]\n", argv[1]);
mg_mgr_init(&mgr, NULL);
/*
* mg_bind() creates a listening connection on a given ip:port and
* with an attached event handler.
* The event handler will only trigger TCP events until the http
* protocol handler is installed.
*/
if ((nc = mg_bind(&mgr, argv[1], ev_handler)) == NULL) {
fprintf(stderr, "Error binding to %s\n", argv[1]);
exit(EXIT_FAILURE);
}
mg_set_protocol_http_websocket(nc);
web_root_opts.document_root = "./web_root";
/*
* We explicitly hand over control to the Mongoose manager
* in this event loop and we can easily multiplex other activities.
*/
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
return EXIT_SUCCESS;
}
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* This is the device endpoint of the Raspberry Pi camera/LED example
* of the Mongoose networking library.
* It is a simple websocket client, sending jpeg frames obtained from the
* RPi camera and receiving JSON commands through the same WebSocket channel
*/
#include <unistd.h>
#include "mongoose.h"
static int s_poll_interval_ms = 100;
static int s_still_period = 100;
static int s_vertical_flip = 0;
static int s_width = 320;
static int s_height = 180;
static const char *s_mjpg_file = "/var/run/shm/cam.jpg";
static struct mg_connection *client;
/*
* Check if there is a new image available and
* send it to the cloud endpoint if the send buffer is not too full.
* The image is moved in a new file by the jpeg optimizer function;
* this ensures that we will detect a new frame when raspistill writes
* it's output file.
*/
static void send_mjpg_frame(struct mg_connection *nc, const char *file_path) {
static int skipped_frames = 0;
struct stat st;
FILE *fp;
/* Check file modification time. */
if (stat(file_path, &st) == 0) {
/* Skip the frame if there is too much unsent data. */
if (nc->send_mbuf.len > 256) skipped_frames++;
/* Read new mjpg frame into a buffer */
fp = fopen(file_path, "rb");
char buf[st.st_size];
fread(buf, 1, sizeof(buf), fp);
fclose(fp);
/*
* Delete the file so we can detect when raspistill creates a new one.
* mtime granularity is only 1s.
*/
unlink(file_path);
/* Send those buffer through the websocket connection */
mg_send_websocket_frame(nc, WEBSOCKET_OP_BINARY, buf, sizeof(buf));
printf("Sent mjpg frame, %lu bytes after skippping %d frames\n",
(unsigned long) sizeof(buf), skipped_frames);
skipped_frames = 0;
}
}
/*
* Turn on or off the LED.
* The LED in this example is an RGB led, so all the colors have to be set.
*/
static void set_led(int v) {
char cmd[512];
snprintf(cmd, sizeof(cmd),
"for i in 22 23 24; do"
" echo %d >/sys/class/gpio/gpio$i/value; done",
v);
system(cmd);
}
/*
* Parse control JSON and perform command:
* for now only LED on/off is supported.
*/
static void perform_control_command(const char *data, size_t len) {
struct json_token toks[200], *onoff;
parse_json(data, len, toks, sizeof(toks));
onoff = find_json_token(toks, "onoff");
set_led(strncmp("[\"on\"]", onoff->ptr, onoff->len) == 0);
}
/* Main event handler. Sends websocket frames and receives control commands */
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct websocket_message *wm = (struct websocket_message *) ev_data;
switch (ev) {
case MG_EV_CONNECT:
printf("Reconnect: %s\n", *(int *) ev_data == 0 ? "ok" : "failed");
if (*(int *) ev_data == 0) {
/*
* Tune the tcp send buffer size, so that we can skip frames
* when the connection is congested. This helps maintaining a
* reasonable latency.
*/
int sndbuf_size = 512;
if (setsockopt(nc->sock, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size,
sizeof(int)) == -1) {
perror("failed to tune TCP send buffer size\n");
}
mg_send_websocket_handshake(nc, "/stream", NULL);
}
break;
case MG_EV_CLOSE:
printf("Connection %p closed\n", nc);
client = NULL;
break;
case MG_EV_POLL:
send_mjpg_frame(nc, s_mjpg_file);
break;
case MG_EV_WEBSOCKET_FRAME:
printf("Got control command: [%.*s]\n", (int) wm->size, wm->data);
perform_control_command((const char *) wm->data, wm->size);
break;
}
}
/*
* This thread regenerates s_mjpg_file every s_poll_interval_ms milliseconds.
* It is Raspberry PI specific, change this function on other systems.
*/
static void *generate_mjpg_data_thread_func(void *param) {
char cmd[400];
(void) param;
snprintf(cmd, sizeof(cmd),
"raspistill -w %d -h %d -n -q 100 -tl %d "
"-t 999999999 -v %s -o %s >/dev/null 2>&1",
s_width, s_height, s_still_period, s_vertical_flip ? "-vf" : "",
s_mjpg_file);
for (;;) {
int ret = system(cmd);
if (WIFSIGNALED(ret)) exit(1);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
char *addr = argv[1];
if (argc < 2) {
fprintf(stderr, "Usage: %s <server_address>\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Start separate thread that generates MJPG data */
mg_start_thread(generate_mjpg_data_thread_func, NULL);
printf("Streaming [%s] to [%s]\n", s_mjpg_file, addr);
mg_mgr_init(&mgr, NULL);
for (;;) {
mg_mgr_poll(&mgr, s_poll_interval_ms);
/* Reconnect if disconnected */
if (!client) {
sleep(1); /* limit reconnections frequency */
printf("Reconnecting to %s...\n", addr);
client = mg_connect(&mgr, addr, ev_handler);
if (client) mg_set_protocol_http_websocket(client);
}
}
return EXIT_SUCCESS;
}
This diff is collapsed.
../../docs/arch.png
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags-->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="stylesheet" href="framework7.min.css">
<title>Smart.c mjpg example</title>
<style type="text/css">
.image img {
display: block;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
<!-- Status bar overlay for full screen mode (PhoneGap) -->
<div class="statusbar-overlay"></div>
<!-- Views -->
<div class="views">
<!-- Your main view, should have "view-main" class -->
<div class="view view-main">
<!-- Top Navbar-->
<div class="navbar">
<div class="navbar-inner">
<!-- We need cool sliding animation on title element, so we have additional "sliding" class -->
<div class="left">
<a href="docs/docs/doc.html" class="link" onclick="location='docs/docs/doc.html'">
<span>About</span>
</a>
</div>
<div class="center sliding">Remote Camera</div>
<div class="right sliding">
<a href="https://github.com/cesanta/mongoose/tree/master/examples/raspberry_pi_mjpeg_led" class="link" onclick="location='https://github.com/cesanta/mongoose/tree/master/examples/raspberry_pi_mjpeg_led'">
<span>Github</span>
<i class="icon icon-next"></i>
</a>
</div>
</div>
</div>
<!-- Pages container, because we use fixed-through navbar and toolbar, it has additional appropriate classes-->
<div class="pages navbar-through toolbar-through">
<!-- Page, "data-page" contains page name -->
<div data-page="index" class="page">
<!-- Scrollable page content -->
<div class="page-content">
<div class="content-block-title">Camera View</div>
<div class="content-block image">
<img src="/mjpg">
</div>
<div class="content-block-title">Device Control</div>
<div class="list-block">
<form action="/api" method="GET" enctype="application/json"
id="form-control">
<ul>
<!-- Switch (Checkbox) -->
<li>
<div class="item-content">
<div class="item-inner">
<div class="item-title label">LED on/off</div>
<div class="item-input">
<label class="label-switch">
<input type="checkbox" name="onoff">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
</ul>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="framework7.min.js"></script>
<script type="text/javascript">
var myApp = new Framework7({
pushState: true,
swipePanel: 'left',
// ... other parameters
});
Dom7(document).on('change', '#form-control', function(ev) {
var data = myApp.formToJSON('#form-control');
var json = JSON.stringify(data);
Dom7.ajax({
url: '/api',
method: 'POST',
contentType: 'application/json',
data: json
});
});
</script>
</body>
</html>
This diff is collapsed.
......@@ -1067,74 +1067,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
#endif
#endif /* CS_COMMON_STR_UTIL_H_ */
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http: *www.gnu.org/licenses/>.
*
* You are free to use this library under the terms of the GNU General
* Public License, 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.
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
*/
#ifndef CS_MONGOOSE_DEPS_FROZEN_FROZEN_H_
#define CS_MONGOOSE_DEPS_FROZEN_FROZEN_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <stdarg.h>
enum json_type {
JSON_TYPE_EOF = 0, /* End of parsed tokens marker */
JSON_TYPE_STRING = 1,
JSON_TYPE_NUMBER = 2,
JSON_TYPE_OBJECT = 3,
JSON_TYPE_TRUE = 4,
JSON_TYPE_FALSE = 5,
JSON_TYPE_NULL = 6,
JSON_TYPE_ARRAY = 7
};
struct json_token {
const char *ptr; /* Points to the beginning of the token */
int len; /* Token length */
int num_desc; /* For arrays and object, total number of descendants */
enum json_type type; /* Type of the token, possible values above */
};
/* Error codes */
#define JSON_STRING_INVALID -1
#define JSON_STRING_INCOMPLETE -2
#define JSON_TOKEN_ARRAY_TOO_SMALL -3
int parse_json(const char *json_string, int json_string_length,
struct json_token *tokens_array, int size_of_tokens_array);
struct json_token *parse_json2(const char *json_string, int string_length);
struct json_token *find_json_token(struct json_token *toks, const char *path);
int json_emit_long(char *buf, int buf_len, long value);
int json_emit_double(char *buf, int buf_len, double value);
int json_emit_quoted_str(char *buf, int buf_len, const char *str, int len);
int json_emit_unquoted_str(char *buf, int buf_len, const char *str, int len);
int json_emit(char *buf, int buf_len, const char *fmt, ...);
int json_emit_va(char *buf, int buf_len, const char *fmt, va_list);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_MONGOOSE_DEPS_FROZEN_FROZEN_H_ */
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
......@@ -2754,144 +2686,6 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
}
#endif /* __cplusplus */
#endif /* CS_MONGOOSE_SRC_HTTP_H_ */
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === JSON-RPC
*/
#ifndef CS_MONGOOSE_SRC_JSON_RPC_H_
#define CS_MONGOOSE_SRC_JSON_RPC_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* JSON-RPC request */
struct mg_rpc_request {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *method; /* Method name */
struct json_token *params; /* Method params */
};
/* JSON-RPC response */
struct mg_rpc_reply {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *result; /* Remote call result */
};
/* JSON-RPC error */
struct mg_rpc_error {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *error_code; /* error.code */
struct json_token *error_message; /* error.message */
struct json_token *error_data; /* error.data, can be NULL */
};
/*
* Parse JSON-RPC reply contained in `buf`, `len` into JSON tokens array
* `toks`, `max_toks`. If buffer contains valid reply, `reply` structure is
* populated. The result of RPC call is located in `reply.result`. On error,
* `error` structure is populated. Returns: the result of calling
* `parse_json(buf, len, toks, max_toks)`:
*
* On success, an offset inside `json_string` is returned
* where parsing has finished. On failure, a negative number is
* returned, one of:
*
* - `#define JSON_STRING_INVALID -1`
* - `#define JSON_STRING_INCOMPLETE -2`
* - `#define JSON_TOKEN_ARRAY_TOO_SMALL -3`
*/
int mg_rpc_parse_reply(const char *buf, int len, struct json_token *toks,
int max_toks, struct mg_rpc_reply *,
struct mg_rpc_error *);
/*
* Create JSON-RPC request in a given buffer.
*
* Return length of the request, which
* can be larger then `len` that indicates an overflow.
* `params_fmt` format string should conform to `json_emit()` API,
* see https://github.com/cesanta/frozen
*/
int mg_rpc_create_request(char *buf, int len, const char *method,
const char *id, const char *params_fmt, ...);
/*
* Create JSON-RPC reply in a given buffer.
*
* Return length of the reply, which
* can be larger then `len` that indicates an overflow.
* `result_fmt` format string should conform to `json_emit()` API,
* see https://github.com/cesanta/frozen
*/
int mg_rpc_create_reply(char *buf, int len, const struct mg_rpc_request *req,
const char *result_fmt, ...);
/*
* Create JSON-RPC error reply in a given buffer.
*
* Return length of the error, which
* can be larger then `len` that indicates an overflow.
* `fmt` format string should conform to `json_emit()` API,
* see https://github.com/cesanta/frozen
*/
int mg_rpc_create_error(char *buf, int len, struct mg_rpc_request *req,
int code, const char *message, const char *fmt, ...);
/* JSON-RPC standard error codes */
#define JSON_RPC_PARSE_ERROR (-32700)
#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)
#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)
#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)
#define JSON_RPC_INTERNAL_ERROR (-32603)
#define JSON_RPC_SERVER_ERROR (-32000)
/*
* Create JSON-RPC error in a given buffer.
*
* Return length of the error, which
* can be larger then `len` that indicates an overflow. See
* JSON_RPC_*_ERROR definitions for standard error values:
*
* - `#define JSON_RPC_PARSE_ERROR (-32700)`
* - `#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)`
* - `#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)`
* - `#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)`
* - `#define JSON_RPC_INTERNAL_ERROR (-32603)`
* - `#define JSON_RPC_SERVER_ERROR (-32000)`
*/
int mg_rpc_create_std_error(char *buf, int len, struct mg_rpc_request *req,
int code);
typedef int (*mg_rpc_handler_t)(char *buf, int len, struct mg_rpc_request *req);
/*
* Dispatches a JSON-RPC request.
*
* Parses JSON-RPC request contained in `buf`, `len`.
* Then, dispatches the request to the correct handler method.
* Valid method names should be specified in NULL
* terminated array `methods`, and corresponding handlers in `handlers`.
* Result is put in `dst`, `dst_len`. Return: length of the result, which
* can be larger then `dst_len` that indicates an overflow.
* Overflown bytes are not written to the buffer.
* If method is not found, an error is automatically generated.
*/
int mg_rpc_dispatch(const char *buf, int, char *dst, int dst_len,
const char **methods, mg_rpc_handler_t *handlers);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_MONGOOSE_SRC_JSON_RPC_H_ */
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment