-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
488 lines (450 loc) · 24.1 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# !/usr/bin/python
# PM: based on http://blog.wachowicz.eu/?p=256
# PM: added POST handling
# import signal # Signal support (server shutdown on signal receive)
# KN: Added connection to local network, server execution mode
# KN: Added support for 150 KB
# AM: Added operation between two devices
from machine import SD
from network import LoRa
from network import WLAN
from time import time
import binascii
import json
import gc
import machine
import network
import os
import select
import socket
import ubinascii
import utime
import ufun # AM: Used to handle the leds in the lopy
from tabla import BaseDatos # AM: Management of user and messages
import posthandler # PM: code to be executed to handle a POST
import swlp # AM: LoRa Protocol
RED = 0xFF0000
YELLOW = 0xFFFF33
GREEN = 0x007F00
PINK =0x6b007f
BLUE = 0x005e63
OFF = 0x000000
# LoRA parameters to work with raspberry
freq=869000000 # def.: frequency=868000000
tx_pow=14 # def.: tx_power=14
band=LoRa.BW_125KHZ # def.: bandwidth=LoRa.868000000
spreadf=7 # def.: sf=7
prea=8 # def.: preamble=8
cod_rate=LoRa.CODING_4_5 # def.: coding_rate=LoRa.CODING_4_5
pow_mode=LoRa.ALWAYS_ON # def.: power_mode=LoRa.ALWAYS_ON
tx_iq_inv=False # def.: tx_iq=false
rx_iq_inv=False # def.: rx_iq=false
ada_dr=False # def.: adr=false
pub=False # def.: public=true
tx_retr=1 # def.: tx_retries=1
region=LoRa.EU868 # def.: region=LoRa.EU868 just for LoPy4
dev_class=LoRa.CLASS_A # def.: device_class=LoRa.CLASS_A
flag_mode=0
WEB_PAGES_HOME_DIR = '/flash' # Directory where webpage files are stored
ANY_ADDR = b'FFFFFFFF'
DEBUG_MODE = False
VERBOSE_MODE = False
NORMAL_MODE = False
class Server:
""" Class describing a simple HTTP server objects."""
def __init__(self, port, mode):
""" Constructor """
self.host = '' # <-- works on all avaivable network interfaces
self.port = port
self.www_dir = WEB_PAGES_HOME_DIR
self.flag_null = 0
self.userR = ""
self.modep=mode # int number 1: Debug Mode 2:Verbose Mode 3:Normal Mode
def activate_server(self):
""" Attempts to aquire the socket and launch the server """
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: # user provided in the __init__() port may be unavaivable
if (self.modep == 1): print ("Launching HTTP server on ", self.host, ":", self.port)
self.socket.bind((self.host, self.port))
except Exception as e:
print ("Warning: Could not acquire port:", self.port, "\n")
print ("I will try a higher port")
# store to user provided port locally for later (in case 8080 fails)
user_port = self.port
self.port = 8080
try:
print ("Launching HTTP server on ", self.host, ":", self.port)
self.socket.bind((self.host, self.port))
except Exception as e:
print ("ERROR: Failed to acquire sockets for ports ", user_port, " and 8080. ")
print ("Try running the Server in a privileged user mode.")
self.shutdown()
import sys
sys.exit(1)
if (self.modep == 1): print ("Server successfully acquired the socket with port:", self.port)
print ("Press Ctrl+C to shut down the server and exit.")
if (self.modep == 1): print ("Awaiting New connection")
self.socket.listen(3) # Maximum number of queued connections
def connectionLoRa(self): # Function to create LoRa socket
try:
self.s_right = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
self.loramac = binascii.hexlify(network.LoRa().mac())
if (self.modep == 1): print ("Socket Created") # AM: Acquisition socket LoRa
except socket.error:
exit('Error creating socket.')
def shutdown(self):
""" Shut down the server """
try:
print ("Shutting down the server")
s.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
print ("Warning: could not shut down the socket. Maybe it was already closed...", e)
def _gen_headers(self, code):
""" Generates HTTP response Headers. """
# determine response code
h = ''
if (code == 200):
h = 'HTTP/1.1 200 OK\n'
elif (code == 404):
h = 'HTTP/1.1 404 Not Found\n'
# write rest of the headers
# current_date = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
# PM: should find an alternative for LoPys
current_date = '4 Agosto 1965'
h += 'Date: ' + current_date +'\n'
h += 'Server: LoRa-Messaging\n'
h += 'Connection: close\n\n' # signal that the conection will be closed after completing the request
return h
def _wait_for_connections(self, s_left, addr, treq):
# determine request method (HEAD and GET are supported) (PM: added support to POST )
request_method = treq.split(' ')[0]
if (self.modep == 1):
print ("Method: ", request_method)
print ("Full HTTP message: -->")
print (treq) # KN: Enable this print to see the value of treq in debug mode
print ("<--")
treqhead = treq.split("\r\n\r\n")[0]
print("treqhead:", treqhead)
treqbody = treq[len(treqhead):].lstrip() # PM: makes easier to handle various types of newlines
if (self.modep == 1):
print ("only the HTTP body: -->")
print (treqbody) # KN: Enable this print to see the value of treqbody in debug mode
print ("<--")
# split on space "GET /file.html" -into-> ('GET','file.html',...)
file_requested = treq.split(' ')
#if(self.modep==1): print("Debug Server: File Requested: ", file_requested) # KN: Enable this print to see the file requested in debug mode
if (file_requested == ''):
file_requested = '/index.html'
file_requested = file_requested[1] # get 2nd element
# Check for URL arguments. Disregard them
file_requested = file_requested.split('?')[0] # disregard anything after '?'
if (file_requested == '/'): # in case no file is specified by the browser
file_requested = '/index.html' # load index.html by default
elif (file_requested == '/favicon.ico'): # most browsers ask for this file...
file_requested = '/index.html' # ...giving them index.html instead
file_requested = self.www_dir + file_requested
if (self.modep == 1): print ("Serving web page ["+file_requested+"]")
# GET method
if (request_method == 'GET') | (request_method == 'HEAD') :
## Load file content
if (self.modep == 1): print ("file_requested: ", file_requested)
try:
gc.collect()
if (request_method == 'GET' and file_requested =='/flash/registro'):
if (self.modep == 1): print ('Regreso del Usuario',self.userR)
tabla = BaseDatos(self.modep)
response_content,self.userR = tabla.ingresoRegistro(self.userR,1)
if (self.modep == 1): print ('Datos Regreso', response_content)
else:
file_handler = open(file_requested,'rb')
if (request_method == 'GET'): # only read the file when GET
response_content = file_handler.read() # read file content
file_handler.close()
response_headers = self._gen_headers(200)
if (self.modep == 1): print ('response_headers', response_headers)
except Exception as e: # in case file was not found, generate 404 page
error_str = str(e)
if (error_str[:24] == 'memory allocation failed'):
print ("Warning, memory allocation failed. Serving response code 500"+" -> "+error_str)
response_headers = self._gen_headers(500)
if (request_method == 'GET'):
response_content = b"<html><body><p>Error 500: Memory allocation failed</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
else:
print ("Warning, file not found from GET. Serving response code 404\n", e)
response_headers = self._gen_headers( 404)
if (request_method == 'GET'):
response_content = b"<html><head><meta charset='utf-8'><title>LoRa</title></head><body><p>Error 404: File not found</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
server_response = response_headers.encode() # return headers for GET and HEAD
if (request_method == 'GET'):
server_response += response_content # return additional content for GET only
s_left.send(server_response)
if (self.modep == 1): print ("Closing connection with client")
ufun.set_led_to(OFF)
s_left.close()
# POST method
elif (request_method == 'POST'):
## Load file content
try:
if (file_requested.find("execposthandler") != -1):
if (self.modep == 1):
print ("... PM: running python code")
print ("DEBUG Server: lenght message:", len(treqbody))
if (len(treqbody) > 25):
self.form = treqbody
response_content, self.dest_lora_address = posthandler.run(treqbody,self.s_right,self.loramac,self.userR,0, self.modep)
else:
if (self.modep == 1): print ("... PM: empty POST received")
response_content = b"<html><body><p>Error: EMPTY FORM RECEIVED, Please Check Again</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
elif (file_requested.find("tabla") != -1):
if (self.modep == 1): print ("DEBUG Server: Checking Messages")
if (self.modep == 2): print ("Checking Messages")
if (self.modep == 3): print ("Checking messages")
gc.collect()
tabla = BaseDatos(self.modep)
response_content = tabla.consulta(self.userR)
elif (file_requested.find("registro") != -1):
if (self.modep == 1):
print ("DEBUG Server: Register")
print ("DEBUG Server: lenght user:", len(treqbody))
print ("DEBUG Server: treqbody:", treqbody)
tabla = BaseDatos(self.modep)
if (len(treqbody) > 12 ):
response_content,self.userR = tabla.ingresoRegistro(treqbody,0)
gc.collect()
print ("Register Ok")
else:
print ("... PM: empty POST received")
response_content = b"<html><body><p>Error: Please Choose a username</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
# Handling sensors data: START
elif (file_requested.find("mqttproxypush") != -1):
if (self.modep == 1):
print ("DEBUG Server: MqttProxy PUSH request")
print ("DEBUG Server: lenght user:", len(treqbody))
print ("DEBUG Server: treqbody:", treqbody)
if (len(treqbody) > 10 ): # to be adapted!!!
print ("MqttProxy PUSH request Ok")
response_content = b"<html><body><p>MqttProxy PUSH request</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
else:
print ("MqttProxy request ERROR")
response_content = b"<html><body><p>ERROR MqttProxy PUSH request</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
# value 3 in "broadcast field" indicates that is anycasting, that is the closest with the service
response_content, self.dest_lora_address = posthandler.run(treqbody,self.s_right,self.loramac,self.userR,3, self.modep)
elif (file_requested.find("mqttproxypop") != -1):
if (self.modep == 1):
print ("DEBUG Server: MqttProxy POP request")
print ("DEBUG Server: lenght user:", len(treqbody))
print ("DEBUG Server: treqbody:", treqbody)
if (len(treqbody) > 10 ): # to be adapted!!!
print ("MqttProxy POP request Ok")
response_content = b"<html><body><p>MqttProxy POP request</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
else:
print ("MqttProxy request ERROR")
response_content = b"<html><body><p>ERROR MqttProxy POP request</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
gc.collect()
tabla = BaseDatos(self.modep)
response_content = tabla.consulta("mqttproxy")
# Handling sensors data: END
elif (file_requested.find("broadcast") != -1):
if (self.modep == 1 | self.modep ==2 ): print ("AM: Sending Message Broadcast")
if (self.modep == 3): print ("Message Broadcast sent")
gc.collect()
tabla = BaseDatos(self.modep)
response_content, self.dest_lora_address = posthandler.run(treqbody,self.s_right,self.loramac,self.userR,1, self.modep)
elif (file_requested.find("telegram") != -1):
print ("AM: Telegram Message")
tabla = BaseDatos(self.modep)
if (self.modep == 1): print ("DEBUG Server: lenght message:", len(treqbody))
if (len(treqbody) > 25):
response_content, self.dest_lora_address = posthandler.run(treqbody,self.s_right,self.loramac,self.userR,2, self.modep)
else:
print ("... AM: empty POST received")
response_content = b"<html><body><p>Error: EMPTY FORM RECEIVED, Please Check Again</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
elif (file_requested.find("resend") != -1):
if (self.modep == 1): print ("DEBUG Server: Resending message")
response_content = posthandler.resend(self.form, self.s_right, self.loramac, self.userR, self.dest_lora_address, self.modep)
else:
file_handler = open(file_requested,'rb')
response_content = file_handler.read() # read file content
file_handler.close()
response_headers = self._gen_headers(200)
except Exception as e: # in case file was not found, generate 404 page
print ("Warning, file not found. Serving response code 404\n", e)
response_headers = self._gen_headers(404)
response_content = b"<html><body><p>Error 404: File not found</p><p>Python HTTP server</p><p><a href='/'>Back to home</a></p></body></html>"
server_response = response_headers.encode() # return headers
server_response += response_content # return additional content
s_left.send(server_response)
if (self.modep == 1): print ("Closing connection with client")
ufun.set_led_to(OFF)
s_left.close()
else:
print ("Unknown HTTP request method:", request_method)
# KN: Function to receive all the message via TCP
def checking_connection(self,s_left,addr):
data = b""
data = s_left.recv(1024)
while True:
check_header = bytes.decode(data)
check_header_list = (check_header.split('\r\n')) #Create list to check header
check_header_dict = {}
for element in check_header_list: #Create a dict
s_element = (str(element))
if (s_element.find(':')) != -1:
keyValue = element.split(': ')
check_header_dict[keyValue[0]] = keyValue[1]
content_length = int(check_header_dict.get('Content-Length', 0)) #Content lenght
checking_header = check_header.split("\r\n\r\n")[0]
checking_body = check_header[len(checking_header):].lstrip()
rec_body = len(checking_body)
r_data = content_length - rec_body
if (self.modep == 1): print ("DEBUG Server: Remaining Body: ", r_data)
if r_data != 0:
data += s_left.recv((1024))
check_headerx = bytes.decode(data)
else:
if (self.modep == 1): print ("DEBUG Server: Data Received")
break
if (self.modep == 1): print ("Got connection from: ", addr)
#if(self.modep==1): print("DEBUG Server: Data received: ", data) # KN: Enable this print to see the data received in debug mode
if (data == b""):
if (self.modep == 1 | self.modep == 2): print ("Null Method, Discarding")
else:
treq = bytes.decode(data)
self._wait_for_connections(s_left,addr,treq)
def conexion(self): # Function in charge of the coordination of sockets
ANY_ADDR = b'FFFFFFFFFFFFFFFF'
while True:
s_read, _, _ = select.select([self.socket, self.s_right], [], [])
for a in s_read:
if a == self.socket:
# reading data from the HTTP channel
if (self.modep == 1): print ("DEBUG Server: In connections_handler: reading data from the HTTP channel")
s_left, addr = self.socket.accept()
self.checking_connection(s_left,addr)
elif a == self.s_right:
# reading data from the LORA channel using swlpv3
if (self.modep == 1): print ("DEBUG Server: reading data from the LORA channel using swlpv3")
ufun.flash_led_to(YELLOW)
data,sender = swlp.trecvcontrol(self.s_right, my_lora_address, ANY_ADDR, self.modep)
LoRaRec(data,self.s_right,sender)
if (self.modep == 1):
#print ("DEBUG Server: Done reading data from the LORA channel using swlpv3:", data) # KN: Enable this print to see the reading data from the LoRa channel in debug mode
print ("The End")
ufun.flash_led_to(OFF)
###################################################################################
def LoRaRec(data,socket,source_address):
bandera = 0
mensaje = b""
tabla = BaseDatos(mode_print)
my_lora_address = binascii.hexlify(network.LoRa().mac())
if (mode_print == 1):
#print ("DEBUG Server: Content in reception LoRa", data) # KN: Enable this print to see the content in reception LoRa in debug mode
print ("DEBUG Server: Source Address in LoRaRec ", source_address)
if (source_address == ANY_ADDR):
content2 = str(data) # Capturing the data, and changing the format
IPlora,user_raw = content2.split(",")
if (IPlora == "b'FFFFFFFraspbsend'") or (IPlora == b'FFFFFFFraspberry'):
if (mode_print == 1): print ("DEBUG Server: It's the raspberry IP")
if (mode_print == 1): print ("DEBUG Server: IP Lora: ", str(IPlora))
lenght = len(user_raw)
userf = user_raw[:lenght - 1]
if (userf == "broadcast"): # Message to all users
message_broadcast = str(IPlora[2:])
tabla = BaseDatos(mode_print)
if (mode_print == 1): print ("DEBUG Server: Message Broadcast received", message_broadcast)
posthandler.broadcast(message_broadcast, mode_print) # Function to save the broadcast message
IPloraf = IPlora[4:]
if (mode_print == 1): print ("DEBUG Server: User ", userf)
bandera = posthandler.consultat(userf, mode_print) # Checking if the user is in the database
if (mode_print == 1): print ("DEBUG Server: Flag ", bandera)
if bandera == 1: # The user is in the database, I'm going to respond
if (mode_print == 1): print ("DEBUG Server: Lora Address ", IPloraf)
sent, retrans, sent, notsend = swlp.tsend(my_lora_address, socket, my_lora_address, IPloraf, mode_print) # Function to send a LoRa Message using the protocol
elif (source_address == my_lora_address[8:]): # The message is for me, I'm going to save it
message_raw = data
#if (mode_print == 1): print ("DEBUG Server: Message in server", message_raw) # KN: Enable this print to see the message in server in debug mode
if (mode_print == 2): print ("Receiving message")
if (message_raw == b"Failed"):
print ("Reception Failed, Discarding")
elif (message_raw != b"") and (message_raw != b"Failed"):
mensajet = str(message_raw)
print("XXXYYYraw", message_raw)
# handling sensors data... there must be a better way to send and receive json
if (mensajet[-10:-1] == "mqttproxy"):
aaa = mensajet.find("{")
zzz = mensajet.find("}")
user_final = "mqttproxy"
messagef = str(mensajet[aaa:zzz+1])
idEmisor = str(mensajet[2:aaa-1])
if (mode_print == 1):
print ("Sender: " + idEmisor)
print ("Message: " + messagef) # KN: Enable this print to see the message in debug mode
print ("User: " + user_final)
tabla.ingreso(idEmisor,user_final,messagef) # Function to save the message in the database
else:
idEmisor, messagef, user_final = mensajet.split(",")
if (mode_print == 1):
print ("Sender: " + str(idEmisor[1:]))
#print ("Message: " + str(messagef)) # KN: Enable this print to see the message in debug mode
print ("User: " + str(user_final))
lenght = len(user_final)
userf = user_final[:lenght - 1]
tabla.ingreso(idEmisor[2:],userf,messagef) # Function to save the message in the database
################################################################################################################################
def choose_mode(): # KN: Execution mode assignment
while True:
mode = str.lower(input('Choose the execution mode ("Debug" "Verbose" "Normal"): '))
if mode == "debug":
modemes = 1
print ("--> Running server in DEBUG mode")
break
elif mode == "verbose":
modemes = 2
print ("--> Running server in VERBOSE mode")
break
elif mode == "normal":
modemes = 3
print ("--> Running server in NORMAL mode")
break
else:
print ("--> Unrecognized execution mode")
return modemes
mode_print = choose_mode() # Function to choose print mode 1:Debug Mode 2:Verbose Mode 3:Normal Mode
# Enabling garbage collection
gc.enable()
gc.collect()
if (mode_print == 1): print ("mem_free: ", gc.mem_free())
try:
sd = SD()
os.mount(sd, '/sd')
if (mode_print == 1): print("SD Card Enabled")
except:
print("WARNING: no SD Card")
# Starting LoRa
lora = LoRa(mode=LoRa.LORA,
frequency=freq,
tx_power=tx_pow,
bandwidth=band,
sf=spreadf,
preamble=prea,
coding_rate=cod_rate,
power_mode=pow_mode,
tx_iq=tx_iq_inv,
rx_iq=rx_iq_inv,
adr=ada_dr,
public=pub,
tx_retries=tx_retr,
region=LoRa.EU868,
device_class=dev_class)
# The lopy is configured as an Access Point and HTTP server
my_lora_address = binascii.hexlify(network.LoRa().mac())
lopy_name = "messenger_" + my_lora_address.decode()[-3:]
print ("Network Name: " + lopy_name)
wlan = WLAN()
wlan.init(mode=WLAN.STA_AP, ssid=lopy_name, auth=None, channel=7, antenna=WLAN.INT_ANT)
tabla = BaseDatos(mode_print) # Instance Class Database
print ("Starting web server")
s = Server(80, mode_print) # Construct server object
s.activate_server() # Acquire the socket
s.connectionLoRa() # Acquire Socket LoRa
s.conexion()