#define
ENABLE_WS_AUTHORIZATION
#include
<ESP8266WiFi.h>
#include
<ESP8266WebServer.h>
#include
<WebSocketsServer.h>
#include
<base64.h>
#include
<FS.h>
#include
"Hash.h"
#include
<DHT12.h>
#include
<ArduinoJson.h>
const
char
*
ssid
=
"<YOUR-SSID>"
;
const
char
*
password
=
"<YOUR-PASSWD>"
;
const
char
*
www_username
=
"admin"
;
const
char
*
www_password
=
"esp8266"
;
String ws_auth_code;
const
uint8_t wsPort
=
81
;
int8_t connectionNumber
=
0
;
unsigned
long
messageInterval
=
2000
;
DHT12 dht12;
ESP8266WebServer httpServer(
80
);
WebSocketsServer webSocket
=
WebSocketsServer(wsPort);
void
serverRouting();
void
manageSecurity();
#ifdef
ENABLE_WS_AUTHORIZATION
bool validateHttpHeader(String headerName, String headerValue);
#endif
void
setup
(
void
) {
Serial.begin
(
115200
);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println
(
""
);
dht12.begin();
while
(WiFi.status()
!
=
WL_CONNECTED) {
delay
(
500
);
Serial.print
(
"."
);
}
Serial.println
(
""
);
Serial.print
(
"Connected to "
);
Serial.println
(ssid);
Serial.print
(
"IP address: "
);
Serial.println
(WiFi.localIP());
Serial.print
(F(
"Inizializing FS..."
));
if
(SPIFFS.begin()) {
Serial.println
(F(
"done."
));
}
else
{
Serial.println
(F(
"fail."
));
}
Serial.println
(
"Set routing for http server!"
);
serverRouting();
httpServer.begin();
Serial.println
(
"HTTP server started"
);
Serial.print
(
"Starting WebSocket.."
);
#ifdef
ENABLE_WS_AUTHORIZATION
const
char
*
headerkeys[]
=
{
"Cookie"
};
size_t headerKeyCount
=
sizeof
(headerkeys)
/
sizeof
(
char
*
);
webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);
#endif
webSocket.begin();
webSocket.onEvent(webSocketEvent);
Serial.println
(
"OK"
);
}
unsigned
long
lastUpdate
=
millis
()
+
messageInterval;
void
loop
(
void
) {
httpServer.handleClient();
webSocket.
loop
();
if
(connectionNumber>
0
&
&
lastUpdate
+
messageInterval<
millis
()){
Serial.println
(
"[WSc] SENT: Broadcast message!!"
);
const
size_t capacity
=
1024
;
DynamicJsonDocument doc(capacity);
doc[
"humidity"
]
=
dht12.readHumidity();
doc[
"temp"
]
=
dht12.readTemperature();
doc[
"humidity"
]
=
random
(
10
,
80
);
doc[
"temp"
]
=
random
(
1000
,
3500
)
/
100
.;
String buf;
serializeJson(doc, buf);
webSocket.broadcastTXT(buf);
lastUpdate
=
millis
();
}
}
String getContentType(String filename) {
if
(filename.endsWith(F(
".htm"
)))
return
F(
"text/html"
);
else
if
(filename.endsWith(F(
".html"
)))
return
F(
"text/html"
);
else
if
(filename.endsWith(F(
".css"
)))
return
F(
"text/css"
);
else
if
(filename.endsWith(F(
".js"
)))
return
F(
"application/javascript"
);
else
if
(filename.endsWith(F(
".json"
)))
return
F(
"application/json"
);
else
if
(filename.endsWith(F(
".png"
)))
return
F(
"image/png"
);
else
if
(filename.endsWith(F(
".gif"
)))
return
F(
"image/gif"
);
else
if
(filename.endsWith(F(
".jpg"
)))
return
F(
"image/jpeg"
);
else
if
(filename.endsWith(F(
".jpeg"
)))
return
F(
"image/jpeg"
);
else
if
(filename.endsWith(F(
".ico"
)))
return
F(
"image/x-icon"
);
else
if
(filename.endsWith(F(
".xml"
)))
return
F(
"text/xml"
);
else
if
(filename.endsWith(F(
".pdf"
)))
return
F(
"application/x-pdf"
);
else
if
(filename.endsWith(F(
".zip"
)))
return
F(
"application/x-zip"
);
else
if
(filename.endsWith(F(
".gz"
)))
return
F(
"application/x-gzip"
);
return
F(
"text/plain"
);
}
bool handleFileRead(String path) {
Serial.print
(F(
"handleFileRead: "
));
Serial.println
(path);
if
(
!
is_authenticated()) {
Serial.println
(F(
"Go on not login!"
));
path
=
F(
"/login.html"
);
handleLogout();
return
true
;
}
else
{
if
(path.endsWith(
"/"
)) path
+
=
F(
"index.html"
);
}
String contentType
=
getContentType(path);
String pathWithGz
=
path
+
F(
".gz"
);
if
(SPIFFS.exists(pathWithGz)
|
|
SPIFFS.exists(path)) {
if
(SPIFFS.exists(pathWithGz)) {
path
+
=
F(
".gz"
);
}
fs::File file
=
SPIFFS.open(path,
"r"
);
size_t sent
=
httpServer.streamFile(file, contentType);
file.close();
Serial.println
(
String(F(
"\tSent file: "
))
+
path
+
String(F(
" of size "
))
+
sent);
return
true
;
}
Serial.println
(String(F(
"\tFile Not Found: "
))
+
path);
return
false
;
}
void
handleNotFound() {
String message
=
"File Not Found\n\n"
;
message
+
=
"URI: "
;
message
+
=
httpServer.uri();
message
+
=
"\nMethod: "
;
message
+
=
(httpServer.method()
=
=
HTTP_GET)
?
"GET"
:
"POST"
;
message
+
=
"\nArguments: "
;
message
+
=
httpServer.args();
message
+
=
"\n"
;
for
(uint8_t i
=
0
; i < httpServer.args(); i
+
+
) {
message
+
=
" "
+
httpServer.argName(i)
+
": "
+
httpServer.arg(i)
+
"\n"
;
}
httpServer.send(
404
,
"text/plain"
, message);
}
void
handleLogin() {
Serial.println
(
"Handle login"
);
String msg;
if
(httpServer.hasHeader(
"Cookie"
)) {
Serial.print
(
"Found cookie: "
);
String cookie
=
httpServer.header(
"Cookie"
);
Serial.println
(cookie);
}
if
(httpServer.hasArg(
"username"
)
&
&
httpServer.hasArg(
"password"
)) {
Serial.print
(
"Found parameter: "
);
if
(httpServer.arg(
"username"
)
=
=
String(www_username)
&
&
httpServer.arg(
"password"
)
=
=
String(www_password)) {
httpServer.sendHeader(
"Location"
,
"/"
);
httpServer.sendHeader(
"Cache-Control"
,
"no-cache"
);
String token
=
sha1(String(www_username)
+
":"
+
String(www_password)
+
":"
+
httpServer.client().remoteIP().toString());
httpServer.sendHeader(
"Set-Cookie"
,
"ESPSESSIONID="
+
token);
httpServer.sendHeader(
"Set-Cookie"
,
"CLIENTIP="
+
httpServer.client().remoteIP().toString());
httpServer.send(
301
);
Serial.println
(
"Log in Successful"
);
return
;
}
msg
=
"Wrong username/password! try again."
;
Serial.println
(
"Log in Failed"
);
httpServer.sendHeader(
"Location"
,
"/login.html?msg="
+
msg);
httpServer.sendHeader(
"Cache-Control"
,
"no-cache"
);
httpServer.send(
301
);
return
;
}
}
void
handleLogout() {
Serial.println
(
"Disconnection"
);
httpServer.sendHeader(
"Location"
,
"/login.html?msg=User disconnected"
);
httpServer.sendHeader(
"Cache-Control"
,
"no-cache"
);
httpServer.sendHeader(
"Set-Cookie"
,
"ESPSESSIONID=0"
);
httpServer.send(
301
);
return
;
}
void
handleTemperatureHumidity(){
Serial.println
(
"handleTemperatureHumidity"
);
manageSecurity();
Serial.println
(
"handleTemperatureHumidity security pass!"
);
const
size_t capacity
=
1024
;
DynamicJsonDocument doc(capacity);
doc[
"humidity"
]
=
dht12.readHumidity();
doc[
"temp"
]
=
dht12.readTemperature();
doc[
"humidity"
]
=
random
(
10
,
80
);
doc[
"temp"
]
=
random
(
1000
,
3500
)
/
100
.;
String buf;
serializeJson(doc, buf);
httpServer.send(
200
, F(
"application/json"
), buf);
}
bool is_authenticated() {
Serial.println
(
"Enter is_authenticated"
);
if
(httpServer.hasHeader(
"Cookie"
)) {
Serial.print
(
"Found cookie: "
);
String cookie
=
httpServer.header(
"Cookie"
);
Serial.println
(cookie);
String token
=
sha1(String(www_username)
+
":"
+
String(www_password)
+
":"
+
httpServer.client().remoteIP().toString());
if
(cookie.indexOf(
"ESPSESSIONID="
+
token)
!
=
-
1
) {
Serial.println
(
"Authentication Successful"
);
return
true
;
}
}
Serial.println
(
"Authentication Failed"
);
return
false
;
}
void
manageSecurity(){
if
(
!
is_authenticated()) {
httpServer.send(
401
, F(
"application/json"
),
"{\"msg\": \"You must authenticate!\"}"
);
return
;
}
}
void
restEndPoint(){
httpServer.on(
"/login"
, HTTP_POST, handleLogin);
httpServer.on(
"/logout"
, HTTP_GET, handleLogout);
httpServer.on(
"/temperatureHumidity"
, HTTP_GET, handleTemperatureHumidity);
}
void
serverRouting() {
restEndPoint();
Serial.println
(F(
"Go on not found!"
));
httpServer.onNotFound([]() {
Serial.println
(F(
"On not found"
));
if
(
!
handleFileRead(httpServer.uri())) {
handleNotFound();
}
});
Serial.println
(F(
"Set cache!"
));
httpServer.serveStatic(
"/configuration.json"
, SPIFFS,
"/configuration.json"
,
"no-cache, no-store, must-revalidate"
);
httpServer.serveStatic(
"/"
, SPIFFS,
"/"
,
"max-age=31536000"
);
const
char
*
headerkeys[]
=
{
"User-Agent"
,
"Cookie"
};
size_t headerkeyssize
=
sizeof
(headerkeys)
/
sizeof
(
char
*
);
httpServer.collectHeaders(headerkeys, headerkeyssize);
}
void
webSocketEvent(uint8_t num, WStype_t type, uint8_t
*
payload, size_t length) {
switch
(type) {
case
WStype_DISCONNECTED:
Serial.printf(
"[%u] Disconnected!\n"
, num);
webSocket.sendTXT(num,
"{\"connected\":false}"
);
connectionNumber
=
webSocket.connectedClients();
Serial.print
(
"Connected devices "
);
Serial.println
(connectionNumber);
break
;
case
WStype_CONNECTED:
{
IPAddress ip
=
webSocket.remoteIP(num);
Serial.printf(
"[%u] Connected from %d.%d.%d.%d url: %s\n"
, num, ip[
0
], ip[
1
], ip[
2
], ip[
3
], payload);
webSocket.sendTXT(num,
"{\"connected\":true}"
);
connectionNumber
=
webSocket.connectedClients();
Serial.print
(
"Connected devices "
);
Serial.println
(connectionNumber);
}
break
;
case
WStype_TEXT:
Serial.printf(
"[%u] RECEIVE TXT: %s\n"
, num, payload);
webSocket.sendTXT(num,
"(ECHO MESSAGE) "
+
String((
char
*
)payload));
break
;
case
WStype_BIN:
Serial.printf(
"[%u] get binary length: %u\n"
, num, length);
hexdump(payload, length);
break
;
}
}
#ifdef
ENABLE_WS_AUTHORIZATION
bool isCookieValid(String rawCookieHeaderValue) {
Serial.print
(
"Cookie validation "
);
Serial.println
(rawCookieHeaderValue);
String clientIP
=
""
;
if
(rawCookieHeaderValue.indexOf(
"CLIENTIP"
)
!
=
-
1
) {
clientIP
=
rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf(
"CLIENTIP="
)
+
9
, rawCookieHeaderValue.indexOf(
"|"
));
Serial.print
(
"clientIP "
);
Serial.println
(clientIP);
}
if
(rawCookieHeaderValue.indexOf(
"ESPSESSIONID"
)
!
=
-
1
) {
String tokenStr
=
rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf(
"ESPSESSIONID="
)
+
13
, rawCookieHeaderValue.indexOf(
"ESPSESSIONID="
)
+
13
+
40
);
Serial.print
(
"ESPSESSIONID "
);
Serial.println
(tokenStr);
String token
=
sha1(String(www_username)
+
":"
+
String(www_password)
+
":"
+
clientIP);
Serial.print
(
"token "
);
Serial.println
(token);
return
tokenStr
=
=
token;
}
return
false
;
}
bool validateHttpHeader(String headerName, String headerValue) {
bool valid
=
true
;
if
(headerName.equalsIgnoreCase(
"Cookie"
)) {
valid
=
isCookieValid(headerValue);
}
return
valid;
}
#endif