Ditch JSON-RPC from mongoose

PUBLISHED_FROM=89b978c02be2f10eb930ff13673d45249fd67763
This commit is contained in:
Sergey Lyubka 2016-07-01 12:32:34 +01:00 committed by Cesanta Bot
parent 421b420a0c
commit b7a0748312
30 changed files with 14 additions and 2323 deletions

View File

@ -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

View File

@ -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

View File

@ -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
```

View File

@ -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 }

View File

@ -1,18 +0,0 @@
---
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 }
---

View File

@ -1,16 +0,0 @@
---
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

View File

@ -1,16 +0,0 @@
---
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

View File

@ -1,16 +0,0 @@
---
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

View File

@ -1,22 +0,0 @@
---
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)`

View File

@ -1,20 +0,0 @@
---
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.

View File

@ -1,24 +0,0 @@
---
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`

View File

@ -1,16 +0,0 @@
---
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

View File

@ -1,14 +0,0 @@
---
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

View File

@ -1,15 +0,0 @@
---
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

View File

@ -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

View File

@ -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;
return;
}
if (v->len != 1) {
LOG(LL_ERROR, ("Invalid value: %.*s", (int) len, data));
goto out_free;
}
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) {

View File

@ -1,2 +0,0 @@
PROG = json_rpc_server
include ../examples.mk

View File

@ -1,68 +0,0 @@
/*
* 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;
}

View File

@ -1,18 +0,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)

View File

@ -1,72 +0,0 @@
= 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
----

View File

@ -1,146 +0,0 @@
/*
* 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;
}

View File

@ -1,173 +0,0 @@
/*
* 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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@ -1,533 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.1">
<title>Raspberry Pi camera/LED demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
[hidden],template{display:none}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
body{margin:0}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
.antialiased,body{-webkit-font-smoothing:antialiased}
img{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.no-bullet{list-style:none}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite:before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
.clearfix:after,.float-group:after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
.keyseq{color:rgba(51,51,51,.8)}
kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
b.button:before{content:"[";padding:0 3px 0 2px}
b.button:after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
#content{margin-top:1.25em}
#content:before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber:after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]:before{display:block}
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote:before{display:none}
.verseblock{margin:0 1em 1.25em 1em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.spread{width:100%}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot{border-width:1px 0}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-check-square-o:first-child,ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{position:relative;top:1px}
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
ul.inline>li>*{display:block}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1{padding-right:.75em;font-weight:bold}
td.hdlist1,td.hdlist2{vertical-align:top}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none}
span.footnote,span.footnoteref{vertical-align:super;font-size:.875em}
span.footnote a,span.footnoteref a{text-decoration:none}
span.footnote a:active,span.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
h1,h2{letter-spacing:-.01em}
dt,th.tableblock,td.content{text-rendering:optimizeLegibility}
p,td.content{letter-spacing:-.01em}
p strong,td.content strong{letter-spacing:-.005em}
p,blockquote,dt,td.content{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@media print{@page{margin:1.25cm .75cm}
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img{page-break-inside:avoid}
thead{display:table-header-group}
img{max-width:100%!important}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
.sect1{padding-bottom:0!important}
.sect1+.sect1{border:0!important}
#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span:before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]:before{display:block}
#footer{background:none!important;padding:0 .9375em}
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
</style>
</head>
<body class="article">
<div id="header">
<h1>Raspberry Pi camera/LED demo</h1>
</div>
<div id="content">
<div class="sect1">
<h2 id="_overview">Overview</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="/">demo</a> 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.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_objective">Objective</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The demo shows how to use websockets to communicate bidirectionally with an embedded device using standard protocols.</p>
</div>
<div class="paragraph">
<p>It also shows that it&#8217;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.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_it_works">How it works</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="docs/arch.png" alt="arch">
</div>
</div>
<div class="paragraph">
<p>There are two components, once with runs on the device (<code>device_side</code>) and one that runs on a stronger machine
and with more bandwidth (<code>cloud_side</code>).</p>
</div>
<div class="paragraph">
<p>The device app connects to the cloud app via websocket and sends a new jpeg frame as fast as the underlying <code>raspistill</code> camera
grabbing application can handle. The device automatically attempts reconnecting.</p>
</div>
<div class="paragraph">
<p>The cloud side serves the webapp static pages and serves an MPJEG image on <code>/mpjg</code>.
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.</p>
</div>
<div class="paragraph">
<p>The web app can turn on and off the LED via a RESTful api accessible via the <code>/api</code> handler.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_installation">Installation</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_server_side">Server side</h3>
<div class="listingblock">
<div class="content">
<pre>git clone https://github.com/cesanta/mongoose
cd mongoose/examples/web_demo
make cloud_side &amp;&amp; ./cloud_side 0.0.0.0:8080</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_raspberry_pi">Raspberry Pi</h3>
<div class="paragraph">
<p>The instructions provided here are tailored for the Raspbian distribution.</p>
</div>
<div class="sect3">
<h4 id="_dependencies">Dependencies</h4>
<div class="dlist">
<dl>
<dt class="hdlist1">jpegoptim</dt>
<dd>
<p>apt-get install jpegoptim</p>
</dd>
<dt class="hdlist1">camera</dt>
<dd>
<p>run raspi-config and enable camera</p>
</dd>
</dl>
</div>
</div>
<div class="sect3">
<h4 id="_led">LED</h4>
<div class="paragraph">
<p>In order to access the led on your <a href="http://www.qdh.org.uk/wordpress/?page_id=15">HotPi</a>
board you need to export the gpio pins:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>for i in 22 23 24; do
echo $i &gt;/sys/class/gpio/export
echo out &gt;/sys/class/gpio/gpio$i/direction
chgrp pi /sys/class/gpio/gpio$i/value
done</pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_build_and_run">Build and run</h4>
<div class="listingblock">
<div class="content">
<pre>git clone https://github.com/cesanta/mongoose
cd mongoose/examples/web_demo
make device_side &amp;&amp; ./device_side yourserver:8080</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2014-11-03 21:30:06 GMT
</div>
</div>
</body>
</html>

View File

@ -1 +0,0 @@
../../docs/arch.png

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,108 +0,0 @@
<!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>

View File

@ -610,603 +610,6 @@ double cs_time() {
return now;
}
#ifdef MG_MODULE_LINES
#line 1 "./src/../deps/frozen/frozen.c"
#endif
/*
* 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 _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
/* Amalgamated: #include "frozen.h" */
#ifdef _WIN32
#define snprintf _snprintf
#endif
#ifndef FROZEN_REALLOC
#define FROZEN_REALLOC realloc
#endif
#ifndef FROZEN_FREE
#define FROZEN_FREE free
#endif
struct frozen {
const char *end;
const char *cur;
struct json_token *tokens;
int max_tokens;
int num_tokens;
int do_realloc;
};
static int parse_object(struct frozen *f);
static int parse_value(struct frozen *f);
#define EXPECT(cond, err_code) \
do { \
if (!(cond)) return (err_code); \
} while (0)
#define TRY(expr) \
do { \
int _n = expr; \
if (_n < 0) return _n; \
} while (0)
#define END_OF_STRING (-1)
static int left(const struct frozen *f) {
return f->end - f->cur;
}
static int is_space(int ch) {
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
}
static void skip_whitespaces(struct frozen *f) {
while (f->cur < f->end && is_space(*f->cur)) f->cur++;
}
static int cur(struct frozen *f) {
skip_whitespaces(f);
return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur;
}
static int test_and_skip(struct frozen *f, int expected) {
int ch = cur(f);
if (ch == expected) {
f->cur++;
return 0;
}
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
}
static int test_no_skip(struct frozen *f, int expected) {
int ch = cur(f);
if (ch == expected) {
return 0;
}
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
}
static int is_alpha(int ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
static int is_digit(int ch) {
return ch >= '0' && ch <= '9';
}
static int is_hex_digit(int ch) {
return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
}
static int get_escape_len(const char *s, int len) {
switch (*s) {
case 'u':
return len < 6 ? JSON_STRING_INCOMPLETE
: is_hex_digit(s[1]) && is_hex_digit(s[2]) &&
is_hex_digit(s[3]) && is_hex_digit(s[4])
? 5
: JSON_STRING_INVALID;
case '"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
return len < 2 ? JSON_STRING_INCOMPLETE : 1;
default:
return JSON_STRING_INVALID;
}
}
static int capture_ptr(struct frozen *f, const char *ptr, enum json_type type) {
if (f->do_realloc && f->num_tokens >= f->max_tokens) {
int new_size = f->max_tokens == 0 ? 100 : f->max_tokens * 2;
void *p = FROZEN_REALLOC(f->tokens, new_size * sizeof(f->tokens[0]));
if (p == NULL) return JSON_TOKEN_ARRAY_TOO_SMALL;
f->max_tokens = new_size;
f->tokens = (struct json_token *) p;
}
if (f->tokens == NULL || f->max_tokens == 0) return 0;
if (f->num_tokens >= f->max_tokens) return JSON_TOKEN_ARRAY_TOO_SMALL;
f->tokens[f->num_tokens].ptr = ptr;
f->tokens[f->num_tokens].type = type;
f->num_tokens++;
return 0;
}
static int capture_len(struct frozen *f, int token_index, const char *ptr) {
if (f->tokens == 0 || f->max_tokens == 0) return 0;
EXPECT(token_index >= 0 && token_index < f->max_tokens, JSON_STRING_INVALID);
f->tokens[token_index].len = ptr - f->tokens[token_index].ptr;
f->tokens[token_index].num_desc = (f->num_tokens - 1) - token_index;
return 0;
}
/* identifier = letter { letter | digit | '_' } */
static int parse_identifier(struct frozen *f) {
EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID);
TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
while (f->cur < f->end &&
(*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) {
f->cur++;
}
capture_len(f, f->num_tokens - 1, f->cur);
return 0;
}
static int get_utf8_char_len(unsigned char ch) {
if ((ch & 0x80) == 0) return 1;
switch (ch & 0xf0) {
case 0xf0:
return 4;
case 0xe0:
return 3;
default:
return 2;
}
}
/* string = '"' { quoted_printable_chars } '"' */
static int parse_string(struct frozen *f) {
int n, ch = 0, len = 0;
TRY(test_and_skip(f, '"'));
TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
for (; f->cur < f->end; f->cur += len) {
ch = *(unsigned char *) f->cur;
len = get_utf8_char_len((unsigned char) ch);
EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
if (ch == '\\') {
EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
len += n;
} else if (ch == '"') {
capture_len(f, f->num_tokens - 1, f->cur);
f->cur++;
break;
};
}
return ch == '"' ? 0 : JSON_STRING_INCOMPLETE;
}
/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */
static int parse_number(struct frozen *f) {
int ch = cur(f);
TRY(capture_ptr(f, f->cur, JSON_TYPE_NUMBER));
if (ch == '-') f->cur++;
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID);
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++;
if (f->cur < f->end && f->cur[0] == '.') {
f->cur++;
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID);
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++;
}
if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) {
f->cur++;
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++;
EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID);
while (f->cur < f->end && is_digit(f->cur[0])) f->cur++;
}
capture_len(f, f->num_tokens - 1, f->cur);
return 0;
}
/* array = '[' [ value { ',' value } ] ']' */
static int parse_array(struct frozen *f) {
int ind;
TRY(test_and_skip(f, '['));
TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_ARRAY));
ind = f->num_tokens - 1;
while (cur(f) != ']') {
TRY(parse_value(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, ']'));
capture_len(f, ind, f->cur);
return 0;
}
static int compare(const char *s, const char *str, int len) {
int i = 0;
while (i < len && s[i] == str[i]) i++;
return i == len ? 1 : 0;
}
static int expect(struct frozen *f, const char *s, int len, enum json_type t) {
int i, n = left(f);
TRY(capture_ptr(f, f->cur, t));
for (i = 0; i < len; i++) {
if (i >= n) return JSON_STRING_INCOMPLETE;
if (f->cur[i] != s[i]) return JSON_STRING_INVALID;
}
f->cur += len;
TRY(capture_len(f, f->num_tokens - 1, f->cur));
return 0;
}
/* value = 'null' | 'true' | 'false' | number | string | array | object */
static int parse_value(struct frozen *f) {
int ch = cur(f);
switch (ch) {
case '"':
TRY(parse_string(f));
break;
case '{':
TRY(parse_object(f));
break;
case '[':
TRY(parse_array(f));
break;
case 'n':
TRY(expect(f, "null", 4, JSON_TYPE_NULL));
break;
case 't':
TRY(expect(f, "true", 4, JSON_TYPE_TRUE));
break;
case 'f':
TRY(expect(f, "false", 5, JSON_TYPE_FALSE));
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
TRY(parse_number(f));
break;
default:
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
}
return 0;
}
/* key = identifier | string */
static int parse_key(struct frozen *f) {
int ch = cur(f);
#if 0
printf("%s 1 [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur);
#endif
if (is_alpha(ch)) {
TRY(parse_identifier(f));
} else if (ch == '"') {
TRY(parse_string(f));
} else {
return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
}
return 0;
}
/* pair = key ':' value */
static int parse_pair(struct frozen *f) {
TRY(parse_key(f));
TRY(test_and_skip(f, ':'));
TRY(parse_value(f));
return 0;
}
/* object = '{' pair { ',' pair } '}' */
static int parse_object(struct frozen *f) {
int ind;
TRY(test_and_skip(f, '{'));
TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_OBJECT));
ind = f->num_tokens - 1;
while (cur(f) != '}') {
TRY(parse_pair(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, '}'));
capture_len(f, ind, f->cur);
return 0;
}
static int doit(struct frozen *f) {
int ret = 0;
if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID;
if (f->end == f->cur) return JSON_STRING_INCOMPLETE;
if (0 == (ret = test_no_skip(f, '{'))) {
TRY(parse_object(f));
} else if (0 == (ret = test_no_skip(f, '['))) {
TRY(parse_array(f));
} else {
return ret;
}
TRY(capture_ptr(f, f->cur, JSON_TYPE_EOF));
capture_len(f, f->num_tokens, f->cur);
return 0;
}
/* json = object */
int parse_json(const char *s, int s_len, struct json_token *arr, int arr_len) {
struct frozen frozen;
memset(&frozen, 0, sizeof(frozen));
frozen.end = s + s_len;
frozen.cur = s;
frozen.tokens = arr;
frozen.max_tokens = arr_len;
TRY(doit(&frozen));
return frozen.cur - s;
}
struct json_token *parse_json2(const char *s, int s_len) {
struct frozen frozen;
memset(&frozen, 0, sizeof(frozen));
frozen.end = s + s_len;
frozen.cur = s;
frozen.do_realloc = 1;
if (doit(&frozen) < 0) {
FROZEN_FREE((void *) frozen.tokens);
frozen.tokens = NULL;
}
return frozen.tokens;
}
static int path_part_len(const char *p) {
int i = 0;
while (p[i] != '\0' && p[i] != '[' && p[i] != '.') i++;
return i;
}
struct json_token *find_json_token(struct json_token *toks, const char *path) {
while (path != 0 && path[0] != '\0') {
int i, ind2 = 0, ind = -1, skip = 2, n = path_part_len(path);
if (path[0] == '[') {
if (toks->type != JSON_TYPE_ARRAY || !is_digit(path[1])) return 0;
for (ind = 0, n = 1; path[n] != ']' && path[n] != '\0'; n++) {
if (!is_digit(path[n])) return 0;
ind *= 10;
ind += path[n] - '0';
}
if (path[n++] != ']') return 0;
skip = 1; /* In objects, we skip 2 elems while iterating, in arrays 1. */
} else if (toks->type != JSON_TYPE_OBJECT)
return 0;
toks++;
for (i = 0; i < toks[-1].num_desc; i += skip, ind2++) {
/* ind == -1 indicated that we're iterating an array, not object */
if (ind == -1 && toks[i].type != JSON_TYPE_STRING) return 0;
if (ind2 == ind ||
(ind == -1 && toks[i].len == n && compare(path, toks[i].ptr, n))) {
i += skip - 1;
break;
};
if (toks[i - 1 + skip].type == JSON_TYPE_ARRAY ||
toks[i - 1 + skip].type == JSON_TYPE_OBJECT) {
i += toks[i - 1 + skip].num_desc;
}
}
if (i == toks[-1].num_desc) return 0;
path += n;
if (path[0] == '.') path++;
if (path[0] == '\0') return &toks[i];
toks += i;
}
return 0;
}
int json_emit_long(char *buf, int buf_len, long int value) {
char tmp[20];
int n = snprintf(tmp, sizeof(tmp), "%ld", value);
strncpy(buf, tmp, buf_len > 0 ? buf_len : 0);
return n;
}
int json_emit_double(char *buf, int buf_len, double value) {
char tmp[20];
int n = snprintf(tmp, sizeof(tmp), "%g", value);
strncpy(buf, tmp, buf_len > 0 ? buf_len : 0);
return n;
}
int json_emit_quoted_str(char *s, int s_len, const char *str, int len) {
const char *begin = s, *end = s + s_len, *str_end = str + len;
char ch;
#define EMIT(x) \
do { \
if (s < end) *s = x; \
s++; \
} while (0)
EMIT('"');
while (str < str_end) {
ch = *str++;
switch (ch) {
case '"':
EMIT('\\');
EMIT('"');
break;
case '\\':
EMIT('\\');
EMIT('\\');
break;
case '\b':
EMIT('\\');
EMIT('b');
break;
case '\f':
EMIT('\\');
EMIT('f');
break;
case '\n':
EMIT('\\');
EMIT('n');
break;
case '\r':
EMIT('\\');
EMIT('r');
break;
case '\t':
EMIT('\\');
EMIT('t');
break;
default:
EMIT(ch);
}
}
EMIT('"');
if (s < end) {
*s = '\0';
}
return s - begin;
}
int json_emit_unquoted_str(char *buf, int buf_len, const char *str, int len) {
if (buf_len > 0 && len > 0) {
int n = len < buf_len ? len : buf_len;
memcpy(buf, str, n);
if (n < buf_len) {
buf[n] = '\0';
}
}
return len;
}
int json_emit_va(char *s, int s_len, const char *fmt, va_list ap) {
const char *end = s + s_len, *str, *orig = s;
size_t len;
while (*fmt != '\0') {
switch (*fmt) {
case '[':
case ']':
case '{':
case '}':
case ',':
case ':':
case ' ':
case '\r':
case '\n':
case '\t':
if (s < end) {
*s = *fmt;
}
s++;
break;
case 'i':
s += json_emit_long(s, end - s, va_arg(ap, long) );
break;
case 'f':
s += json_emit_double(s, end - s, va_arg(ap, double) );
break;
case 'v':
str = va_arg(ap, char *);
len = va_arg(ap, size_t);
s += json_emit_quoted_str(s, end - s, str, len);
break;
case 'V':
str = va_arg(ap, char *);
len = va_arg(ap, size_t);
s += json_emit_unquoted_str(s, end - s, str, len);
break;
case 's':
str = va_arg(ap, char *);
s += json_emit_quoted_str(s, end - s, str, strlen(str));
break;
case 'S':
str = va_arg(ap, char *);
s += json_emit_unquoted_str(s, end - s, str, strlen(str));
break;
case 'T':
s += json_emit_unquoted_str(s, end - s, "true", 4);
break;
case 'F':
s += json_emit_unquoted_str(s, end - s, "false", 5);
break;
case 'N':
s += json_emit_unquoted_str(s, end - s, "null", 4);
break;
default:
return 0;
}
fmt++;
}
/* Best-effort to 0-terminate generated string */
if (s < end) {
*s = '\0';
}
return s - orig;
}
int json_emit(char *buf, int buf_len, const char *fmt, ...) {
int len;
va_list ap;
va_start(ap, fmt);
len = json_emit_va(buf, buf_len, fmt, ap);
va_end(ap);
return len;
}
#ifdef MG_MODULE_LINES
#line 1 "./src/../../common/md5.c"
#endif
/*
@ -8245,167 +7648,6 @@ struct mg_str mg_mk_str(const char *s) {
return ret;
}
#ifdef MG_MODULE_LINES
#line 1 "./src/json-rpc.c"
#endif
/* Copyright (c) 2014 Cesanta Software Limited */
/* All rights reserved */
#ifndef MG_DISABLE_JSON_RPC
/* Amalgamated: #include "mongoose/src/internal.h" */
/* Amalgamated: #include "mongoose/src/json-rpc.h" */
/* Amalgamated: #include "mongoose/deps/frozen/frozen.h" */
int mg_rpc_create_reply(char *buf, int len, const struct mg_rpc_request *req,
const char *result_fmt, ...) {
static const struct json_token null_tok = {"null", 4, 0, JSON_TYPE_NULL};
const struct json_token *id = req->id == NULL ? &null_tok : req->id;
va_list ap;
int n = 0;
n += json_emit(buf + n, len - n, "{s:s,s:", "jsonrpc", "2.0", "id");
if (id->type == JSON_TYPE_STRING) {
n += json_emit_quoted_str(buf + n, len - n, id->ptr, id->len);
} else {
n += json_emit_unquoted_str(buf + n, len - n, id->ptr, id->len);
}
n += json_emit(buf + n, len - n, ",s:", "result");
va_start(ap, result_fmt);
n += json_emit_va(buf + n, len - n, result_fmt, ap);
va_end(ap);
n += json_emit(buf + n, len - n, "}");
return n;
}
int mg_rpc_create_request(char *buf, int len, const char *method,
const char *id, const char *params_fmt, ...) {
va_list ap;
int n = 0;
n += json_emit(buf + n, len - n, "{s:s,s:s,s:s,s:", "jsonrpc", "2.0", "id",
id, "method", method, "params");
va_start(ap, params_fmt);
n += json_emit_va(buf + n, len - n, params_fmt, ap);
va_end(ap);
n += json_emit(buf + n, len - n, "}");
return n;
}
int mg_rpc_create_error(char *buf, int len, struct mg_rpc_request *req,
int code, const char *message, const char *fmt, ...) {
va_list ap;
int n = 0;
n += json_emit(buf + n, len - n, "{s:s,s:V,s:{s:i,s:s,s:", "jsonrpc", "2.0",
"id", req->id == NULL ? "null" : req->id->ptr,
req->id == NULL ? 4 : req->id->len, "error", "code",
(long) code, "message", message, "data");
va_start(ap, fmt);
n += json_emit_va(buf + n, len - n, fmt, ap);
va_end(ap);
n += json_emit(buf + n, len - n, "}}");
return n;
}
int mg_rpc_create_std_error(char *buf, int len, struct mg_rpc_request *req,
int code) {
const char *message = NULL;
switch (code) {
case JSON_RPC_PARSE_ERROR:
message = "parse error";
break;
case JSON_RPC_INVALID_REQUEST_ERROR:
message = "invalid request";
break;
case JSON_RPC_METHOD_NOT_FOUND_ERROR:
message = "method not found";
break;
case JSON_RPC_INVALID_PARAMS_ERROR:
message = "invalid parameters";
break;
case JSON_RPC_SERVER_ERROR:
message = "server error";
break;
default:
message = "unspecified error";
break;
}
return mg_rpc_create_error(buf, len, req, code, message, "N");
}
int mg_rpc_dispatch(const char *buf, int len, char *dst, int dst_len,
const char **methods, mg_rpc_handler_t *handlers) {
struct json_token tokens[200];
struct mg_rpc_request req;
int i, n;
memset(&req, 0, sizeof(req));
n = parse_json(buf, len, tokens, sizeof(tokens) / sizeof(tokens[0]));
if (n <= 0) {
int err_code = (n == JSON_STRING_INVALID) ? JSON_RPC_PARSE_ERROR
: JSON_RPC_SERVER_ERROR;
return mg_rpc_create_std_error(dst, dst_len, &req, err_code);
}
req.message = tokens;
req.id = find_json_token(tokens, "id");
req.method = find_json_token(tokens, "method");
req.params = find_json_token(tokens, "params");
if (req.id == NULL || req.method == NULL) {
return mg_rpc_create_std_error(dst, dst_len, &req,
JSON_RPC_INVALID_REQUEST_ERROR);
}
for (i = 0; methods[i] != NULL; i++) {
int mlen = strlen(methods[i]);
if (mlen == req.method->len &&
memcmp(methods[i], req.method->ptr, mlen) == 0)
break;
}
if (methods[i] == NULL) {
return mg_rpc_create_std_error(dst, dst_len, &req,
JSON_RPC_METHOD_NOT_FOUND_ERROR);
}
return handlers[i](dst, dst_len, &req);
}
int mg_rpc_parse_reply(const char *buf, int len, struct json_token *toks,
int max_toks, struct mg_rpc_reply *rep,
struct mg_rpc_error *er) {
int n = parse_json(buf, len, toks, max_toks);
memset(rep, 0, sizeof(*rep));
memset(er, 0, sizeof(*er));
if (n > 0) {
if ((rep->result = find_json_token(toks, "result")) != NULL) {
rep->message = toks;
rep->id = find_json_token(toks, "id");
} else {
er->message = toks;
er->id = find_json_token(toks, "id");
er->error_code = find_json_token(toks, "error.code");
er->error_message = find_json_token(toks, "error.message");
er->error_data = find_json_token(toks, "error.data");
}
}
return n;
}
#endif /* MG_DISABLE_JSON_RPC */
#ifdef MG_MODULE_LINES
#line 1 "./src/mqtt.c"
#endif
/*

View File

@ -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