mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5e10450dd | ||
|
|
e9d79b130d | ||
|
|
7951dac9d5 | ||
|
|
6c77351c58 | ||
|
|
7c4cda622f | ||
|
|
d8f8c1cb99 | ||
|
|
d2b9ad570f | ||
|
|
5b36f32996 | ||
|
|
3298030b8e | ||
|
|
6cfc696439 | ||
|
|
a93b3a06dd | ||
|
|
79e8d77ea0 | ||
|
|
d68fab9ee5 | ||
|
|
bda470245e | ||
|
|
0c95290f93 | ||
|
|
8989739d32 | ||
|
|
1112981e6e | ||
|
|
0cf82744ce | ||
|
|
5cdf838325 | ||
|
|
6418cad39f | ||
|
|
46529fd51e | ||
|
|
c09ba40a48 | ||
|
|
47d52896a1 | ||
|
|
d60c99b01c | ||
|
|
b8aeba06a2 | ||
|
|
8a0bc2d614 | ||
|
|
e34747d1ad | ||
|
|
1a6bc7c514 | ||
|
|
ffa0ddfdbf | ||
|
|
ca55bff1cf | ||
|
|
cfee5c4fdb | ||
|
|
e46185068b | ||
|
|
6ef2fea45e | ||
|
|
e244dd84a9 | ||
|
|
5034937a15 | ||
|
|
060045be65 | ||
|
|
9adfe8d484 | ||
|
|
799cc369e0 | ||
|
|
b97d3fea4b | ||
|
|
2a4ca9d5c3 | ||
|
|
747e40e74e | ||
|
|
cf279e7885 | ||
|
|
cbf968735a | ||
|
|
57b35e6007 | ||
|
|
1eaabdd8e5 | ||
|
|
dcd4e660a3 | ||
|
|
96409e74e5 | ||
|
|
e28ee097a1 | ||
|
|
2b4e544867 | ||
|
|
ee042a3481 | ||
|
|
63075f6846 | ||
|
|
7f2e42a06d | ||
|
|
3fbfa33b9a | ||
|
|
179aa75047 | ||
|
|
4658194d08 | ||
|
|
b35657814c | ||
|
|
a2322c77c6 | ||
|
|
b50c183c19 | ||
|
|
a941687db0 | ||
|
|
c2eef52b65 | ||
|
|
3e6cbbde04 | ||
|
|
6400536282 | ||
|
|
776103068f | ||
|
|
93beff3b65 | ||
|
|
22f8d86b05 | ||
|
|
3a9a9c7ad0 | ||
|
|
13238b1ad2 | ||
|
|
56f661e9c6 | ||
|
|
5351f46efb | ||
|
|
0cd3335b78 | ||
|
|
0611627448 | ||
|
|
f014918435 | ||
|
|
51e016089e |
No files matched your search
@@ -16,11 +16,10 @@ network protocols or scalable network applications with little effort.
|
||||
Mongoose helps developers to manage the complexity of network programming
|
||||
and let them concentrate on the logic, saving time and money.
|
||||
|
||||
[Download Mongoose Source Code here](http://hubs.ly/H0150FK0)
|
||||
[Download Mongoose Source Code here](http://hubs.ly/H01bWvx0)
|
||||
|
||||
> Nothing overdone. Nothing less. So unbelievably easy to use.
|
||||
> Just how good software should be!
|
||||
> - Pritin Tyagaraj, SAP
|
||||
> Just how good software should be! -- Pritin Tyagaraj, SAP
|
||||
|
||||
|
||||
# Features
|
||||
@@ -60,6 +59,13 @@ the functionality:
|
||||
[Developing a new product? Contact us today to discuss how Mongoose can help
|
||||
](https://www.cesanta.com/contact)
|
||||
|
||||
# Contributions
|
||||
|
||||
People who have agreed to the
|
||||
[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml)
|
||||
can make contributions. Note that the CLA isn't a copyright
|
||||
_assigment_ but rather a copyright _license_.
|
||||
You retain the copyright on your contributions.
|
||||
|
||||
# Licensing
|
||||
|
||||
@@ -89,7 +95,7 @@ and web designers. Available in three editions to suit your needs: free, pro
|
||||
(USD 5) and dev edition (from USD 8). To install, simply download, double-click
|
||||
to start and run browser - that's all!
|
||||
|
||||
[Download Mongoose Binary here](https://www.cesanta.com/mongoose)
|
||||
[Download Mongoose Binary here](https://www.cesanta.com/products#binary)
|
||||
|
||||
# Other products by Cesanta
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of object file images to be generated ()
|
||||
# GEN_BINS - list of binaries to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
TARGET = eagle
|
||||
#FLAVOR = release
|
||||
FLAVOR = debug
|
||||
|
||||
EXTRA_CCFLAGS += -Wall -Werror
|
||||
|
||||
ifndef PDIR # {
|
||||
GEN_IMAGES= eagle.app.v6.out
|
||||
GEN_BINS= eagle.app.v6.bin
|
||||
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
|
||||
SUBDIRS=user
|
||||
|
||||
endif # } PDIR
|
||||
|
||||
LDDIR = $(SDK_PATH)/ld
|
||||
|
||||
CCFLAGS += -Os
|
||||
|
||||
TARGET_LDFLAGS = \
|
||||
-nostdlib \
|
||||
-Wl,-EL \
|
||||
--longcalls \
|
||||
--text-section-literals
|
||||
|
||||
ifeq ($(FLAVOR),debug)
|
||||
TARGET_LDFLAGS += -g -O2
|
||||
endif
|
||||
|
||||
ifeq ($(FLAVOR),release)
|
||||
TARGET_LDFLAGS += -g -O0
|
||||
endif
|
||||
|
||||
COMPONENTS_eagle.app.v6 = \
|
||||
user/libuser.a
|
||||
|
||||
LINKFLAGS_eagle.app.v6 = \
|
||||
-L$(SDK_PATH)/lib \
|
||||
-Wl,--gc-sections \
|
||||
-nostdlib \
|
||||
-T$(LD_FILE) \
|
||||
-Wl,--no-check-sections \
|
||||
-u call_user_start \
|
||||
-Wl,-static \
|
||||
-Wl,--start-group \
|
||||
-lcirom \
|
||||
-lgcc \
|
||||
-lhal \
|
||||
-lphy \
|
||||
-lpp \
|
||||
-lnet80211 \
|
||||
-lwpa \
|
||||
-lcrypto \
|
||||
-lmain \
|
||||
-lfreertos \
|
||||
-llwip \
|
||||
$(DEP_LIBS_eagle.app.v6) \
|
||||
-Wl,--end-group
|
||||
|
||||
DEPENDS_eagle.app.v6 = \
|
||||
$(LD_FILE) \
|
||||
$(LDDIR)/eagle.rom.addr.v6.ld
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
|
||||
#UNIVERSAL_TARGET_DEFINES = \
|
||||
|
||||
# Other potential configuration flags include:
|
||||
# -DTXRX_TXBUF_DEBUG
|
||||
# -DTXRX_RXBUF_DEBUG
|
||||
# -DWLAN_CONFIG_CCX
|
||||
CONFIGURATION_DEFINES = -DICACHE_FLASH
|
||||
|
||||
DEFINES += \
|
||||
$(UNIVERSAL_TARGET_DEFINES) \
|
||||
$(CONFIGURATION_DEFINES)
|
||||
|
||||
DDEFINES += \
|
||||
$(UNIVERSAL_TARGET_DEFINES) \
|
||||
$(CONFIGURATION_DEFINES)
|
||||
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
sinclude $(SDK_PATH)/Makefile
|
||||
|
||||
.PHONY: FORCE
|
||||
FORCE:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
This is a Mongoose "Hello, world" that can be compiled under ESP8266 RTOS SDK.
|
||||
|
||||
It sets up an AP (SSID `Mongoose`) and serves a "hello world" page on http://192.168.4.1/
|
||||
|
||||
Most of the the boilerplate comes from [project_template](https://github.com/espressif/ESP8266_RTOS_SDK/tree/master/examples/project_template) (@ [3ca6af5](https://github.com/espressif/ESP8266_RTOS_SDK/tree/3ca6af5da68678d809b34c7cd98bee71e0235039/examples/project_template)) with minimal changes.
|
||||
|
||||
To build with no changes to the SDK, you will need a module with 1MB (8Mb) flash or more.
|
||||
|
||||
Compile (for NodeMCU 1.0):
|
||||
|
||||
```
|
||||
$ export SDK_PATH=/path/to/ESP8266_RTOS_SDK
|
||||
$ export BIN_PATH=./bin
|
||||
$ make clean; make BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=dio SPI_SIZE_MAP=6
|
||||
```
|
||||
|
||||
Flash (using [esptool](https://github.com/themadinventor/esptool)):
|
||||
|
||||
```
|
||||
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
|
||||
write_flash --flash_mode=dio --flash_size=32m \
|
||||
0x00000 ${SDK_PATH}/bin/boot_v1.4\(b1\).bin \
|
||||
0x01000 ${BIN_PATH}/upgrade/user1.4096.new.6.bin
|
||||
```
|
||||
|
||||
|
||||
The output can be made to fit in 512KB (4Mb), but stock linker scripts do not reserve enough space for code.
|
||||
Custom linker script is provided for that, so you can use it for smaller devices like so (example that will work with ESP-01):
|
||||
|
||||
```
|
||||
$ make clean; make BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=qio SPI_SIZE_MAP=0 LD_FILE=ld/eagle.app.v6.512.compact.ld
|
||||
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
|
||||
write_flash --flash_mode=qio --flash_size=4m \
|
||||
0x00000 ${BIN_PATH}/eagle.flash.bin \
|
||||
0x10000 ${BIN_PATH}/eagle.irom0text.bin
|
||||
```
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/bin/bash
|
||||
|
||||
:<<!
|
||||
******NOTICE******
|
||||
MUST set SDK_PATH & BIN_PATH firstly!!!
|
||||
example:
|
||||
export SDK_PATH=~/ESP8266_RTOS_SDK
|
||||
export BIN_PATH=~/esp8266_bin
|
||||
!
|
||||
|
||||
echo "gen_misc.sh version 20150911"
|
||||
echo ""
|
||||
|
||||
if [ $SDK_PATH ]; then
|
||||
echo "SDK_PATH:"
|
||||
echo "$SDK_PATH"
|
||||
echo ""
|
||||
else
|
||||
echo "ERROR: Please export SDK_PATH in gen_misc.sh firstly, exit!!!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $BIN_PATH ]; then
|
||||
echo "BIN_PATH:"
|
||||
echo "$BIN_PATH"
|
||||
echo ""
|
||||
else
|
||||
echo "ERROR: Please export BIN_PATH in gen_misc.sh firstly, exit!!!"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "Please check SDK_PATH & BIN_PATH, enter (Y/y) to continue:"
|
||||
read input
|
||||
|
||||
if [[ $input != Y ]] && [[ $input != y ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
echo "Please follow below steps(1-5) to generate specific bin(s):"
|
||||
echo "STEP 1: use boot_v1.2+ by default"
|
||||
boot=new
|
||||
|
||||
echo "boot mode: $boot"
|
||||
echo ""
|
||||
|
||||
echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)"
|
||||
echo "enter (0/1/2, default 0):"
|
||||
read input
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
if [ $boot != none ]; then
|
||||
boot=none
|
||||
echo "ignore boot"
|
||||
fi
|
||||
app=0
|
||||
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||
elif [ $input == 1 ]; then
|
||||
if [ $boot == none ]; then
|
||||
app=0
|
||||
echo "choose no boot before"
|
||||
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||
else
|
||||
app=1
|
||||
echo "generate bin: user1.bin"
|
||||
fi
|
||||
elif [ $input == 2 ]; then
|
||||
if [ $boot == none ]; then
|
||||
app=0
|
||||
echo "choose no boot before"
|
||||
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||
else
|
||||
app=2
|
||||
echo "generate bin: user2.bin"
|
||||
fi
|
||||
else
|
||||
if [ $boot != none ]; then
|
||||
boot=none
|
||||
echo "ignore boot"
|
||||
fi
|
||||
app=0
|
||||
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)"
|
||||
echo "enter (0/1/2/3, default 2):"
|
||||
read input
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
spi_speed=40
|
||||
elif [ $input == 0 ]; then
|
||||
spi_speed=20
|
||||
elif [ $input == 1 ]; then
|
||||
spi_speed=26.7
|
||||
elif [ $input == 3 ]; then
|
||||
spi_speed=80
|
||||
else
|
||||
spi_speed=40
|
||||
fi
|
||||
|
||||
echo "spi speed: $spi_speed MHz"
|
||||
echo ""
|
||||
|
||||
echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)"
|
||||
echo "enter (0/1/2/3, default 0):"
|
||||
read input
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
spi_mode=QIO
|
||||
elif [ $input == 1 ]; then
|
||||
spi_mode=QOUT
|
||||
elif [ $input == 2 ]; then
|
||||
spi_mode=DIO
|
||||
elif [ $input == 3 ]; then
|
||||
spi_mode=DOUT
|
||||
else
|
||||
spi_mode=QIO
|
||||
fi
|
||||
|
||||
echo "spi mode: $spi_mode"
|
||||
echo ""
|
||||
|
||||
echo "STEP 5: choose spi size and map"
|
||||
echo " 0= 512KB( 256KB+ 256KB)"
|
||||
echo " 2=1024KB( 512KB+ 512KB)"
|
||||
echo " 3=2048KB( 512KB+ 512KB)"
|
||||
echo " 4=4096KB( 512KB+ 512KB)"
|
||||
echo " 5=2048KB(1024KB+1024KB)"
|
||||
echo " 6=4096KB(1024KB+1024KB)"
|
||||
echo "enter (0/2/3/4/5/6, default 0):"
|
||||
read input
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
spi_size_map=0
|
||||
echo "spi size: 512KB"
|
||||
echo "spi ota map: 256KB + 256KB"
|
||||
elif [ $input == 2 ]; then
|
||||
spi_size_map=2
|
||||
echo "spi size: 1024KB"
|
||||
echo "spi ota map: 512KB + 512KB"
|
||||
elif [ $input == 3 ]; then
|
||||
spi_size_map=3
|
||||
echo "spi size: 2048KB"
|
||||
echo "spi ota map: 512KB + 512KB"
|
||||
elif [ $input == 4 ]; then
|
||||
spi_size_map=4
|
||||
echo "spi size: 4096KB"
|
||||
echo "spi ota map: 512KB + 512KB"
|
||||
elif [ $input == 5 ]; then
|
||||
spi_size_map=5
|
||||
echo "spi size: 2048KB"
|
||||
echo "spi ota map: 1024KB + 1024KB"
|
||||
elif [ $input == 6 ]; then
|
||||
spi_size_map=6
|
||||
echo "spi size: 4096KB"
|
||||
echo "spi ota map: 1024KB + 1024KB"
|
||||
else
|
||||
spi_size_map=0
|
||||
echo "spi size: 512KB"
|
||||
echo "spi ota map: 256KB + 256KB"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
echo "start..."
|
||||
echo ""
|
||||
|
||||
make clean
|
||||
|
||||
make BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE_MAP=$spi_size_map
|
||||
@@ -0,0 +1,20 @@
|
||||
/* eagle.flash.bin @ 0x00000 */
|
||||
/* eagle.irom0text.bin @ 0x10000 */
|
||||
|
||||
/*
|
||||
* Flash map for 512KB flash.
|
||||
*
|
||||
* There is 0x8000 (32KB) available between 0x8000 and 0x10000.
|
||||
*
|
||||
* Note: IROM images starts at 0x10000 (ignore Makefile output which says 0x40000.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
dport0_0_seg : org = 0x3FF00000, len = 0x10
|
||||
dram0_0_seg : org = 0x3FFE8000, len = 0x18000
|
||||
iram1_0_seg : org = 0x40100000, len = 0x8000
|
||||
irom0_0_seg : org = 0x40210000, len = 0x6C000
|
||||
}
|
||||
|
||||
INCLUDE "../ld/eagle.app.v6.common.ld"
|
||||
@@ -0,0 +1,56 @@
|
||||
This is a simple project template.
|
||||
|
||||
sample_lib is an example for multi-level folder Makefile, notice the folder structure and each Makefile, you can get the clue.
|
||||
|
||||
|
||||
HOWTO:
|
||||
1. Copy this folder to anywhere.
|
||||
Example:
|
||||
Copy to ~/workspace/project_template
|
||||
You can rename this folder as you like.
|
||||
|
||||
2. Export SDK_PATH and BIN_PATH.
|
||||
Example:
|
||||
Your SDK path is ~/esp_iot_rtos_sdk, and want generate bin at ~/esp8266_bin.
|
||||
Do follow steps:
|
||||
1>. export SDK_PATH=~/esp_iot_rtos_sdk
|
||||
2>. export BIN_PATH=~/esp8266_bin
|
||||
SDK and project are seperate, you can update SDK without change your project.
|
||||
|
||||
3. Enter project_template folder, run ./gen_misc.sh, and follow the tips and steps.
|
||||
|
||||
|
||||
Compile Options:
|
||||
(1) COMPILE
|
||||
Possible value: xcc
|
||||
Default value:
|
||||
If not set, use gcc by default.
|
||||
|
||||
(2) BOOT
|
||||
Possible value: none/old/new
|
||||
none: no need boot
|
||||
old: use boot_v1.1
|
||||
new: use boot_v1.2
|
||||
Default value: new
|
||||
|
||||
(3) APP
|
||||
Possible value: 0/1/2
|
||||
0: original mode, generate eagle.app.v6.flash.bin and eagle.app.v6.irom0text.bin
|
||||
1: generate user1
|
||||
2: generate user2
|
||||
Default value: 0
|
||||
|
||||
(3) SPI_SPEED
|
||||
Possible value: 20/26.7/40/80
|
||||
Default value: 40
|
||||
|
||||
(4) SPI_MODE
|
||||
Possible value: QIO/QOUT/DIO/DOUT
|
||||
Default value: QIO
|
||||
|
||||
(4) SPI_SIZE_MAP
|
||||
Possible value: 0/2/3/4/5/6
|
||||
Default value: 0
|
||||
|
||||
For example:
|
||||
make COMPILE=gcc BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=0
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libuser.a
|
||||
endif
|
||||
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
DEFINES += -DMG_DISABLE_DAV -DMG_DISABLE_SYNC_RESOLVER -DMG_DISABLE_CGI \
|
||||
-DMG_DISABLE_SOCKETPAIR -DMG_DISABLE_DIRECTORY_LISTING \
|
||||
-DMG_MAX_HTTP_HEADERS=20 -DMG_MAX_HTTP_REQUEST_SIZE=1024 \
|
||||
-DMG_MAX_PATH=40 -DMG_MAX_HTTP_SEND_IOBUF=1024 \
|
||||
-DMG_ESP8266 -DRTOS_SDK -DMG_LWIP
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES += -I ./
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_common.h"
|
||||
|
||||
/* Makes fprintf(stdout) and stderr work. */
|
||||
_ssize_t _write_r(struct _reent *r, int fd, void *buf, size_t len) {
|
||||
if (fd == 1 || fd == 2) {
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++) {
|
||||
os_putc(((char *) buf)[i]);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* You'll need to implement _open_r and friends if you want file operations. See
|
||||
* https://github.com/cesanta/smart.js/blob/master/platforms/esp8266/user/esp_fs.c
|
||||
* for SPIFFS-based implementation.
|
||||
*/
|
||||
|
||||
void abort(void) __attribute__((no_instrument_function));
|
||||
void abort(void) {
|
||||
/* cause an unaligned access exception, that will drop you into gdb */
|
||||
*(int *) 1 = 1;
|
||||
while (1)
|
||||
; /* avoid gcc warning because stdlib abort() has noreturn attribute */
|
||||
}
|
||||
|
||||
void _exit(int status) {
|
||||
printf("_exit(%d)\n", status);
|
||||
abort();
|
||||
}
|
||||
|
||||
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
||||
uint32_t time = system_get_time();
|
||||
tp->tv_sec = time / 1000000;
|
||||
tp->tv_usec = time % 1000000;
|
||||
return 0;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "esp_common.h"
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#define AP_SSID "Mongoose"
|
||||
#define AP_PASS "Mongoose"
|
||||
#define AP_CHAN 9
|
||||
#define MG_LISTEN_ADDR "80"
|
||||
|
||||
#define MG_TASK_STACK_SIZE 2048
|
||||
#define MG_TASK_PRIORITY 1
|
||||
|
||||
void uart_div_modify(int uart_no, unsigned int freq);
|
||||
|
||||
void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||
static const char *reply_fmt =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n"
|
||||
"Hello %s\n";
|
||||
LOG(LL_DEBUG, ("conn %p ev %d", nc, ev));
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_ACCEPT: {
|
||||
char addr[32];
|
||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||
LOG(LL_INFO, ("Connection %p from %s", nc, addr));
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
char addr[32];
|
||||
struct http_message *hm = (struct http_message *) p;
|
||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||
LOG(LL_INFO,
|
||||
("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
|
||||
hm->method.p, (int) hm->uri.len, hm->uri.p));
|
||||
mg_printf(nc, reply_fmt, addr);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
LOG(LL_INFO, ("Connection %p closed", nc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_ap() {
|
||||
struct softap_config cfg;
|
||||
|
||||
wifi_set_opmode_current(SOFTAP_MODE);
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
strcpy((char *) cfg.ssid, AP_SSID);
|
||||
strcpy((char *) cfg.password, AP_PASS);
|
||||
cfg.ssid_len = strlen((const char *) cfg.ssid);
|
||||
cfg.authmode =
|
||||
strlen((const char *) cfg.password) ? AUTH_WPA2_PSK : AUTH_OPEN;
|
||||
cfg.channel = AP_CHAN;
|
||||
cfg.ssid_hidden = 0;
|
||||
cfg.max_connection = 10;
|
||||
cfg.beacon_interval = 100; /* ms */
|
||||
|
||||
LOG(LL_INFO, ("Setting up AP '%s' on channel %d", cfg.ssid, cfg.channel));
|
||||
wifi_softap_set_config_current(&cfg);
|
||||
}
|
||||
|
||||
static void mg_task(void *arg) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
|
||||
cs_log_set_level(LL_INFO);
|
||||
|
||||
LOG(LL_INFO, ("SDK version: %s", system_get_sdk_version()));
|
||||
setup_ap();
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
nc = mg_bind(&mgr, MG_LISTEN_ADDR, ev_handler);
|
||||
if (nc == NULL) {
|
||||
LOG(LL_ERROR, ("Error setting up listener!"));
|
||||
return;
|
||||
}
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
|
||||
while (1) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
xTaskHandle s_mg_task_handle;
|
||||
|
||||
void user_init(void) {
|
||||
uart_div_modify(0, UART_CLK_FREQ / 115200);
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
xTaskCreate(mg_task, (const signed char *) "mongoose", MG_TASK_STACK_SIZE,
|
||||
NULL, MG_TASK_PRIORITY, &s_mg_task_handle);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ static uint8_t board_ip[] = {192, 168, 10, 177};
|
||||
#ifdef WIFI_CC3000
|
||||
static uint8_t subnet_mask[] = {255, 255, 255, 0};
|
||||
static uint8_t gateway[] = {192, 168, 10, 254};
|
||||
static uint8_t dmg_ip[] = {192, 168, 10, 254};
|
||||
static uint8_t dns_ip[] = {192, 168, 10, 254};
|
||||
|
||||
static const char *wlan_ssid = "mynetwork";
|
||||
static const char *wlan_pwd = "mypassword";
|
||||
@@ -117,7 +117,7 @@ void setup()
|
||||
avr_netinit(board_mac, board_ip);
|
||||
#elif defined(WIFI_CC3000)
|
||||
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
|
||||
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dmg_ip)) != 0) {
|
||||
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dns_ip)) != 0) {
|
||||
Serial.println("Initialization error, check network settings");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ static uint8_t board_ip[] = {192, 168, 10, 8};
|
||||
#ifdef WIFI_CC3000
|
||||
static uint8_t subnet_mask[] = {255, 255, 255, 0};
|
||||
static uint8_t gateway[] = {192, 168, 10, 254};
|
||||
static uint8_t dmg_ip[] = {192, 168, 10, 254};
|
||||
static uint8_t dns_ip[] = {192, 168, 10, 254};
|
||||
|
||||
static const char *wlan_ssid = "mynetwork";
|
||||
static const char *wlan_pwd = "mypassword";
|
||||
@@ -116,7 +116,7 @@ void setup() {
|
||||
avr_netinit(board_mac, board_ip);
|
||||
#elif defined(WIFI_CC3000)
|
||||
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
|
||||
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dmg_ip)) != 0) {
|
||||
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dns_ip)) != 0) {
|
||||
Serial.println("Initialization error, check network settings");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
PROG = big_upload
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This example demonstrates how to handle very large requests without keeping
|
||||
// them in memory.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
|
||||
struct file_writer_data {
|
||||
FILE *fp;
|
||||
size_t bytes_left;
|
||||
};
|
||||
|
||||
static void handle_request(struct mg_connection *nc) {
|
||||
// This handler gets called only when we don't reset proto_handler in
|
||||
// handle_recv, so we just return a page unconditionally.
|
||||
mg_printf(nc, "%s",
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/upload\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
|
||||
static void handle_recv(struct mg_connection *nc) {
|
||||
struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
|
||||
|
||||
if (data == NULL) {
|
||||
// This is a new connection, try to parse HTTP request.
|
||||
struct http_message hm;
|
||||
int req_len = mg_parse_http(nc->recv_mbuf.buf, nc->recv_mbuf.len, &hm,
|
||||
1 /* is_req */);
|
||||
|
||||
if (req_len < 0 ||
|
||||
(req_len == 0 && nc->recv_mbuf.len >= MG_MAX_HTTP_REQUEST_SIZE)) {
|
||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
} else if (req_len == 0) {
|
||||
// Request is not complete yet, do nothing.
|
||||
} else if (mg_vcasecmp(&hm.method, "POST") == 0 &&
|
||||
mg_vcmp(&hm.uri, "/upload") == 0) {
|
||||
// This is the request that we don't want to buffer in memory.
|
||||
|
||||
if (hm.body.len == (size_t) ~0 || hm.body.len == 0) {
|
||||
mg_printf(nc, "%s",
|
||||
"HTTP/1.1 411 Content-Length required\r\n"
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset proto_handler so Mongoose's http_handler() won't get confused
|
||||
// once we start removing data from the buffer.
|
||||
nc->proto_handler = NULL;
|
||||
|
||||
// Headers will be inaccessible later, so put everything we need into
|
||||
// user_data.
|
||||
data = calloc(1, sizeof(struct file_writer_data));
|
||||
data->bytes_left = hm.body.len;
|
||||
data->fp = tmpfile();
|
||||
if (data->fp == NULL) {
|
||||
mg_printf(nc, "%s",
|
||||
"HTTP/1.1 500 Failed to open a file\r\n"
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
nc->user_data = (void *) data;
|
||||
|
||||
// Remove the headers from the buffer.
|
||||
mbuf_remove(&nc->recv_mbuf, hm.body.p - nc->recv_mbuf.buf);
|
||||
|
||||
// Invoke itself again to write the piece of the body that is already in
|
||||
// the buffer.
|
||||
handle_recv(nc);
|
||||
}
|
||||
} else { // data != NULL
|
||||
size_t to_write = data->bytes_left, written = 0;
|
||||
if (nc->recv_mbuf.len < to_write) to_write = nc->recv_mbuf.len;
|
||||
written = fwrite(nc->recv_mbuf.buf, 1, to_write, data->fp);
|
||||
mbuf_remove(&nc->recv_mbuf, written);
|
||||
data->bytes_left -= written;
|
||||
if (data->bytes_left <= 0) {
|
||||
// Request is complete, do something meaningful here.
|
||||
|
||||
mg_printf(nc,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
"Written %ld of POST data to a temp file\n\n",
|
||||
(long) ftell(data->fp));
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
// handle_close will free the resources.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we free all allocated resources
|
||||
static void handle_close(struct mg_connection *nc) {
|
||||
struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
|
||||
|
||||
if (data != NULL) {
|
||||
fclose(data->fp);
|
||||
free(data);
|
||||
nc->user_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
(void) ev_data;
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
// Invoked when the full HTTP request is in the buffer (including body).
|
||||
handle_request(nc);
|
||||
break;
|
||||
case MG_EV_RECV:
|
||||
// Invoked every time new data arrives.
|
||||
handle_recv(nc);
|
||||
break;
|
||||
case MG_EV_CLOSE:
|
||||
handle_close(nc);
|
||||
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);
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -17,20 +17,20 @@ static in_addr_t s_our_ip_addr;
|
||||
static const char *s_listening_addr = "udp://:5533";
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct mg_dmg_message *msg;
|
||||
struct mg_dmg_resource_record *rr;
|
||||
struct mg_dmg_reply reply;
|
||||
struct mg_dns_message *msg;
|
||||
struct mg_dns_resource_record *rr;
|
||||
struct mg_dns_reply reply;
|
||||
int i;
|
||||
|
||||
switch (ev) {
|
||||
case MG_DNS_MESSAGE:
|
||||
msg = (struct mg_dmg_message *) ev_data;
|
||||
reply = mg_dmg_create_reply(&nc->send_mbuf, msg);
|
||||
msg = (struct mg_dns_message *) ev_data;
|
||||
reply = mg_dns_create_reply(&nc->send_mbuf, msg);
|
||||
|
||||
for (i = 0; i < msg->num_questions; i++) {
|
||||
rr = &msg->questions[i];
|
||||
if (rr->rtype == MG_DNS_A_RECORD) {
|
||||
mg_dmg_reply_record(&reply, rr, NULL, rr->rtype, 3600,
|
||||
mg_dns_reply_record(&reply, rr, NULL, rr->rtype, 3600,
|
||||
&s_our_ip_addr, 4);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
* See http://goo.gl/QWvufr for a distinction between NXDOMAIN and NODATA.
|
||||
*/
|
||||
|
||||
mg_dmg_send_reply(nc, &reply);
|
||||
mg_dns_send_reply(nc, &reply);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||
if (ev == MG_EV_HTTP_REQUEST) {
|
||||
mg_serve_http(nc, p, s_http_server_opts); // Serves static content
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - static content only</h1>
|
||||
|
||||
<p>
|
||||
This page shows initial steps on how to make a device connected.
|
||||
Mongoose serves static content from the <code>web_root</code>
|
||||
directory. Images, CSS, HTML, JavaScript files could be dropped into the
|
||||
<code>web_root</code> directory - and they'll be served happily
|
||||
by Mongoose.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Below is a placeholder for the
|
||||
settings section, and a real-time dashboard, which will be
|
||||
implemented in the following examples.
|
||||
Both settings section, and a dashboard section, could be organised
|
||||
differently on this UI - for example, in different tabs. But, to
|
||||
make the demonstration clearer, we show everything on one page.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,31 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #36c;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - settings section</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here, we create a settings section
|
||||
that expose some internal device variables, and allow user to change
|
||||
them. They could be some stored in a database, in a file, or in
|
||||
firmware RAM. In this example, we store them in RAM, and use two
|
||||
variables, one integer and one string. We use SSI
|
||||
<code><!--#call parameter_name --></code> directive to fetch
|
||||
variable values from Mongoose.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,67 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||
// Generate random value, as an example of changing CPU usage
|
||||
// Getting real CPU usage depends on the OS.
|
||||
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
// Use chunked encoding in order to avoid calculating Content-Length
|
||||
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
|
||||
// Output JSON object which holds CPU usage data
|
||||
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||
|
||||
// Send empty chunk, the end of response
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||
handle_get_cpu_usage(nc);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - RESTful call</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here we add a RESTful call
|
||||
to the Mongoose to show CPU usage stats on a dashboard.
|
||||
RESTful call is done using Jquery's <code>ajax</code> call.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
|
||||
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
|
||||
|
||||
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var
Line truncated
|
||||
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,12 @@
|
||||
$(document).ready(function() {
|
||||
// Start 1-second timer to call RESTful endpoint
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
url: '/get_cpu_usage',
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
$('#cpu_usage').text(json.result + '% ');
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||
// Generate random value, as an example of changing CPU usage
|
||||
// Getting real CPU usage depends on the OS.
|
||||
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
// Use chunked encoding in order to avoid calculating Content-Length
|
||||
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
|
||||
// Output JSON object which holds CPU usage data
|
||||
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||
|
||||
// Send empty chunk, the end of response
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||
handle_get_cpu_usage(nc);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void push_data_to_all_websocket_connections(struct mg_mgr *m) {
|
||||
struct mg_connection *c;
|
||||
int memory_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
for (c = mg_next(m, NULL); c != NULL; c = mg_next(m, c)) {
|
||||
if (c->flags & MG_F_IS_WEBSOCKET) {
|
||||
mg_printf_websocket_frame(c, WEBSOCKET_OP_TEXT, "%d", memory_usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
static time_t last_time;
|
||||
time_t now = time(NULL);
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
if (now - last_time > 0) {
|
||||
push_data_to_all_websocket_connections(&mgr);
|
||||
last_time = now;
|
||||
}
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||
<script src="jquery.flot.min.js"></script>
|
||||
<script src="jquery.flot.time.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - Websocket push</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here we add a real-time Websocket
|
||||
server push, and update dashboard graph as data arrives.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||
<div id="graphs"></div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
|
||||
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
|
||||
|
||||
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var
Line truncated
|
||||
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Javascript plotting library for jQuery, version 0.8.3.
|
||||
|
||||
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
*/
|
||||
(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;if(!$.fn.detach){$.fn.detach=function(){return this.each(function(){if(this.parentNode){this.parentNode.removeChild(this)}})}}function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){vaLine truncated
|
||||
if(yrange.from==null)yrange.from=yrange.axis.min;if(yrange.to==null)yrange.to=yrange.axis.max;if(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);var xequal=xrange.from===xrange.to,yequal=yrange.from===yrange.to;if(xequal&&yequal){continue}xrange.from=Math.floor(xrange.axis.p2c(xrange.from));xrange.to=Math.floor(xrange.axis.p2c(xrange.to));yrange.from=Math.floor(yrange.axis.p2c(yrange.from));yrange.to=Math.floor(yrange.axis.p2c(yrange.to));if(xequal||yequal){var lineWidth=m.lineWidth||options.grid.markingsLineWidth,subPixel=lineWidth%2?.5:0;ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=lineWidth;if(xequal){ctx.moveTo(xrange.to+subPixel,yrange.from);ctx.lineTo(xrange.to+subPixel,yrange.to)}else{ctx.moveTo(xrange.from,yrange.to+subPixel);ctx.lineTo(xrange.to,yrange.to+subPixel)}ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;j<axes.length;++j){var axis=axes[j],box=axis.box,t=axis.tickLength,x,y,xoff,yoff;if(!axis.show||axis.ticks.length==0)continue;ctx.lineWidth=1;if(axis.direction=="x"){x=0;if(t=="full")y=axis.position=="top"?0:plotHeight;else y=box.top-plotOffset.top+(axis.position=="top"?box.height:0)}else{y=0;if(t=="full")x=axis.position=="left"?0:plotWidth;else x=box.left-plotOffset.left+(axis.position=="left"?box.width:0)}if(!axis.innermost){ctx.strokeStyle=axis.options.color;ctx.beginPath();xoff=yoff=0;if(axis.direction=="x")xoff=plotWidth+1;else yoff=plotHeight+1;if(ctx.lineWidth==1){if(axis.direction=="x"){y=Math.floor(y)+.5}else{x=Math.floor(x)+.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff);ctx.stroke()}ctx.strokeStyle=axis.options.tickColor;ctx.beginPath();for(i=0;i<axis.ticks.length;++i){var v=axis.ticks[i].v;xoff=yoff=0;if(isNaN(v)||v<axis.min||v>axis.max||t=="full"&&(typeof bw=="object"&&bw[axis.position]>0||bw>0)&&(v==axis.min||v==axis.max))continue;if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top")yoff=-yoff}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left")xoff=-xoff}if(ctx.lineWidth==1){if(axis.direction=="x")x=Math.floor(x)+.5;else y=Math.floor(y)+.5}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0)return;for(var i=0;i<axis.ticks.length;++i){tick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max)continue;if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series)}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;if(y1<=y2&&y1<axisy.min){if(y2<axLine truncated
|
||||
@@ -0,0 +1,432 @@
|
||||
/* Pretty handling of time axes.
|
||||
|
||||
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||
API.txt for details.
|
||||
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var options = {
|
||||
xaxis: {
|
||||
timezone: null, // "browser" for local to the client or timezone for timezone-js
|
||||
timeformat: null, // format string to use
|
||||
twelveHourClock: false, // 12 or 24 time in time mode
|
||||
monthNames: null // list of names of months
|
||||
}
|
||||
};
|
||||
|
||||
// round to nearby lower multiple of base
|
||||
|
||||
function floorInBase(n, base) {
|
||||
return base * Math.floor(n / base);
|
||||
}
|
||||
|
||||
// Returns a string with the date d formatted according to fmt.
|
||||
// A subset of the Open Group's strftime format is supported.
|
||||
|
||||
function formatDate(d, fmt, monthNames, dayNames) {
|
||||
|
||||
if (typeof d.strftime == "function") {
|
||||
return d.strftime(fmt);
|
||||
}
|
||||
|
||||
var leftPad = function(n, pad) {
|
||||
n = "" + n;
|
||||
pad = "" + (pad == null ? "0" : pad);
|
||||
return n.length == 1 ? pad + n : n;
|
||||
};
|
||||
|
||||
var r = [];
|
||||
var escape = false;
|
||||
var hours = d.getHours();
|
||||
var isAM = hours < 12;
|
||||
|
||||
if (monthNames == null) {
|
||||
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
}
|
||||
|
||||
if (dayNames == null) {
|
||||
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
var hours12;
|
||||
|
||||
if (hours > 12) {
|
||||
hours12 = hours - 12;
|
||||
} else if (hours == 0) {
|
||||
hours12 = 12;
|
||||
} else {
|
||||
hours12 = hours;
|
||||
}
|
||||
|
||||
for (var i = 0; i < fmt.length; ++i) {
|
||||
|
||||
var c = fmt.charAt(i);
|
||||
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
case 'a': c = "" + dayNames[d.getDay()]; break;
|
||||
case 'b': c = "" + monthNames[d.getMonth()]; break;
|
||||
case 'd': c = leftPad(d.getDate()); break;
|
||||
case 'e': c = leftPad(d.getDate(), " "); break;
|
||||
case 'h': // For back-compat with 0.7; remove in 1.0
|
||||
case 'H': c = leftPad(hours); break;
|
||||
case 'I': c = leftPad(hours12); break;
|
||||
case 'l': c = leftPad(hours12, " "); break;
|
||||
case 'm': c = leftPad(d.getMonth() + 1); break;
|
||||
case 'M': c = leftPad(d.getMinutes()); break;
|
||||
// quarters not in Open Group's strftime specification
|
||||
case 'q':
|
||||
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
|
||||
case 'S': c = leftPad(d.getSeconds()); break;
|
||||
case 'y': c = leftPad(d.getFullYear() % 100); break;
|
||||
case 'Y': c = "" + d.getFullYear(); break;
|
||||
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
|
||||
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
|
||||
case 'w': c = "" + d.getDay(); break;
|
||||
}
|
||||
r.push(c);
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == "%") {
|
||||
escape = true;
|
||||
} else {
|
||||
r.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.join("");
|
||||
}
|
||||
|
||||
// To have a consistent view of time-based data independent of which time
|
||||
// zone the client happens to be in we need a date-like object independent
|
||||
// of time zones. This is done through a wrapper that only calls the UTC
|
||||
// versions of the accessor methods.
|
||||
|
||||
function makeUtcWrapper(d) {
|
||||
|
||||
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
|
||||
sourceObj[sourceMethod] = function() {
|
||||
return targetObj[targetMethod].apply(targetObj, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var utc = {
|
||||
date: d
|
||||
};
|
||||
|
||||
// support strftime, if found
|
||||
|
||||
if (d.strftime != undefined) {
|
||||
addProxyMethod(utc, "strftime", d, "strftime");
|
||||
}
|
||||
|
||||
addProxyMethod(utc, "getTime", d, "getTime");
|
||||
addProxyMethod(utc, "setTime", d, "setTime");
|
||||
|
||||
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
|
||||
|
||||
for (var p = 0; p < props.length; p++) {
|
||||
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
|
||||
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
|
||||
}
|
||||
|
||||
return utc;
|
||||
};
|
||||
|
||||
// select time zone strategy. This returns a date-like object tied to the
|
||||
// desired timezone
|
||||
|
||||
function dateGenerator(ts, opts) {
|
||||
if (opts.timezone == "browser") {
|
||||
return new Date(ts);
|
||||
} else if (!opts.timezone || opts.timezone == "utc") {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
|
||||
var d = new timezoneJS.Date();
|
||||
// timezone-js is fickle, so be sure to set the time zone before
|
||||
// setting the time.
|
||||
d.setTimezone(opts.timezone);
|
||||
d.setTime(ts);
|
||||
return d;
|
||||
} else {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
}
|
||||
}
|
||||
|
||||
// map of app. size of time units in milliseconds
|
||||
|
||||
var timeUnitSize = {
|
||||
"second": 1000,
|
||||
"minute": 60 * 1000,
|
||||
"hour": 60 * 60 * 1000,
|
||||
"day": 24 * 60 * 60 * 1000,
|
||||
"month": 30 * 24 * 60 * 60 * 1000,
|
||||
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
|
||||
"year": 365.2425 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
|
||||
// the allowed tick sizes, after 1 year we use
|
||||
// an integer algorithm
|
||||
|
||||
var baseSpec = [
|
||||
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
|
||||
[30, "second"],
|
||||
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
|
||||
[30, "minute"],
|
||||
[1, "hour"], [2, "hour"], [4, "hour"],
|
||||
[8, "hour"], [12, "hour"],
|
||||
[1, "day"], [2, "day"], [3, "day"],
|
||||
[0.25, "month"], [0.5, "month"], [1, "month"],
|
||||
[2, "month"]
|
||||
];
|
||||
|
||||
// we don't know which variant(s) we'll need yet, but generating both is
|
||||
// cheap
|
||||
|
||||
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
|
||||
[1, "year"]]);
|
||||
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
|
||||
[1, "year"]]);
|
||||
|
||||
function init(plot) {
|
||||
plot.hooks.processOptions.push(function (plot, options) {
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
|
||||
var opts = axis.options;
|
||||
|
||||
if (opts.mode == "time") {
|
||||
axis.tickGenerator = function(axis) {
|
||||
|
||||
var ticks = [];
|
||||
var d = dateGenerator(axis.min, opts);
|
||||
var minSize = 0;
|
||||
|
||||
// make quarter use a possibility if quarters are
|
||||
// mentioned in either of these options
|
||||
|
||||
var spec = (opts.tickSize && opts.tickSize[1] ===
|
||||
"quarter") ||
|
||||
(opts.minTickSize && opts.minTickSize[1] ===
|
||||
"quarter") ? specQuarters : specMonths;
|
||||
|
||||
if (opts.minTickSize != null) {
|
||||
if (typeof opts.tickSize == "number") {
|
||||
minSize = opts.tickSize;
|
||||
} else {
|
||||
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < spec.length - 1; ++i) {
|
||||
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
|
||||
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
|
||||
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var size = spec[i][0];
|
||||
var unit = spec[i][1];
|
||||
|
||||
// special-case the possibility of several years
|
||||
|
||||
if (unit == "year") {
|
||||
|
||||
// if given a minTickSize in years, just use it,
|
||||
// ensuring that it's an integer
|
||||
|
||||
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
|
||||
size = Math.floor(opts.minTickSize[0]);
|
||||
} else {
|
||||
|
||||
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
|
||||
var norm = (axis.delta / timeUnitSize.year) / magn;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
}
|
||||
|
||||
// minimum size for years is 1
|
||||
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
axis.tickSize = opts.tickSize || [size, unit];
|
||||
var tickSize = axis.tickSize[0];
|
||||
unit = axis.tickSize[1];
|
||||
|
||||
var step = tickSize * timeUnitSize[unit];
|
||||
|
||||
if (unit == "second") {
|
||||
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
|
||||
} else if (unit == "minute") {
|
||||
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
|
||||
} else if (unit == "hour") {
|
||||
d.setHours(floorInBase(d.getHours(), tickSize));
|
||||
} else if (unit == "month") {
|
||||
d.setMonth(floorInBase(d.getMonth(), tickSize));
|
||||
} else if (unit == "quarter") {
|
||||
d.setMonth(3 * floorInBase(d.getMonth() / 3,
|
||||
tickSize));
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
|
||||
}
|
||||
|
||||
// reset smaller components
|
||||
|
||||
d.setMilliseconds(0);
|
||||
|
||||
if (step >= timeUnitSize.minute) {
|
||||
d.setSeconds(0);
|
||||
}
|
||||
if (step >= timeUnitSize.hour) {
|
||||
d.setMinutes(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day) {
|
||||
d.setHours(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day * 4) {
|
||||
d.setDate(1);
|
||||
}
|
||||
if (step >= timeUnitSize.month * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 3));
|
||||
}
|
||||
if (step >= timeUnitSize.quarter * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 6));
|
||||
}
|
||||
if (step >= timeUnitSize.year) {
|
||||
d.setMonth(0);
|
||||
}
|
||||
|
||||
var carry = 0;
|
||||
var v = Number.NaN;
|
||||
var prev;
|
||||
|
||||
do {
|
||||
|
||||
prev = v;
|
||||
v = d.getTime();
|
||||
ticks.push(v);
|
||||
|
||||
if (unit == "month" || unit == "quarter") {
|
||||
if (tickSize < 1) {
|
||||
|
||||
// a bit complicated - we'll divide the
|
||||
// month/quarter up but we need to take
|
||||
// care of fractions so we don't end up in
|
||||
// the middle of a day
|
||||
|
||||
d.setDate(1);
|
||||
var start = d.getTime();
|
||||
d.setMonth(d.getMonth() +
|
||||
(unit == "quarter" ? 3 : 1));
|
||||
var end = d.getTime();
|
||||
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
|
||||
carry = d.getHours();
|
||||
d.setHours(0);
|
||||
} else {
|
||||
d.setMonth(d.getMonth() +
|
||||
tickSize * (unit == "quarter" ? 3 : 1));
|
||||
}
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(d.getFullYear() + tickSize);
|
||||
} else {
|
||||
d.setTime(v + step);
|
||||
}
|
||||
} while (v < axis.max && v != prev);
|
||||
|
||||
return ticks;
|
||||
};
|
||||
|
||||
axis.tickFormatter = function (v, axis) {
|
||||
|
||||
var d = dateGenerator(v, axis.options);
|
||||
|
||||
// first check global format
|
||||
|
||||
if (opts.timeformat != null) {
|
||||
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
|
||||
}
|
||||
|
||||
// possibly use quarters if quarters are mentioned in
|
||||
// any of these places
|
||||
|
||||
var useQuarters = (axis.options.tickSize &&
|
||||
axis.options.tickSize[1] == "quarter") ||
|
||||
(axis.options.minTickSize &&
|
||||
axis.options.minTickSize[1] == "quarter");
|
||||
|
||||
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
|
||||
var span = axis.max - axis.min;
|
||||
var suffix = (opts.twelveHourClock) ? " %p" : "";
|
||||
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
|
||||
var fmt;
|
||||
|
||||
if (t < timeUnitSize.minute) {
|
||||
fmt = hourCode + ":%M:%S" + suffix;
|
||||
} else if (t < timeUnitSize.day) {
|
||||
if (span < 2 * timeUnitSize.day) {
|
||||
fmt = hourCode + ":%M" + suffix;
|
||||
} else {
|
||||
fmt = "%b %d " + hourCode + ":%M" + suffix;
|
||||
}
|
||||
} else if (t < timeUnitSize.month) {
|
||||
fmt = "%b %d";
|
||||
} else if ((useQuarters && t < timeUnitSize.quarter) ||
|
||||
(!useQuarters && t < timeUnitSize.year)) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "%b";
|
||||
} else {
|
||||
fmt = "%b %Y";
|
||||
}
|
||||
} else if (useQuarters && t < timeUnitSize.year) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "Q%q";
|
||||
} else {
|
||||
fmt = "Q%q %Y";
|
||||
}
|
||||
} else {
|
||||
fmt = "%Y";
|
||||
}
|
||||
|
||||
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
|
||||
|
||||
return rt;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'time',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
// Time-axis support used to be in Flot core, which exposed the
|
||||
// formatDate function on the plot object. Various plugins depend
|
||||
// on the function, so we need to re-expose it here.
|
||||
|
||||
$.plot.formatDate = formatDate;
|
||||
$.plot.dateGenerator = dateGenerator;
|
||||
|
||||
})(jQuery);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,75 @@
|
||||
$(document).ready(function() {
|
||||
// Start 1-second timer to call RESTful endpoint
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
url: '/get_cpu_usage',
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
$('#cpu_usage').text(json.result + '% ');
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Initialize graph
|
||||
var $c = $('<div class="graph"/>').appendTo('#graphs');
|
||||
var dur = 120;
|
||||
var past = Date.now() - dur * 1000;
|
||||
var options = {
|
||||
lines: {
|
||||
fill: true,
|
||||
lineWidth: 0,
|
||||
fillColor: {colors: [ { opacity: 0 }, { opacity: 1 } ] }
|
||||
},
|
||||
grid: { borderWidth: 1, borderColor: '#ccc'},
|
||||
xaxis: { mode: 'time', ticks: 5 },
|
||||
legend: { labelBoxBorderColor: '#fff' },
|
||||
colors: [ '#fec', '#396', '#e39', '9e2' ],
|
||||
hooks: {
|
||||
draw: [function(plot, canvas) {
|
||||
canvas.font = '13px sans-serif';
|
||||
canvas.fillStyle = '#aaa';
|
||||
canvas.fillText('Resource Usage', 35, 25);
|
||||
}]
|
||||
}
|
||||
};
|
||||
var plot = $.plot($c, [], options);
|
||||
|
||||
var updateGraph = function(counter) {
|
||||
var data = plot.getData();
|
||||
var now = Date.now();
|
||||
var oldest = now - dur * 1000;
|
||||
data[0] = {
|
||||
show: false,
|
||||
data: [[oldest, null], [now, null]]
|
||||
};
|
||||
|
||||
console.log(data);
|
||||
|
||||
// Remove old points
|
||||
$.each(data, function(di, d) {
|
||||
while (d.data.length > 0 && d.data[0][0] < oldest) {
|
||||
d.data.shift();
|
||||
}
|
||||
});
|
||||
|
||||
// Add new points
|
||||
var new_datapoint = [now, counter];
|
||||
if (data[1]) {
|
||||
data[1].label = 'memory';
|
||||
data[1].data.push(new_datapoint);
|
||||
} else {
|
||||
data[1] = [{ data: [new_datapoint] }];
|
||||
}
|
||||
|
||||
// Redraw the graph
|
||||
plot.setData(data);
|
||||
plot.setupGrid();
|
||||
plot.draw();
|
||||
};
|
||||
|
||||
// Create Websocket connection. For simplicity, no reconnect logic is here.
|
||||
var ws = new WebSocket('ws://' + location.host);
|
||||
ws.onmessage = function(ev) {
|
||||
updateGraph(ev.data);
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#graphs {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.graph {
|
||||
min-height: 12em;
|
||||
border: 0px solid #ccc;
|
||||
position: relative;
|
||||
margin: 0 1em 1em 0;
|
||||
width: 36em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.graph_title {
|
||||
position: absolute;
|
||||
top: 0.5em; left: 1em;
|
||||
color: #ccc;
|
||||
font-weight: bold;
|
||||
z-index: 999;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
CFLAGS = -W -Wall -I../.. -Wno-unused-function $(CFLAGS_EXTRA) $(MODULE_CFLAGS)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\mongoose.c">
|
||||
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||
</ClCompile>
|
||||
<ClCompile Include="http_client.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\mongoose.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{BDF3FC6F-2658-4A27-846D-6A45FB53C602}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>http_client</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -24,7 +24,7 @@ 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 recieve a copy of the frame.
|
||||
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.
|
||||
|
||||
|
||||
@@ -454,7 +454,7 @@ grabbing application can handle. The device automatically attempts reconnecting.
|
||||
<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 recieve a copy of the frame.</p>
|
||||
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>
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{BDC33376-C2B4-4D19-8F6F-F88116F1DE9A}</ProjectGuid>
|
||||
<RootNamespace>restful_client</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);MG_ENABLE_THREADS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);MG_ENABLE_THREADS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\mongoose.c" />
|
||||
<ClCompile Include="restful_client.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\mongoose.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -8,6 +8,11 @@ else ifeq ($(SSL), krypton)
|
||||
CFLAGS += -DMG_ENABLE_SSL ../../../krypton/krypton.c
|
||||
endif
|
||||
|
||||
ifeq ($(JS), yes)
|
||||
V7_PATH = ../../deps/v7
|
||||
CFLAGS_EXTRA += -DMG_ENABLE_JAVASCRIPT -I $(V7_PATH) $(V7_PATH)/v7.c
|
||||
endif
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
|
||||
@@ -67,9 +67,14 @@ int main(int argc, char *argv[]) {
|
||||
s_http_port = argv[++i];
|
||||
} else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
|
||||
s_http_server_opts.auth_domain = argv[++i];
|
||||
#ifdef MG_ENABLE_JAVASCRIPT
|
||||
} else if (strcmp(argv[i], "-j") == 0 && i + 1 < argc) {
|
||||
const char *init_file = argv[++i];
|
||||
mg_enable_javascript(&mgr, v7_create(), init_file);
|
||||
#endif
|
||||
} else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
|
||||
s_http_server_opts.global_auth_file = argv[++i];
|
||||
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "-A") == 0 && i + 1 < argc) {
|
||||
s_http_server_opts.per_directory_auth_file = argv[++i];
|
||||
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
|
||||
s_http_server_opts.url_rewrites = argv[++i];
|
||||
@@ -81,6 +86,9 @@ int main(int argc, char *argv[]) {
|
||||
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
|
||||
ssl_cert = argv[++i];
|
||||
#endif
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: [%s]\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{26BC7405-4171-4B98-B5F2-B3159D4A8F5C}</ProjectGuid>
|
||||
<RootNamespace>restful_server</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);MG_ENABLE_THREADS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>./../../</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);MG_ENABLE_THREADS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\mongoose.c">
|
||||
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||
</ClCompile>
|
||||
<ClCompile Include="restful_server.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\mongoose.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+1
-1
@@ -4,7 +4,7 @@ CFLAGS = -W -Wall -I../.. -pthread -DMG_ENABLE_SSL -DMG_ENABLE_IPV6 -DMG_ENABLE_
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
$(CC) $(SOURCES) -g -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /DMG_ENABLE_SSL /MD /Fe$@
|
||||
|
||||
+2414
-1848
File diff suppressed because it is too large.
Load diff
+297
-73
@@ -20,7 +20,16 @@
|
||||
* license, as set out in <https://www.cesanta.com/license>.
|
||||
*/
|
||||
|
||||
#define MG_VERSION "6.0"
|
||||
#define MG_VERSION "6.1"
|
||||
|
||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||
#ifdef MG_LOCALS
|
||||
#include <mg_locals.h>
|
||||
#endif
|
||||
|
||||
#if defined(MG_ENABLE_DEBUG) && !defined(CS_ENABLE_DEBUG)
|
||||
#define CS_ENABLE_DEBUG
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 2015 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
@@ -68,6 +77,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
|
||||
* MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
|
||||
* MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
|
||||
* MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
|
||||
@@ -95,7 +105,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -129,6 +139,7 @@
|
||||
#define __func__ __FILE__ ":" STR(__LINE__)
|
||||
#endif
|
||||
#define snprintf _snprintf
|
||||
#define fileno _fileno
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) *1000)
|
||||
#define to64(x) _atoi64(x)
|
||||
@@ -141,7 +152,7 @@
|
||||
#endif
|
||||
#define random() rand()
|
||||
typedef int socklen_t;
|
||||
typedef char int8_t;
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
@@ -194,13 +205,17 @@ struct dirent *readdir(DIR *dir);
|
||||
#include <cc3200_libc.h>
|
||||
#include <cc3200_socket.h>
|
||||
|
||||
#elif /* not CC3200 */ defined(MG_ESP8266) && defined(RTOS_SDK)
|
||||
#elif /* not CC3200 */ defined(MG_LWIP)
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#if defined(MG_ESP8266) && defined(RTOS_SDK)
|
||||
#include <esp_libc.h>
|
||||
#define random() os_random()
|
||||
#endif
|
||||
|
||||
/* TODO(alashkin): check if zero is OK */
|
||||
#define SOMAXCONN 0
|
||||
#include <stdlib.h>
|
||||
@@ -218,8 +233,11 @@ struct dirent *readdir(DIR *dir);
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef LWIP_PROVIDE_ERRNO
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -233,7 +251,11 @@ struct dirent *readdir(DIR *dir);
|
||||
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define INT64_FMT PRId64
|
||||
#if defined(ESP8266) || defined(MG_ESP8266) || defined(MG_CC3200)
|
||||
#define SIZE_T_FMT "u"
|
||||
#else
|
||||
#define SIZE_T_FMT "zu"
|
||||
#endif
|
||||
#define to64(x) strtoll(x, NULL, 10)
|
||||
typedef int sock_t;
|
||||
typedef struct stat cs_stat_t;
|
||||
@@ -245,25 +267,55 @@ int64_t strtoll(const char *str, char **endptr, int base);
|
||||
#endif
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#define __DBG(x) \
|
||||
do { \
|
||||
printf("%-20s ", __func__); \
|
||||
printf x; \
|
||||
putchar('\n'); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
#ifdef MG_ENABLE_DEBUG
|
||||
#define DBG __DBG
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
#endif
|
||||
|
||||
#endif /* OSDEP_HEADER_INCLUDED */
|
||||
#ifndef _CS_DBG_H_
|
||||
#define _CS_DBG_H_
|
||||
|
||||
enum cs_log_level {
|
||||
LL_NONE = -1,
|
||||
LL_ERROR = 0,
|
||||
LL_WARN = 1,
|
||||
LL_INFO = 2,
|
||||
LL_DEBUG = 3,
|
||||
LL_VERBOSE_DEBUG = 4,
|
||||
|
||||
_LL_MIN = -2,
|
||||
_LL_MAX = 5,
|
||||
};
|
||||
|
||||
#ifndef CS_NDEBUG
|
||||
|
||||
extern enum cs_log_level s_cs_log_level;
|
||||
void cs_log_set_level(enum cs_log_level level);
|
||||
|
||||
void cs_log_printf(const char *fmt, ...);
|
||||
|
||||
#define LOG(l, x) \
|
||||
if (s_cs_log_level >= l) { \
|
||||
fprintf(stderr, "%-20s ", __func__); \
|
||||
cs_log_printf x; \
|
||||
}
|
||||
|
||||
#define DBG(x) \
|
||||
if (s_cs_log_level >= LL_VERBOSE_DEBUG) { \
|
||||
fprintf(stderr, "%-20s ", __func__); \
|
||||
cs_log_printf x; \
|
||||
}
|
||||
|
||||
#else /* NDEBUG */
|
||||
|
||||
#define cs_log_set_level(l)
|
||||
|
||||
#define LOG(l, x)
|
||||
#define DBG(x)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _CS_DBG_H_ */
|
||||
/*
|
||||
* Copyright (c) 2015 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
@@ -394,6 +446,22 @@ void MD5_Init(MD5_CTX *c);
|
||||
void MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len);
|
||||
void MD5_Final(unsigned char *md, MD5_CTX *c);
|
||||
|
||||
/*
|
||||
* Return stringified MD5 hash for NULL terminated list of strings.
|
||||
* Example:
|
||||
*
|
||||
* char buf[33];
|
||||
* cs_md5(buf, "foo", "bar", NULL);
|
||||
*/
|
||||
char *cs_md5(char buf[33], ...);
|
||||
|
||||
/*
|
||||
* Stringify binary data. Output buffer size must be 2 * size_of_input + 1
|
||||
* because each byte of input takes 2 bytes in string representation
|
||||
* plus 1 byte for the terminating \0 character.
|
||||
*/
|
||||
void cs_to_hex(char *to, const unsigned char *p, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
@@ -454,10 +522,13 @@ extern "C" {
|
||||
int c_snprintf(char *buf, size_t buf_size, const char *format, ...);
|
||||
int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap);
|
||||
|
||||
#if !(_XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L) && \
|
||||
!(__DARWIN_C_LEVEL >= 200809L) && !defined(RTOS_SDK) || \
|
||||
#if (!(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) && \
|
||||
!(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) && \
|
||||
!(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) && \
|
||||
!defined(RTOS_SDK)) || \
|
||||
defined(_WIN32)
|
||||
int strnlen(const char *s, size_t maxlen);
|
||||
#define _MG_PROVIDE_STRNLEN
|
||||
size_t strnlen(const char *s, size_t maxlen);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -562,6 +633,11 @@ int json_emit_va(char *buf, int buf_len, const char *fmt, va_list);
|
||||
#ifndef MG_NET_HEADER_INCLUDED
|
||||
#define MG_NET_HEADER_INCLUDED
|
||||
|
||||
#ifdef MG_ENABLE_JAVASCRIPT
|
||||
#define EXCLUDE_COMMON
|
||||
#include <v7.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MG_ENABLE_SSL
|
||||
#ifdef __APPLE__
|
||||
@@ -574,11 +650,11 @@ typedef void *SSL_CTX;
|
||||
#endif
|
||||
|
||||
#ifdef MG_USE_READ_WRITE
|
||||
#define MG_EV_RECV_FUNC(s, b, l, f) read(s, b, l)
|
||||
#define MG_EV_SEND_FUNC(s, b, l, f) write(s, b, l)
|
||||
#define MG_RECV_FUNC(s, b, l, f) read(s, b, l)
|
||||
#define MG_SEND_FUNC(s, b, l, f) write(s, b, l)
|
||||
#else
|
||||
#define MG_EV_RECV_FUNC(s, b, l, f) recv(s, b, l, f)
|
||||
#define MG_EV_SEND_FUNC(s, b, l, f) send(s, b, l, f)
|
||||
#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f)
|
||||
#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -628,6 +704,9 @@ struct mg_mgr {
|
||||
sock_t ctl[2]; /* Socketpair for mg_wakeup() */
|
||||
void *user_data; /* User data */
|
||||
void *mgr_data; /* Implementation-specific event manager's data. */
|
||||
#ifdef MG_ENABLE_JAVASCRIPT
|
||||
struct v7 *v7;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -638,7 +717,8 @@ struct mg_connection {
|
||||
struct mg_connection *listener; /* Set only for accept()-ed connections */
|
||||
struct mg_mgr *mgr; /* Pointer to containing manager */
|
||||
|
||||
sock_t sock; /* Socket to the remote peer */
|
||||
sock_t sock; /* Socket to the remote peer */
|
||||
int err;
|
||||
union socket_address sa; /* Remote peer address */
|
||||
size_t recv_mbuf_limit; /* Max size of recv buffer */
|
||||
struct mbuf recv_mbuf; /* Received data */
|
||||
@@ -667,10 +747,9 @@ struct mg_connection {
|
||||
|
||||
/* Flags that are settable by user */
|
||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||
#define MG_F_DONT_SEND (1 << 11) /* Do not send data to peer */
|
||||
#define MG_F_CLOSE_IMMEDIATELY (1 << 12) /* Disconnect */
|
||||
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 13) /* Websocket specific */
|
||||
#define MG_F_DELETE_CHUNK (1 << 14) /* HTTP specific */
|
||||
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
|
||||
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
|
||||
#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */
|
||||
|
||||
#define MG_F_USER_1 (1 << 20) /* Flags left for application */
|
||||
#define MG_F_USER_2 (1 << 21)
|
||||
@@ -707,6 +786,7 @@ void mg_mgr_free(struct mg_mgr *);
|
||||
*/
|
||||
time_t mg_mgr_poll(struct mg_mgr *, int milli);
|
||||
|
||||
#ifndef MG_DISABLE_SOCKETPAIR
|
||||
/*
|
||||
* Pass a message of a given length to all connections.
|
||||
*
|
||||
@@ -720,6 +800,7 @@ time_t mg_mgr_poll(struct mg_mgr *, int milli);
|
||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
||||
*/
|
||||
void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Iterate over all active connections.
|
||||
@@ -889,13 +970,17 @@ const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
|
||||
/*
|
||||
* Send data to the connection.
|
||||
*
|
||||
* Return number of written bytes. Note that sending
|
||||
* functions do not actually push data to the socket. They just append data
|
||||
* to the output buffer. The exception is UDP connections. For UDP, data is
|
||||
* sent immediately, and returned value indicates an actual number of bytes
|
||||
* sent to the socket.
|
||||
* Note that sending functions do not actually push data to the socket.
|
||||
* They just append data to the output buffer. MG_EV_SEND will be delivered when
|
||||
* the data has actually been pushed out.
|
||||
*/
|
||||
int mg_send(struct mg_connection *, const void *buf, int len);
|
||||
void mg_send(struct mg_connection *, const void *buf, int len);
|
||||
|
||||
/* Enables format string warnings for mg_printf */
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
/* don't separate from mg_printf declaration */
|
||||
|
||||
/*
|
||||
* Send `printf`-style formatted data to the connection.
|
||||
@@ -924,7 +1009,9 @@ int mg_socketpair(sock_t[2], int sock_type);
|
||||
* CAUTION: this function can block.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
#ifndef MG_DISABLE_SYNC_RESOLVER
|
||||
int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Verify given IP address against the ACL.
|
||||
@@ -957,11 +1044,84 @@ int mg_check_ip_acl(const char *acl, uint32_t remote_ip);
|
||||
*/
|
||||
void mg_enable_multithreading(struct mg_connection *nc);
|
||||
|
||||
#ifdef MG_ENABLE_JAVASCRIPT
|
||||
/*
|
||||
* Enable server-side JavaScript scripting.
|
||||
* Requires `-DMG_ENABLE_JAVASCRIPT` compilation flag, and V7 engine sources.
|
||||
* v7 instance must not be destroyed during manager's lifetime.
|
||||
* Return V7 error.
|
||||
*/
|
||||
enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7,
|
||||
const char *init_js_file_name);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* MG_NET_HEADER_INCLUDED */
|
||||
#ifndef MG_NET_IF_HEADER_INCLUDED
|
||||
#define MG_NET_IF_HEADER_INCLUDED
|
||||
|
||||
/*
|
||||
* Internal async networking core interface.
|
||||
* Consists of calls made by the core, which should not block,
|
||||
* and callbacks back into the core ("..._cb").
|
||||
* Callbacks may (will) cause methods to be invoked from within,
|
||||
* but methods are not allowed to invoke callbacks inline.
|
||||
*
|
||||
* Implementation must ensure that only one callback is invoked at any time.
|
||||
*/
|
||||
|
||||
/* Request that a TCP connection is made to the specified address. */
|
||||
void mg_if_connect_tcp(struct mg_connection *nc,
|
||||
const union socket_address *sa);
|
||||
/* Open a UDP socket. Doesn't actually connect anything. */
|
||||
void mg_if_connect_udp(struct mg_connection *nc);
|
||||
/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */
|
||||
void mg_if_connect_cb(struct mg_connection *nc, int err);
|
||||
|
||||
/* Set up a listening TCP socket on a given address. rv = 0 -> ok. */
|
||||
int mg_if_listen_tcp(struct mg_connection *nc, union socket_address *sa);
|
||||
/* Deliver a new TCP connection. Returns NULL in case on error (unable to
|
||||
* create connection, in which case interface state should be discarded. */
|
||||
struct mg_connection *mg_if_accept_tcp_cb(struct mg_connection *lc,
|
||||
union socket_address *sa,
|
||||
size_t sa_len);
|
||||
|
||||
/* Request that a "listening" UDP socket be created. */
|
||||
int mg_if_listen_udp(struct mg_connection *nc, union socket_address *sa);
|
||||
|
||||
/* Send functions for TCP and UDP. Sent data is copied before return. */
|
||||
void mg_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len);
|
||||
void mg_if_udp_send(struct mg_connection *nc, const void *buf, size_t len);
|
||||
/* Callback that reports that data has been put on the wire. */
|
||||
void mg_if_sent_cb(struct mg_connection *nc, int num_sent);
|
||||
|
||||
/*
|
||||
* Receive callback.
|
||||
* buf must be heap-allocated and ownership is transferred to the core.
|
||||
* Core will acknowledge consumption by calling mg_if_recved.
|
||||
* No more than one chunk of data can be unacknowledged at any time.
|
||||
*/
|
||||
void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len);
|
||||
void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len,
|
||||
union socket_address *sa, size_t sa_len);
|
||||
void mg_if_recved(struct mg_connection *nc, size_t len);
|
||||
|
||||
/* Deliver a POLL event to the connection. */
|
||||
void mg_if_poll(struct mg_connection *nc, time_t now);
|
||||
|
||||
/* Perform interface-related cleanup on connection before destruction. */
|
||||
void mg_if_destroy_conn(struct mg_connection *nc);
|
||||
|
||||
void mg_close_conn(struct mg_connection *nc);
|
||||
|
||||
/* Put connection's address into *sa, local (remote = 0) or remote. */
|
||||
void mg_if_get_conn_addr(struct mg_connection *nc, int remote,
|
||||
union socket_address *sa);
|
||||
|
||||
#endif /* MG_NET_IF_HEADER_INCLUDED */
|
||||
/*
|
||||
* Copyright (c) 2014 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
@@ -1066,6 +1226,10 @@ FILE *mg_fopen(const char *path, const char *mode);
|
||||
int mg_open(const char *path, int flag, int mode);
|
||||
#endif /* MG_DISABLE_FILESYSTEM */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MG_ENABLE_THREADS
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_THREADS
|
||||
/*
|
||||
* Start a new detached thread.
|
||||
@@ -1082,7 +1246,7 @@ void mg_set_close_on_exec(sock_t);
|
||||
#define MG_SOCK_STRINGIFY_PORT 2
|
||||
#define MG_SOCK_STRINGIFY_REMOTE 4
|
||||
/*
|
||||
* Convert socket's local or remote address into string.
|
||||
* Convert connection's local or remote address into string.
|
||||
*
|
||||
* The `flags` parameter is a bit mask that controls the behavior,
|
||||
* see `MG_SOCK_STRINGIFY_*` definitions.
|
||||
@@ -1094,7 +1258,11 @@ void mg_set_close_on_exec(sock_t);
|
||||
* If both port number and IP address are printed, they are separated by `:`.
|
||||
* If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported.
|
||||
*/
|
||||
void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
|
||||
int flags);
|
||||
#ifndef MG_DISABLE_SOCKET_IF /* Legacy interface. */
|
||||
void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Convert socket's address into string.
|
||||
@@ -1122,7 +1290,7 @@ int mg_hexdump(const void *buf, int len, char *dst, int dst_len);
|
||||
* event handler.
|
||||
*/
|
||||
void mg_hexdump_connection(struct mg_connection *nc, const char *path,
|
||||
int num_bytes, int ev);
|
||||
const void *buf, int num_bytes, int ev);
|
||||
/*
|
||||
* Print message to buffer. If buffer is large enough to hold the message,
|
||||
* return buffer. If buffer is to small, allocate large enough buffer on heap,
|
||||
@@ -1197,8 +1365,12 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef MG_MAX_PATH
|
||||
#ifdef PATH_MAX
|
||||
#define MG_MAX_PATH PATH_MAX
|
||||
#else
|
||||
#define MG_MAX_PATH 1024
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_MAX_HTTP_SEND_IOBUF
|
||||
#define MG_MAX_HTTP_SEND_IOBUF 4096
|
||||
@@ -1224,10 +1396,11 @@ extern "C" {
|
||||
struct http_message {
|
||||
struct mg_str message; /* Whole message: request line + headers + body */
|
||||
|
||||
struct mg_str proto; /* "HTTP/1.1" -- for both request and response */
|
||||
/* HTTP Request line (or HTTP response line) */
|
||||
struct mg_str method; /* "GET" */
|
||||
struct mg_str uri; /* "/my_file.html" */
|
||||
struct mg_str proto; /* "HTTP/1.1" -- for both request and response */
|
||||
|
||||
/* For responses, code and response status message are set */
|
||||
int resp_code;
|
||||
struct mg_str resp_status_msg;
|
||||
@@ -1372,6 +1545,38 @@ void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len);
|
||||
*/
|
||||
void mg_printf_http_chunk(struct mg_connection *, const char *, ...);
|
||||
|
||||
/*
|
||||
* Send response status line.
|
||||
* If `extra_headers` is not NULL, then `extra_headers` are also sent
|
||||
* after the reponse line. `extra_headers` must NOT end end with new line.
|
||||
* Example:
|
||||
*
|
||||
* mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
|
||||
*
|
||||
* Will result in:
|
||||
*
|
||||
* HTTP/1.1 200 OK\r\n
|
||||
* Access-Control-Allow-Origin: *\r\n
|
||||
*/
|
||||
void mg_send_response_line(struct mg_connection *c, int status_code,
|
||||
const char *extra_headers);
|
||||
|
||||
/*
|
||||
* Send response line and headers.
|
||||
* This function sends response line with the `status_code`, and automatically
|
||||
* sends one header: either "Content-Length", or "Transfer-Encoding".
|
||||
* If `content_length` is negative, then "Transfer-Encoding: chunked" header
|
||||
* is sent, otherwise, "Content-Length" header is sent.
|
||||
*
|
||||
* NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent
|
||||
* using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions.
|
||||
* Otherwise, `mg_send()` or `mg_printf()` must be used.
|
||||
* Extra headers could be set through `extra_headers` - and note `extra_headers`
|
||||
* must NOT be terminated by a new line.
|
||||
*/
|
||||
void mg_send_head(struct mg_connection *n, int status_code,
|
||||
int64_t content_length, const char *extra_headers);
|
||||
|
||||
/*
|
||||
* Send printf-formatted HTTP chunk, escaping HTML tags.
|
||||
*/
|
||||
@@ -1526,6 +1731,9 @@ struct mg_serve_http_opts {
|
||||
/* List of index files. Default is "" */
|
||||
const char *index_files;
|
||||
|
||||
/* Path to a HTTP requests log file. Leave as NULL to disable access log. */
|
||||
const char *access_log_file;
|
||||
|
||||
/*
|
||||
* Leave as NULL to disable authentication.
|
||||
* To enable directory protection with authentication, set this to ".htpasswd"
|
||||
@@ -1573,12 +1781,22 @@ struct mg_serve_http_opts {
|
||||
* If uri_pattern starts with `@` symbol, then Mongoose compares it with the
|
||||
* HOST header of the request. If they are equal, Mongoose sets document root
|
||||
* to `file_or_directory_path`, implementing virtual hosts support.
|
||||
* Example: `@foo.com=/document/root/for/foo.com`
|
||||
*
|
||||
* If `uri_pattern` starts with `%` symbol, then Mongoose compares it with
|
||||
* the listening port. If they match, then Mongoose issues a 301 redirect.
|
||||
* For example, to redirect all HTTP requests to the
|
||||
* HTTPS port, do `%80=https://my.site.com`. Note that the request URI is
|
||||
* automatically appended to the redirect location.
|
||||
*/
|
||||
const char *url_rewrites;
|
||||
|
||||
/* DAV document root. If NULL, DAV requests are going to fail. */
|
||||
const char *dav_document_root;
|
||||
|
||||
/* DAV passwords file. If NULL, DAV requests are going to fail. */
|
||||
const char *dav_auth_file;
|
||||
|
||||
/* Glob pattern for the files to hide. */
|
||||
const char *hidden_file_pattern;
|
||||
|
||||
@@ -1593,6 +1811,12 @@ struct mg_serve_http_opts {
|
||||
* ".txt=text/plain; charset=utf-8,.c=text/plain"
|
||||
*/
|
||||
const char *custom_mime_types;
|
||||
|
||||
/*
|
||||
* Extra HTTP headers to add to each server response.
|
||||
* Example: to enable CORS, set this to "Access-Control-Allow-Origin: *".
|
||||
*/
|
||||
const char *extra_headers;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2081,35 +2305,35 @@ extern "C" {
|
||||
|
||||
#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
|
||||
|
||||
enum mg_dmg_resource_record_kind {
|
||||
enum mg_dns_resource_record_kind {
|
||||
MG_DNS_INVALID_RECORD = 0,
|
||||
MG_DNS_QUESTION,
|
||||
MG_DNS_ANSWER
|
||||
};
|
||||
|
||||
/* DNS resource record. */
|
||||
struct mg_dmg_resource_record {
|
||||
struct mg_dns_resource_record {
|
||||
struct mg_str name; /* buffer with compressed name */
|
||||
int rtype;
|
||||
int rclass;
|
||||
int ttl;
|
||||
enum mg_dmg_resource_record_kind kind;
|
||||
enum mg_dns_resource_record_kind kind;
|
||||
struct mg_str rdata; /* protocol data (can be a compressed name) */
|
||||
};
|
||||
|
||||
/* DNS message (request and response). */
|
||||
struct mg_dmg_message {
|
||||
struct mg_dns_message {
|
||||
struct mg_str pkt; /* packet body */
|
||||
uint16_t flags;
|
||||
uint16_t transaction_id;
|
||||
int num_questions;
|
||||
int num_answers;
|
||||
struct mg_dmg_resource_record questions[MG_MAX_DNS_QUESTIONS];
|
||||
struct mg_dmg_resource_record answers[MG_MAX_DNS_ANSWERS];
|
||||
struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS];
|
||||
struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS];
|
||||
};
|
||||
|
||||
struct mg_dmg_resource_record *mg_dmg_next_record(
|
||||
struct mg_dmg_message *, int, struct mg_dmg_resource_record *);
|
||||
struct mg_dns_resource_record *mg_dns_next_record(
|
||||
struct mg_dns_message *, int, struct mg_dns_resource_record *);
|
||||
|
||||
/*
|
||||
* Parse the record data from a DNS resource record.
|
||||
@@ -2122,20 +2346,20 @@ struct mg_dmg_resource_record *mg_dmg_next_record(
|
||||
*
|
||||
* TODO(mkm): MX
|
||||
*/
|
||||
int mg_dmg_parse_record_data(struct mg_dmg_message *,
|
||||
struct mg_dmg_resource_record *, void *, size_t);
|
||||
int mg_dns_parse_record_data(struct mg_dns_message *,
|
||||
struct mg_dns_resource_record *, void *, size_t);
|
||||
|
||||
/*
|
||||
* Send a DNS query to the remote end.
|
||||
*/
|
||||
void mg_send_dmg_query(struct mg_connection *, const char *, int);
|
||||
void mg_send_dns_query(struct mg_connection *, const char *, int);
|
||||
|
||||
/*
|
||||
* Insert a DNS header to an IO buffer.
|
||||
*
|
||||
* Return number of bytes inserted.
|
||||
*/
|
||||
int mg_dmg_insert_header(struct mbuf *, size_t, struct mg_dmg_message *);
|
||||
int mg_dns_insert_header(struct mbuf *, size_t, struct mg_dns_message *);
|
||||
|
||||
/*
|
||||
* Append already encoded body from an existing message.
|
||||
@@ -2145,7 +2369,7 @@ int mg_dmg_insert_header(struct mbuf *, size_t, struct mg_dmg_message *);
|
||||
*
|
||||
* Return number of appened bytes.
|
||||
*/
|
||||
int mg_dmg_copy_body(struct mbuf *, struct mg_dmg_message *);
|
||||
int mg_dns_copy_body(struct mbuf *, struct mg_dns_message *);
|
||||
|
||||
/*
|
||||
* Encode and append a DNS resource record to an IO buffer.
|
||||
@@ -2163,11 +2387,11 @@ int mg_dmg_copy_body(struct mbuf *, struct mg_dmg_message *);
|
||||
*
|
||||
* Return the number of bytes appened or -1 in case of error.
|
||||
*/
|
||||
int mg_dmg_encode_record(struct mbuf *, struct mg_dmg_resource_record *,
|
||||
int mg_dns_encode_record(struct mbuf *, struct mg_dns_resource_record *,
|
||||
const char *, size_t, const void *, size_t);
|
||||
|
||||
/* Low-level: parses a DNS response. */
|
||||
int mg_parse_dns(const char *, int, struct mg_dmg_message *);
|
||||
int mg_parse_dns(const char *, int, struct mg_dns_message *);
|
||||
|
||||
/*
|
||||
* Uncompress a DNS compressed name.
|
||||
@@ -2182,7 +2406,7 @@ int mg_parse_dns(const char *, int, struct mg_dmg_message *);
|
||||
* If `dst_len` is 0 `dst` can be NULL.
|
||||
* Return the uncompressed name length.
|
||||
*/
|
||||
size_t mg_dmg_uncompress_name(struct mg_dmg_message *, struct mg_str *, char *,
|
||||
size_t mg_dns_uncompress_name(struct mg_dns_message *, struct mg_str *, char *,
|
||||
int);
|
||||
|
||||
/*
|
||||
@@ -2191,10 +2415,10 @@ size_t mg_dmg_uncompress_name(struct mg_dmg_message *, struct mg_str *, char *,
|
||||
* DNS event handler parses incoming UDP packets, treating them as DNS
|
||||
* requests. If incoming packet gets successfully parsed by the DNS event
|
||||
* handler, a user event handler will receive `MG_DNS_REQUEST` event, with
|
||||
* `ev_data` pointing to the parsed `struct mg_dmg_message`.
|
||||
* `ev_data` pointing to the parsed `struct mg_dns_message`.
|
||||
*
|
||||
* See
|
||||
* https://github.com/cesanta/mongoose/tree/master/examples/captive_dmg_server[captive_dmg_server]
|
||||
* https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server[captive_dns_server]
|
||||
* example on how to handle DNS request and send DNS reply.
|
||||
*/
|
||||
void mg_set_protocol_dns(struct mg_connection *);
|
||||
@@ -2226,8 +2450,8 @@ extern "C" {
|
||||
|
||||
#define MG_DNS_SERVER_DEFAULT_TTL 3600
|
||||
|
||||
struct mg_dmg_reply {
|
||||
struct mg_dmg_message *msg;
|
||||
struct mg_dns_reply {
|
||||
struct mg_dns_message *msg;
|
||||
struct mbuf *io;
|
||||
size_t start;
|
||||
};
|
||||
@@ -2240,10 +2464,10 @@ struct mg_dmg_reply {
|
||||
* "reply + recursion allowed" will be added to the message flags and
|
||||
* message's num_answers will be set to 0.
|
||||
*
|
||||
* Answer records can be appended with `mg_dmg_send_reply` or by lower
|
||||
* Answer records can be appended with `mg_dns_send_reply` or by lower
|
||||
* level function defined in the DNS API.
|
||||
*
|
||||
* In order to send the reply use `mg_dmg_send_reply`.
|
||||
* In order to send the reply use `mg_dns_send_reply`.
|
||||
* It's possible to use a connection's send buffer as reply buffers,
|
||||
* and it will work for both UDP and TCP connections.
|
||||
*
|
||||
@@ -2251,17 +2475,17 @@ struct mg_dmg_reply {
|
||||
*
|
||||
* [source,c]
|
||||
* -----
|
||||
* reply = mg_dmg_create_reply(&nc->send_mbuf, msg);
|
||||
* reply = mg_dns_create_reply(&nc->send_mbuf, msg);
|
||||
* for (i = 0; i < msg->num_questions; i++) {
|
||||
* rr = &msg->questions[i];
|
||||
* if (rr->rtype == MG_DNS_A_RECORD) {
|
||||
* mg_dmg_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4);
|
||||
* mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4);
|
||||
* }
|
||||
* }
|
||||
* mg_dmg_send_reply(nc, &reply);
|
||||
* mg_dns_send_reply(nc, &reply);
|
||||
* -----
|
||||
*/
|
||||
struct mg_dmg_reply mg_dmg_create_reply(struct mbuf *, struct mg_dmg_message *);
|
||||
struct mg_dns_reply mg_dns_create_reply(struct mbuf *, struct mg_dns_message *);
|
||||
|
||||
/*
|
||||
* Append a DNS reply record to the IO buffer and to the DNS message.
|
||||
@@ -2271,7 +2495,7 @@ struct mg_dmg_reply mg_dmg_create_reply(struct mbuf *, struct mg_dmg_message *);
|
||||
*
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
int mg_dmg_reply_record(struct mg_dmg_reply *, struct mg_dmg_resource_record *,
|
||||
int mg_dns_reply_record(struct mg_dns_reply *, struct mg_dns_resource_record *,
|
||||
const char *, int, int, const void *, size_t);
|
||||
|
||||
/*
|
||||
@@ -2280,13 +2504,13 @@ int mg_dmg_reply_record(struct mg_dmg_reply *, struct mg_dmg_resource_record *,
|
||||
* The DNS data is stored in an IO buffer pointed by reply structure in `r`.
|
||||
* This function mutates the content of that buffer in order to ensure that
|
||||
* the DNS header reflects size and flags of the mssage, that might have been
|
||||
* updated either with `mg_dmg_reply_record` or by direct manipulation of
|
||||
* updated either with `mg_dns_reply_record` or by direct manipulation of
|
||||
* `r->message`.
|
||||
*
|
||||
* Once sent, the IO buffer will be trimmed unless the reply IO buffer
|
||||
* is the connection's send buffer and the connection is not in UDP mode.
|
||||
*/
|
||||
int mg_dmg_send_reply(struct mg_connection *, struct mg_dmg_reply *);
|
||||
void mg_dns_send_reply(struct mg_connection *, struct mg_dns_reply *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -2311,7 +2535,7 @@ int mg_dmg_send_reply(struct mg_connection *, struct mg_dmg_reply *);
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef void (*mg_resolve_callback_t)(struct mg_dmg_message *, void *);
|
||||
typedef void (*mg_resolve_callback_t)(struct mg_dns_message *, void *);
|
||||
|
||||
/* Options for `mg_resolve_async_opt`. */
|
||||
struct mg_resolve_async_opts {
|
||||
@@ -2337,14 +2561,14 @@ int mg_resolve_async(struct mg_mgr *, const char *, int, mg_resolve_callback_t,
|
||||
* will receive a NULL `msg`.
|
||||
*
|
||||
* The DNS answers can be extracted with `mg_next_record` and
|
||||
* `mg_dmg_parse_record_data`:
|
||||
* `mg_dns_parse_record_data`:
|
||||
*
|
||||
* [source,c]
|
||||
* ----
|
||||
* struct in_addr ina;
|
||||
* struct mg_dmg_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD,
|
||||
* struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD,
|
||||
* NULL);
|
||||
* mg_dmg_parse_record_data(msg, rr, &ina, sizeof(ina));
|
||||
* mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina));
|
||||
* ----
|
||||
*/
|
||||
int mg_resolve_async_opt(struct mg_mgr *, const char *, int,
|
||||
|
||||
Reference in new issue
Block a user