mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-21 22:18:03 +08:00
183fc7ced9
PUBLISHED_FROM=faf0beb7545eb426c941fc366b6f87667723eb4c
147 lines
4.9 KiB
C
147 lines
4.9 KiB
C
/*
|
|
* 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;
|
|
}
|