added WebSockets (#331)
This commit is contained in:
		
							parent
							
								
									f64935eb1d
								
							
						
					
					
						commit
						bce2139f06
					
				
					 16 changed files with 3810 additions and 1363 deletions
				
			
		| 
						 | 
				
			
			@ -1,10 +1,11 @@
 | 
			
		|||
PROGRAM=http_server
 | 
			
		||||
 | 
			
		||||
#ESPBAUD=921600
 | 
			
		||||
 | 
			
		||||
EXTRA_CFLAGS=-DLWIP_HTTPD_CGI=1 -DLWIP_HTTPD_SSI=1 -I./fsdata
 | 
			
		||||
 | 
			
		||||
EXTRA_COMPONENTS=extras/httpd
 | 
			
		||||
#Enable debugging
 | 
			
		||||
#EXTRA_CFLAGS+=-DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON
 | 
			
		||||
 | 
			
		||||
EXTRA_COMPONENTS=extras/mbedtls extras/httpd
 | 
			
		||||
 | 
			
		||||
include ../../common.mk
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,16 +3,15 @@
 | 
			
		|||
	<head>
 | 
			
		||||
		<meta charset="utf-8">
 | 
			
		||||
		<meta name="viewport" content="width=device-width, user-scalable=no">
 | 
			
		||||
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/siimple.min.css">
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/style.css">
 | 
			
		||||
		<link rel="shortcut icon" href="img/favicon.png">
 | 
			
		||||
 | 
			
		||||
		<title>HTTP Server</title>
 | 
			
		||||
	</head>
 | 
			
		||||
	<body>
 | 
			
		||||
		<ul class="navbar">
 | 
			
		||||
			<li><a href="/">Home</a></li>
 | 
			
		||||
			<li><a href="websockets">WebSockets</a></li>
 | 
			
		||||
			<li><a href="about">About</a></li>
 | 
			
		||||
		</ul>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,24 +3,23 @@
 | 
			
		|||
	<head>
 | 
			
		||||
		<meta charset="utf-8">
 | 
			
		||||
		<meta name="viewport" content="width=device-width, user-scalable=no">
 | 
			
		||||
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/siimple.min.css">
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/style.css">
 | 
			
		||||
		<link rel="shortcut icon" href="img/favicon.png">
 | 
			
		||||
 | 
			
		||||
		<title>HTTP Server</title>
 | 
			
		||||
	</head>
 | 
			
		||||
	<body>
 | 
			
		||||
		<ul class="navbar">
 | 
			
		||||
			<li><a href="/">Home</a></li>
 | 
			
		||||
			<li><a href="websockets">WebSockets</a></li>
 | 
			
		||||
			<li><a class="active" href="about">About</a></li>
 | 
			
		||||
		</ul>
 | 
			
		||||
 | 
			
		||||
		<div class="grid main">
 | 
			
		||||
			<h1>About</h1>
 | 
			
		||||
			<p>This server is built on httpd from LwIP.</p>
 | 
			
		||||
			<p>This server is based on httpd from LwIP.</p>
 | 
			
		||||
			<p>To enable debugging compile with flags -DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON.</p>
 | 
			
		||||
			<p>For more info see <a href="http://www.nongnu.org/lwip/2_0_0/group__httpd.html">HTTP Server documentation</a>.</p>
 | 
			
		||||
		</div>
 | 
			
		||||
	</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,16 +3,15 @@
 | 
			
		|||
	<head>
 | 
			
		||||
		<meta charset="utf-8">
 | 
			
		||||
		<meta name="viewport" content="width=device-width, user-scalable=no">
 | 
			
		||||
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/siimple.min.css">
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/style.css">
 | 
			
		||||
		<link rel="shortcut icon" href="img/favicon.png">
 | 
			
		||||
 | 
			
		||||
		<title>HTTP Server</title>
 | 
			
		||||
	</head>
 | 
			
		||||
	<body>
 | 
			
		||||
		<ul class="navbar">
 | 
			
		||||
			<li><a class="active" href="/">Home</a></li>
 | 
			
		||||
			<li><a href="websockets">WebSockets</a></li>
 | 
			
		||||
			<li><a href="about">About</a></li>
 | 
			
		||||
		</ul>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								examples/http_server/fsdata/fs/js/smoothie_min.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/http_server/fsdata/fs/js/smoothie_min.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										130
									
								
								examples/http_server/fsdata/fs/websockets.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								examples/http_server/fsdata/fs/websockets.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,130 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
	<head>
 | 
			
		||||
		<meta charset="utf-8">
 | 
			
		||||
		<meta name="viewport" content="width=device-width, user-scalable=no">
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/siimple.min.css">
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="css/style.css">
 | 
			
		||||
		<link rel="shortcut icon" href="img/favicon.png">
 | 
			
		||||
		<title>HTTP Server</title>
 | 
			
		||||
	</head>
 | 
			
		||||
	<body>
 | 
			
		||||
		<ul class="navbar">
 | 
			
		||||
			<li><a href="/">Home</a></li>
 | 
			
		||||
			<li><a class="active" href="websockets">WebSockets</a></li>
 | 
			
		||||
			<li><a href="about">About</a></li>
 | 
			
		||||
		</ul>
 | 
			
		||||
 | 
			
		||||
		<div class="grid main">
 | 
			
		||||
			<h1>WebSockets Demo</h1>
 | 
			
		||||
			<div id="status_box" class="alert alert-info">Loading..</div>
 | 
			
		||||
			<p>This page is similar to the home page but uses WebSockets for real-time updates.</p>
 | 
			
		||||
			<div class="cover" align="center">
 | 
			
		||||
				<canvas id="chartCanvas" width="512" height="100"></canvas>
 | 
			
		||||
				<p/>
 | 
			
		||||
				<p>LED Control</p>
 | 
			
		||||
				<div class="onoffswitch">
 | 
			
		||||
					<input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" id="led-switch" onclick="gpio()">
 | 
			
		||||
					<label class="onoffswitch-label" for="led-switch">
 | 
			
		||||
						<span class="onoffswitch-inner"></span>
 | 
			
		||||
						<span class="onoffswitch-switch"></span>
 | 
			
		||||
					</label>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
			<h1>Server Status</h1>
 | 
			
		||||
			<table class="table table-striped">
 | 
			
		||||
				<tr>
 | 
			
		||||
					<td><b>Uptime:</b></td>
 | 
			
		||||
					<td id="uptime"></td>
 | 
			
		||||
				</tr>
 | 
			
		||||
				<tr>
 | 
			
		||||
					<td><b>Free heap:</b></td>
 | 
			
		||||
					<td id="heap"></td>
 | 
			
		||||
				</tr>
 | 
			
		||||
				<tr>
 | 
			
		||||
					<td><b>LED state:</b></td>
 | 
			
		||||
					<td id="led"></td>
 | 
			
		||||
				</tr>
 | 
			
		||||
			</table>
 | 
			
		||||
 | 
			
		||||
			<h1>How it works</h1>
 | 
			
		||||
			<p>This demo uses 2 WebScokets. Status parameters are streamed by the server in JSON format every 2 seconds.
 | 
			
		||||
				A <code>websocket_task</code> is created each time a specific URI is requested.</p>
 | 
			
		||||
			<p>ADC values are being continuously polled by the client (i.e. your browser).
 | 
			
		||||
				Each time a WebSocket frame is received on the server side, <code>websocket_cb</code> function is being called.</p>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<script type="text/javascript" src="js/smoothie_min.js"></script>
 | 
			
		||||
		<script>
 | 
			
		||||
			var ws;
 | 
			
		||||
			var retries;
 | 
			
		||||
			var series = new TimeSeries();
 | 
			
		||||
			window.onload = function() {
 | 
			
		||||
				wsOpen();
 | 
			
		||||
				startPolling();
 | 
			
		||||
			}
 | 
			
		||||
			function setMsg(cls, text) {
 | 
			
		||||
				sbox = document.getElementById('status_box');
 | 
			
		||||
				sbox.className = "alert alert-" + cls;
 | 
			
		||||
				sbox.innerHTML = text;
 | 
			
		||||
				console.log(text);
 | 
			
		||||
			}
 | 
			
		||||
			function startPolling() {
 | 
			
		||||
				var chart = new SmoothieChart({millisPerPixel:11,grid:{fillStyle:'#ffffff',strokeStyle:'#ffffff',borderVisible:false},
 | 
			
		||||
					labels:{fillStyle:'#000000'},maxValue:1024,minValue:0});
 | 
			
		||||
				chart.addTimeSeries(series, {lineWidth:2,strokeStyle:'#03a9f4',fillStyle:'#f1f5fa'});
 | 
			
		||||
				chart.streamTo(document.getElementById("chartCanvas"), 500);
 | 
			
		||||
				setInterval(function() { wsWrite('A'); }, 500);
 | 
			
		||||
			}
 | 
			
		||||
			function onMessage(evt) {
 | 
			
		||||
				retries = 0;
 | 
			
		||||
				var dv = new DataView(evt.data);
 | 
			
		||||
				var val = dv.getUint16(0);
 | 
			
		||||
				if (val == 0xBEEF || val == 0xDEAD)
 | 
			
		||||
					console.log("LED switched");
 | 
			
		||||
				else
 | 
			
		||||
					series.append(new Date().getTime(), val);
 | 
			
		||||
			}
 | 
			
		||||
			function wsOpen() {
 | 
			
		||||
				if (ws === undefined || ws.readyState != 0) {
 | 
			
		||||
					if (retries)
 | 
			
		||||
						setMsg("error", "WebSocket timeout, retrying..");
 | 
			
		||||
					else
 | 
			
		||||
						setMsg("info", "Opening WebSocket..");
 | 
			
		||||
					ws = new WebSocket("ws://" + location.host);
 | 
			
		||||
					ws.binaryType = 'arraybuffer';
 | 
			
		||||
					ws.onopen = function(evt) { retries = 0; setMsg("done", "WebSocket is open."); };
 | 
			
		||||
					ws.onerror = function(evt) { setMsg("error", "WebSocket error!"); };
 | 
			
		||||
					ws.onmessage = function(evt) { onMessage(evt); };
 | 
			
		||||
					wsOpenStream();
 | 
			
		||||
					retries = 0;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			function wsOpenStream() {
 | 
			
		||||
				var uri = "/stream"
 | 
			
		||||
				var ws = new WebSocket("ws://" + location.host + uri);
 | 
			
		||||
				ws.onmessage = function(evt) {
 | 
			
		||||
					console.log(evt.data);
 | 
			
		||||
					var stats = JSON.parse(evt.data);
 | 
			
		||||
					console.log(stats);
 | 
			
		||||
					document.getElementById('uptime').innerHTML = stats.uptime + ' seconds';
 | 
			
		||||
					document.getElementById('heap').innerHTML = stats.heap + ' bytes';
 | 
			
		||||
					document.getElementById('led').innerHTML = (stats.led == 1) ? 'On' : 'Off';
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
			function wsWrite(data) {
 | 
			
		||||
				if (ws.readyState == 3 || retries++ > 5)
 | 
			
		||||
					wsOpen();
 | 
			
		||||
				else if (ws.readyState == 1)
 | 
			
		||||
					ws.send(data);
 | 
			
		||||
			}
 | 
			
		||||
			function gpio() {
 | 
			
		||||
				if (document.getElementById('led-switch').checked)
 | 
			
		||||
					wsWrite('E');
 | 
			
		||||
				else
 | 
			
		||||
					wsWrite('D');
 | 
			
		||||
			}
 | 
			
		||||
		</script>
 | 
			
		||||
	</body>
 | 
			
		||||
</html>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -21,6 +21,28 @@ enum {
 | 
			
		|||
    SSI_LED_STATE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int32_t ssi_handler(int32_t iIndex, char *pcInsert, int32_t iInsertLen)
 | 
			
		||||
{
 | 
			
		||||
    switch (iIndex) {
 | 
			
		||||
        case SSI_UPTIME:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "%d",
 | 
			
		||||
                    xTaskGetTickCount() * portTICK_PERIOD_MS / 1000);
 | 
			
		||||
            break;
 | 
			
		||||
        case SSI_FREE_HEAP:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "%d", (int) xPortGetFreeHeapSize());
 | 
			
		||||
            break;
 | 
			
		||||
        case SSI_LED_STATE:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, (GPIO.OUT & BIT(LED_PIN)) ? "Off" : "On");
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "N/A");
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Tell the server how many characters to insert */
 | 
			
		||||
    return (strlen(pcInsert));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *gpio_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < iNumParams; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -46,26 +68,89 @@ char *about_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcVal
 | 
			
		|||
    return "/about.html";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t ssi_handler(int32_t iIndex, char *pcInsert, int32_t iInsertLen)
 | 
			
		||||
char *websocket_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
 | 
			
		||||
{
 | 
			
		||||
    switch (iIndex) {
 | 
			
		||||
        case SSI_UPTIME:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "%d",
 | 
			
		||||
                    xTaskGetTickCount() * portTICK_PERIOD_MS / 1000);
 | 
			
		||||
    return "/websockets.html";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void websocket_task(void *pvParameter)
 | 
			
		||||
{
 | 
			
		||||
    struct tcp_pcb *pcb = (struct tcp_pcb *) pvParameter;
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        if (pcb == NULL || pcb->state != ESTABLISHED) {
 | 
			
		||||
            printf("Connection closed, deleting task\n");
 | 
			
		||||
            break;
 | 
			
		||||
        case SSI_FREE_HEAP:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "%d", (int) xPortGetFreeHeapSize());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000;
 | 
			
		||||
        int heap = (int) xPortGetFreeHeapSize();
 | 
			
		||||
        int led = !gpio_read(LED_PIN);
 | 
			
		||||
 | 
			
		||||
        /* Generate response in JSON format */
 | 
			
		||||
        char response[64];
 | 
			
		||||
        int len = snprintf(response, sizeof (response),
 | 
			
		||||
                "{\"uptime\" : \"%d\","
 | 
			
		||||
                " \"heap\" : \"%d\","
 | 
			
		||||
                " \"led\" : \"%d\"}", uptime, heap, led);
 | 
			
		||||
        if (len < sizeof (response))
 | 
			
		||||
            websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
 | 
			
		||||
 | 
			
		||||
        vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vTaskDelete(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This function is called when websocket frame is received.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: this function is executed on TCP thread and should return as soon
 | 
			
		||||
 * as possible.
 | 
			
		||||
 */
 | 
			
		||||
void websocket_cb(struct tcp_pcb *pcb, uint8_t *data, u16_t data_len, uint8_t mode)
 | 
			
		||||
{
 | 
			
		||||
    printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char*) data);
 | 
			
		||||
 | 
			
		||||
    uint8_t response[2];
 | 
			
		||||
    uint16_t val;
 | 
			
		||||
 | 
			
		||||
    switch (data[0]) {
 | 
			
		||||
        case 'A': // ADC
 | 
			
		||||
            /* This should be done on a separate thread in 'real' applications */
 | 
			
		||||
            val = sdk_system_adc_read();
 | 
			
		||||
            break;
 | 
			
		||||
        case SSI_LED_STATE:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, (GPIO.OUT & BIT(LED_PIN)) ? "Off" : "On");
 | 
			
		||||
        case 'D': // Disable LED
 | 
			
		||||
            gpio_write(LED_PIN, true);
 | 
			
		||||
            val = 0xDEAD;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'E': // Enable LED
 | 
			
		||||
            gpio_write(LED_PIN, false);
 | 
			
		||||
            val = 0xBEEF;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            snprintf(pcInsert, iInsertLen, "N/A");
 | 
			
		||||
            printf("Unknown command\n");
 | 
			
		||||
            val = 0;
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Tell the server how many characters to insert */
 | 
			
		||||
    return (strlen(pcInsert));
 | 
			
		||||
    response[1] = (uint8_t) val;
 | 
			
		||||
    response[0] = val >> 8;
 | 
			
		||||
 | 
			
		||||
    websocket_write(pcb, response, 2, WS_BIN_MODE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This function is called when new websocket is open and
 | 
			
		||||
 * creates a new websocket_task if requested URI equals '/stream'.
 | 
			
		||||
 */
 | 
			
		||||
void websocket_open_cb(struct tcp_pcb *pcb, const char *uri)
 | 
			
		||||
{
 | 
			
		||||
    printf("WS URI: %s\n", uri);
 | 
			
		||||
    if (!strcmp(uri, "/stream")) {
 | 
			
		||||
        printf("request for streaming\n");
 | 
			
		||||
        xTaskCreate(&websocket_task, "websocket_task", 256, (void *) pcb, 2, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void httpd_task(void *pvParameters)
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +158,7 @@ void httpd_task(void *pvParameters)
 | 
			
		|||
    tCGI pCGIs[] = {
 | 
			
		||||
        {"/gpio", (tCGIHandler) gpio_cgi_handler},
 | 
			
		||||
        {"/about", (tCGIHandler) about_cgi_handler},
 | 
			
		||||
        {"/websockets", (tCGIHandler) websocket_cgi_handler},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const char *pcConfigSSITags[] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +171,8 @@ void httpd_task(void *pvParameters)
 | 
			
		|||
    http_set_cgi_handlers(pCGIs, sizeof (pCGIs) / sizeof (pCGIs[0]));
 | 
			
		||||
    http_set_ssi_handler((tSSIHandler) ssi_handler, pcConfigSSITags,
 | 
			
		||||
            sizeof (pcConfigSSITags) / sizeof (pcConfigSSITags[0]));
 | 
			
		||||
    websocket_register_callbacks((tWsOpenHandler) websocket_open_cb,
 | 
			
		||||
            (tWsHandler) websocket_cb);
 | 
			
		||||
    httpd_init();
 | 
			
		||||
 | 
			
		||||
    for (;;);
 | 
			
		||||
| 
						 | 
				
			
			@ -110,5 +198,5 @@ void user_init(void)
 | 
			
		|||
    gpio_write(LED_PIN, true);
 | 
			
		||||
 | 
			
		||||
    /* initialize tasks */
 | 
			
		||||
    xTaskCreate(&httpd_task, "HTTP Daemon", 1024, NULL, 2, NULL);
 | 
			
		||||
    xTaskCreate(&httpd_task, "HTTP Daemon", 128, NULL, 2, NULL);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 | 
			
		||||
 * All rights reserved. 
 | 
			
		||||
 * 
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification, 
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
 * are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
| 
						 | 
				
			
			@ -11,21 +11,21 @@
 | 
			
		|||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. The name of the author may not be used to endorse or promote products
 | 
			
		||||
 *    derived from this software without specific prior written permission. 
 | 
			
		||||
 *    derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 | 
			
		||||
 * OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the lwIP TCP/IP stack.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Adam Dunkels <adam@sics.se>
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 | 
			
		||||
 * All rights reserved. 
 | 
			
		||||
 * 
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification, 
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
 * are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
| 
						 | 
				
			
			@ -11,21 +11,21 @@
 | 
			
		|||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. The name of the author may not be used to endorse or promote products
 | 
			
		||||
 *    derived from this software without specific prior written permission. 
 | 
			
		||||
 *    derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 | 
			
		||||
 * OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the lwIP TCP/IP stack.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Adam Dunkels <adam@sics.se>
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 | 
			
		||||
 * All rights reserved. 
 | 
			
		||||
 * 
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification, 
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
 * are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
| 
						 | 
				
			
			@ -11,21 +11,21 @@
 | 
			
		|||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. The name of the author may not be used to endorse or promote products
 | 
			
		||||
 *    derived from this software without specific prior written permission. 
 | 
			
		||||
 *    derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 | 
			
		||||
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 | 
			
		||||
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 | 
			
		||||
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 | 
			
		||||
 * OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the lwIP TCP/IP stack.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Adam Dunkels <adam@sics.se>
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,6 +268,29 @@
 | 
			
		|||
#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
 | 
			
		||||
#endif /* HTTPD_USE_MEM_POOL */
 | 
			
		||||
 | 
			
		||||
#include <mbedtls/sha1.h>
 | 
			
		||||
#include <mbedtls/base64.h>
 | 
			
		||||
#include "strcasestr.h"
 | 
			
		||||
 | 
			
		||||
static const char WS_HEADER[] = "Upgrade: websocket\r\n";
 | 
			
		||||
static const char WS_GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 | 
			
		||||
static const char WS_RSP[] = "HTTP/1.1 101 Switching Protocols\r\n" \
 | 
			
		||||
                             "Upgrade: websocket\r\n" \
 | 
			
		||||
                             "Connection: Upgrade\r\n" \
 | 
			
		||||
                             "Sec-WebSocket-Accept: ";
 | 
			
		||||
 | 
			
		||||
/* Response buffer length (30 = base64 encoded key max length) */
 | 
			
		||||
#define WS_BUF_LEN           (sizeof(WS_RSP) + sizeof(CRLF CRLF) + 30 - 2)
 | 
			
		||||
 | 
			
		||||
/* WebSocket timeout: X*(HTTPD_POLL_INTERVAL), default is 10*4*500ms = 20s */
 | 
			
		||||
#ifndef WS_TIMEOUT
 | 
			
		||||
#define WS_TIMEOUT           10
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Callback functions */
 | 
			
		||||
static tWsHandler websocket_cb = NULL;
 | 
			
		||||
static tWsOpenHandler websocket_open_cb = NULL;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  const char *name;
 | 
			
		||||
| 
						 | 
				
			
			@ -341,6 +364,8 @@ struct http_state {
 | 
			
		|||
  struct fs_file *handle;
 | 
			
		||||
  char *file;       /* Pointer to first unsent byte in buf. */
 | 
			
		||||
 | 
			
		||||
  u8_t is_websocket;
 | 
			
		||||
 | 
			
		||||
  struct tcp_pcb *pcb;
 | 
			
		||||
#if LWIP_HTTPD_SUPPORT_REQUESTLIST
 | 
			
		||||
  struct pbuf *req;
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +411,9 @@ static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs
 | 
			
		|||
static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
 | 
			
		||||
static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check);
 | 
			
		||||
static err_t http_poll(void *arg, struct tcp_pcb *pcb);
 | 
			
		||||
 | 
			
		||||
static err_t websocket_send_close(struct tcp_pcb *pcb);
 | 
			
		||||
 | 
			
		||||
#if LWIP_HTTPD_FS_ASYNC_READ
 | 
			
		||||
static void http_continue(void *connection);
 | 
			
		||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
 | 
			
		||||
| 
						 | 
				
			
			@ -666,6 +694,17 @@ http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_
 | 
			
		|||
  }
 | 
			
		||||
#endif /* LWIP_HTTPD_SUPPORT_POST*/
 | 
			
		||||
 | 
			
		||||
  if (hs != NULL) {
 | 
			
		||||
    if (hs->is_websocket)
 | 
			
		||||
      websocket_send_close(pcb);
 | 
			
		||||
 | 
			
		||||
    if (hs->req != NULL) {
 | 
			
		||||
      /* this should not happen */
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("Freeing buffer (malformed request?)\n"));
 | 
			
		||||
      pbuf_free(hs->req);
 | 
			
		||||
      hs->req = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tcp_arg(pcb, NULL);
 | 
			
		||||
  tcp_recv(pcb, NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -699,7 +738,7 @@ http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_
 | 
			
		|||
static err_t
 | 
			
		||||
http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
 | 
			
		||||
{
 | 
			
		||||
   return http_close_or_abort_conn(pcb, hs, 0);
 | 
			
		||||
  return http_close_or_abort_conn(pcb, hs, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** End of file: either close the connection (Connection: close) or
 | 
			
		||||
| 
						 | 
				
			
			@ -716,7 +755,11 @@ http_eof(struct tcp_pcb *pcb, struct http_state *hs)
 | 
			
		|||
    hs->keepalive = 1;
 | 
			
		||||
  } else
 | 
			
		||||
#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
 | 
			
		||||
  {
 | 
			
		||||
  if (hs->is_websocket) {
 | 
			
		||||
    http_state_eof(hs);
 | 
			
		||||
    http_state_init(hs);
 | 
			
		||||
    hs->is_websocket = 1;
 | 
			
		||||
  } else {
 | 
			
		||||
    http_close_conn(pcb, hs);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1915,6 +1958,60 @@ http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Parse WebSocket request */
 | 
			
		||||
  hs->is_websocket = 0;
 | 
			
		||||
  unsigned char *retval = NULL;
 | 
			
		||||
  if (strncasestr(data, WS_HEADER, data_len)) {
 | 
			
		||||
    LWIP_DEBUGF(HTTPD_DEBUG, ("WebSocket opening handshake\n"));
 | 
			
		||||
    char *key_start = strncasestr(data, "Sec-WebSocket-Key: ", data_len);
 | 
			
		||||
    if (key_start) {
 | 
			
		||||
      key_start += 19;
 | 
			
		||||
      char *key_end = strncasestr(key_start, "\r\n", data_len);
 | 
			
		||||
      if (key_end) {
 | 
			
		||||
        char key[64];
 | 
			
		||||
        int len = sizeof (char) * (key_end - key_start);
 | 
			
		||||
        if ((len + sizeof (WS_GUID) < sizeof (key)) && (len > 0)) {
 | 
			
		||||
          /* Allocate response buffer */
 | 
			
		||||
          retval = mem_malloc(WS_BUF_LEN);
 | 
			
		||||
          if (retval == NULL) {
 | 
			
		||||
            LWIP_DEBUGF(HTTPD_DEBUG, ("Out of memory\n"));
 | 
			
		||||
            return ERR_MEM;
 | 
			
		||||
          }
 | 
			
		||||
          unsigned char *retval_ptr;
 | 
			
		||||
          retval_ptr = memcpy(retval, WS_RSP, sizeof(WS_RSP));
 | 
			
		||||
          retval_ptr += sizeof(WS_RSP) - 1;
 | 
			
		||||
 | 
			
		||||
          /* Concatenate key */
 | 
			
		||||
          memcpy(key, key_start, len);
 | 
			
		||||
          strlcpy(&key[len], WS_GUID, sizeof(key));
 | 
			
		||||
          LWIP_DEBUGF(HTTPD_DEBUG, ("Resulting key: %s\n", key));
 | 
			
		||||
 | 
			
		||||
          /* Get SHA1 */
 | 
			
		||||
          int key_len = sizeof(WS_GUID) - 1 + len;
 | 
			
		||||
          unsigned char sha1sum[20];
 | 
			
		||||
          mbedtls_sha1((unsigned char *) key, key_len, sha1sum);
 | 
			
		||||
 | 
			
		||||
          /* Base64 encode */
 | 
			
		||||
          unsigned int olen;
 | 
			
		||||
          mbedtls_base64_encode(NULL, 0, &olen, sha1sum, 20); //get length
 | 
			
		||||
          int ok = mbedtls_base64_encode(retval_ptr, WS_BUF_LEN, &olen, sha1sum, 20);
 | 
			
		||||
 | 
			
		||||
          if (ok == 0) {
 | 
			
		||||
            memcpy(&retval_ptr[olen], CRLF CRLF, sizeof(CRLF CRLF));
 | 
			
		||||
            hs->is_websocket = 1;
 | 
			
		||||
            LWIP_DEBUGF(HTTPD_DEBUG, ("Base64 encoded: %s\n", retval_ptr));
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          LWIP_DEBUGF(HTTPD_DEBUG, ("Key overflow"));
 | 
			
		||||
          return ERR_MEM;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("error: malformed packet\n"));
 | 
			
		||||
      return ERR_ARG;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* received enough data for minimal request? */
 | 
			
		||||
  if (data_len >= MIN_REQ_LEN) {
 | 
			
		||||
    /* wait for CRLF before parsing anything */
 | 
			
		||||
| 
						 | 
				
			
			@ -2000,7 +2097,17 @@ http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb
 | 
			
		|||
          } else
 | 
			
		||||
#endif /* LWIP_HTTPD_SUPPORT_POST */
 | 
			
		||||
          {
 | 
			
		||||
            return http_find_file(hs, uri, is_09);
 | 
			
		||||
            if (hs->is_websocket && retval != NULL) {
 | 
			
		||||
              LWIP_DEBUGF(HTTPD_DEBUG, ("Sending:\n%s\n", retval));
 | 
			
		||||
              u16_t len = strlen((char *) retval);
 | 
			
		||||
              http_write(pcb, retval, &len, 0);
 | 
			
		||||
              mem_free(retval);
 | 
			
		||||
              if(websocket_open_cb)
 | 
			
		||||
                websocket_open_cb(pcb, uri);
 | 
			
		||||
              return ERR_OK; // We handled this
 | 
			
		||||
            } else {
 | 
			
		||||
              return http_find_file(hs, uri, is_09);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -2272,7 +2379,7 @@ http_poll(void *arg, struct tcp_pcb *pcb)
 | 
			
		|||
    return ERR_OK;
 | 
			
		||||
  } else {
 | 
			
		||||
    hs->retries++;
 | 
			
		||||
    if (hs->retries == HTTPD_MAX_RETRIES) {
 | 
			
		||||
    if (hs->retries == ((hs->is_websocket) ? WS_TIMEOUT : HTTPD_MAX_RETRIES)) {
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
 | 
			
		||||
      http_close_conn(pcb, hs);
 | 
			
		||||
      return ERR_OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -2294,6 +2401,94 @@ http_poll(void *arg, struct tcp_pcb *pcb)
 | 
			
		|||
  return ERR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
websocket_register_callbacks(tWsOpenHandler ws_open_cb, tWsHandler ws_cb)
 | 
			
		||||
{
 | 
			
		||||
  websocket_open_cb = ws_open_cb;
 | 
			
		||||
  websocket_cb = ws_cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
err_t
 | 
			
		||||
websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode)
 | 
			
		||||
{
 | 
			
		||||
  if (len > 125)
 | 
			
		||||
    return ERR_BUF;
 | 
			
		||||
 | 
			
		||||
  unsigned char buf[len + 2];
 | 
			
		||||
  buf[0] = 0x80 | mode;
 | 
			
		||||
  buf[1] = len;
 | 
			
		||||
  memcpy(&buf[2], data, len);
 | 
			
		||||
  len += 2;
 | 
			
		||||
 | 
			
		||||
  LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] sending packet\n"));
 | 
			
		||||
 | 
			
		||||
  return http_write(pcb, buf, &len, TCP_WRITE_FLAG_COPY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Send status code 1000 (normal closure).
 | 
			
		||||
 */
 | 
			
		||||
static err_t
 | 
			
		||||
websocket_send_close(struct tcp_pcb *pcb)
 | 
			
		||||
{
 | 
			
		||||
  const char buf[] = {0x88, 0x02, 0x03, 0xe8};
 | 
			
		||||
  u16_t len = sizeof (buf);
 | 
			
		||||
  LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] closing connection\n"));
 | 
			
		||||
  return tcp_write(pcb, buf, len, TCP_WRITE_FLAG_COPY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse websocket frame.
 | 
			
		||||
 *
 | 
			
		||||
 * @return ERR_OK: frame parsed
 | 
			
		||||
 *         ERR_CLSD: close request from client
 | 
			
		||||
 *         ERR_VAL: invalid frame.
 | 
			
		||||
 */
 | 
			
		||||
static err_t
 | 
			
		||||
websocket_parse(struct tcp_pcb *pcb, struct pbuf *p)
 | 
			
		||||
{
 | 
			
		||||
  unsigned char *data;
 | 
			
		||||
  data = (unsigned char*) p->payload;
 | 
			
		||||
  u16_t data_len = p->len;
 | 
			
		||||
 | 
			
		||||
  if (data != NULL && data_len > 1) {
 | 
			
		||||
    LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] frame received\n"));
 | 
			
		||||
    u8_t opcode = data[0] & 0x0F;
 | 
			
		||||
    switch (opcode) {
 | 
			
		||||
      case WS_TEXT_MODE:
 | 
			
		||||
      case WS_BIN_MODE:
 | 
			
		||||
        LWIP_DEBUGF(HTTPD_DEBUG, ("Opcode: 0x%hX, frame length: %d\n", opcode, data_len));
 | 
			
		||||
        if (data_len > 6) {
 | 
			
		||||
          u8_t len = data[1] & 0x7F;
 | 
			
		||||
          if (len > data_len || len > 125) {
 | 
			
		||||
            LWIP_DEBUGF(HTTPD_DEBUG, ("Error: large frames not supported\n"));
 | 
			
		||||
            return ERR_VAL;
 | 
			
		||||
          } else {
 | 
			
		||||
            if (data_len - 6 != len)
 | 
			
		||||
              LWIP_DEBUGF(HTTPD_DEBUG, ("Multiple frames received\n"));
 | 
			
		||||
            data_len = len;
 | 
			
		||||
          }
 | 
			
		||||
          /* unmask */
 | 
			
		||||
          for (int i = 0; i < data_len; i++)
 | 
			
		||||
            data[i + 6] = (data[i + 6] ^ data[2 + i % 4]);
 | 
			
		||||
          /* user callback */
 | 
			
		||||
          if (websocket_cb)
 | 
			
		||||
            websocket_cb(pcb, &data[6], data_len, opcode);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 0x08: // close
 | 
			
		||||
        LWIP_DEBUGF(HTTPD_DEBUG, ("Close request\n"));
 | 
			
		||||
        return ERR_CLSD;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported opcode 0x%hX\n", opcode));
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ERR_OK;
 | 
			
		||||
  }
 | 
			
		||||
  return ERR_VAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data has been received on this pcb.
 | 
			
		||||
 * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
 | 
			
		||||
| 
						 | 
				
			
			@ -2306,6 +2501,29 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
 | 
			
		|||
  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb,
 | 
			
		||||
    (void*)p, lwip_strerr(err)));
 | 
			
		||||
 | 
			
		||||
  if (hs != NULL && hs->is_websocket) {
 | 
			
		||||
    if (p == NULL) {
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: buffer error\n"));
 | 
			
		||||
      http_close_or_abort_conn(pcb, hs, 0);
 | 
			
		||||
      return ERR_BUF;
 | 
			
		||||
    }
 | 
			
		||||
    tcp_recved(pcb, p->tot_len);
 | 
			
		||||
 | 
			
		||||
    err_t err = websocket_parse(pcb, p);
 | 
			
		||||
 | 
			
		||||
    if (p != NULL) {
 | 
			
		||||
      /* otherwise tcp buffer hogs */
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] freeing buffer\n"));
 | 
			
		||||
      pbuf_free(p);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (err == ERR_CLSD) {
 | 
			
		||||
      http_close_conn(pcb, hs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ERR_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
 | 
			
		||||
    /* error or closed by other side? */
 | 
			
		||||
    if (p != NULL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2349,7 +2567,8 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
 | 
			
		|||
    if (hs->handle == NULL) {
 | 
			
		||||
      parsed = http_parse_request(&p, hs, pcb);
 | 
			
		||||
      LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
 | 
			
		||||
        || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE);
 | 
			
		||||
        || parsed == ERR_INPROGRESS ||parsed == ERR_ARG
 | 
			
		||||
        || parsed == ERR_USE || parsed == ERR_MEM);
 | 
			
		||||
    } else {
 | 
			
		||||
      LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2375,7 +2594,7 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
 | 
			
		|||
        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", hs->file, hs->left));
 | 
			
		||||
        http_send(pcb, hs);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (parsed == ERR_ARG) {
 | 
			
		||||
    } else if (parsed == ERR_ARG || parsed == ERR_MEM) {
 | 
			
		||||
      /* @todo: close on ERR_USE? */
 | 
			
		||||
      http_close_conn(pcb, hs);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,7 @@
 | 
			
		|||
#include "lwip/opt.h"
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "lwip/pbuf.h"
 | 
			
		||||
#include "lwip/tcp.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** Set this to 1 to support CGI */
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +57,6 @@
 | 
			
		|||
#define LWIP_HTTPD_SUPPORT_POST   0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if LWIP_HTTPD_CGI
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -231,6 +231,33 @@ void httpd_post_data_recved(void *connection, u16_t recved_len);
 | 
			
		|||
 | 
			
		||||
#endif /* LWIP_HTTPD_SUPPORT_POST */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  WS_TEXT_MODE = 0x01,
 | 
			
		||||
  WS_BIN_MODE  = 0x02,
 | 
			
		||||
} WS_MODE;
 | 
			
		||||
 | 
			
		||||
typedef void (*tWsHandler)(struct tcp_pcb *pcb, uint8_t *data, u16_t data_len, uint8_t mode);
 | 
			
		||||
typedef void (*tWsOpenHandler)(struct tcp_pcb *pcb, const char *uri);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Write data into a websocket.
 | 
			
		||||
 *
 | 
			
		||||
 * @param pcb tcp_pcb to send.
 | 
			
		||||
 * @param data data to send.
 | 
			
		||||
 * @param len data length (should not exceed 125).
 | 
			
		||||
 * @param mode WS_TEXT_MODE or WS_BIN_MODE.
 | 
			
		||||
 * @return ERR_OK if write succeeded.
 | 
			
		||||
 */
 | 
			
		||||
err_t websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Register websocket callback functions. Use NULL if callback is not needed.
 | 
			
		||||
 *
 | 
			
		||||
 * @param ws_open_cb called when new websocket is opened.
 | 
			
		||||
 * @param ws_cb called when data is received from client.
 | 
			
		||||
 */
 | 
			
		||||
void websocket_register_callbacks(tWsOpenHandler ws_open_cb, tWsHandler ws_cb);
 | 
			
		||||
 | 
			
		||||
void httpd_init(void);
 | 
			
		||||
 | 
			
		||||
#endif /* __HTTPD_H__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,2 +1,4 @@
 | 
			
		|||
Maintained by lujji (https://github.com/lujji/esp-httpd).
 | 
			
		||||
 | 
			
		||||
Note: this module expects your project to provide "fsdata.c" created with "makefsdata" utility.
 | 
			
		||||
See examples/http_server.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										80
									
								
								extras/httpd/strcasestr.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								extras/httpd/strcasestr.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
/*-
 | 
			
		||||
 * Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
 | 
			
		||||
 * Copyright (c) 1990, 1993
 | 
			
		||||
 *	The Regents of the University of California.  All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This code is derived from software contributed to Berkeley by
 | 
			
		||||
 * Chris Torek.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of the University nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | 
			
		||||
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
			
		||||
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
			
		||||
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
 * SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
#include "strcasestr.h"
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
strcasestr(s, find)
 | 
			
		||||
const char *s, *find;
 | 
			
		||||
{
 | 
			
		||||
    char c, sc;
 | 
			
		||||
    size_t len;
 | 
			
		||||
 | 
			
		||||
    if ((c = *find++) != 0) {
 | 
			
		||||
        c = tolower((unsigned char) c);
 | 
			
		||||
        len = strlen(find);
 | 
			
		||||
        do {
 | 
			
		||||
            do {
 | 
			
		||||
                if ((sc = *s++) == 0)
 | 
			
		||||
                    return (NULL);
 | 
			
		||||
            } while ((char) tolower((unsigned char) sc) != c);
 | 
			
		||||
        } while (strncasecmp(s, find, len) != 0);
 | 
			
		||||
        s--;
 | 
			
		||||
    }
 | 
			
		||||
    return ((char *) s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
strncasestr(s, find, slen)
 | 
			
		||||
const char *s;
 | 
			
		||||
const char *find;
 | 
			
		||||
size_t slen;
 | 
			
		||||
{
 | 
			
		||||
    char c, sc;
 | 
			
		||||
    size_t len;
 | 
			
		||||
 | 
			
		||||
    if ((c = *find++) != '\0') {
 | 
			
		||||
        len = strlen(find);
 | 
			
		||||
        do {
 | 
			
		||||
            do {
 | 
			
		||||
                if (slen-- < 1 || (sc = *s++) == '\0')
 | 
			
		||||
                    return (NULL);
 | 
			
		||||
            } while (sc != c);
 | 
			
		||||
            if (len > slen)
 | 
			
		||||
                return (NULL);
 | 
			
		||||
        } while (strncasecmp(s, find, len) != 0);
 | 
			
		||||
        s--;
 | 
			
		||||
    }
 | 
			
		||||
    return ((char *) s);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								extras/httpd/strcasestr.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								extras/httpd/strcasestr.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
#ifndef STRCASESTR_H
 | 
			
		||||
#define STRCASESTR_H
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
char *strcasestr(const char *s, const char *find);
 | 
			
		||||
char *strncasestr(const char *s, const char * find, size_t slen);
 | 
			
		||||
 | 
			
		||||
#endif /* STRCASESTR_H */
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue