modulitiz-micro 2.31.0__py311-none-any.whl → 2.32.0__py311-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modulitiz_micro/ModuloBase64.py +61 -61
- modulitiz_micro/ModuloColorText.py +35 -35
- modulitiz_micro/ModuloDate.py +295 -295
- modulitiz_micro/ModuloFunzioni.py +70 -58
- modulitiz_micro/ModuloListe.py +150 -150
- modulitiz_micro/ModuloMeteo.py +72 -72
- modulitiz_micro/ModuloNumeri.py +130 -130
- modulitiz_micro/ModuloPyinstaller.py +29 -29
- modulitiz_micro/ModuloSeriale.py +61 -61
- modulitiz_micro/ModuloStatistiche.py +31 -31
- modulitiz_micro/ModuloStringhe.py +180 -180
- modulitiz_micro/ModuloTarghe.py +46 -46
- modulitiz_micro/android/ModuloAndroid.py +18 -18
- modulitiz_micro/android/ModuloAndroidAdb.py +48 -48
- modulitiz_micro/android/ModuloAndroidSim.py +130 -130
- modulitiz_micro/android/beans/SmsBean.py +12 -12
- modulitiz_micro/android/enums/AndroidSmsTypeEnum.py +17 -17
- modulitiz_micro/database/AbstractDatabaseService.py +13 -13
- modulitiz_micro/database/AbstractSql.py +69 -69
- modulitiz_micro/database/ModuloSqlOracle.py +19 -19
- modulitiz_micro/database/ModuloSqlServer.py +43 -43
- modulitiz_micro/database/eccezioni/EccezioneDbNoData.py +6 -6
- modulitiz_micro/database/mysql/AbstractBasicMysql.py +114 -114
- modulitiz_micro/database/mysql/ModuloMysql.py +163 -163
- modulitiz_micro/database/mysql/MysqlCommonConverter.py +47 -47
- modulitiz_micro/database/mysql/eccezioni/EccezioneMysqlOffline.py +6 -6
- modulitiz_micro/database/sqlite/AbstractBasicSQLite.py +114 -114
- modulitiz_micro/database/sqlite/ModuloSQLite.py +82 -82
- modulitiz_micro/eccezioni/EccezioneBase.py +7 -7
- modulitiz_micro/eccezioni/EccezioneCtrlC.py +7 -7
- modulitiz_micro/eccezioni/EccezioneRuntime.py +7 -7
- modulitiz_micro/eccezioni/EccezioneScheduler.py +7 -7
- modulitiz_micro/eccezioni/EccezioneSoNonSupportato.py +7 -7
- modulitiz_micro/eccezioni/http/EccezioneHttp.py +8 -8
- modulitiz_micro/eccezioni/http/EccezioneHttp404.py +7 -7
- modulitiz_micro/eccezioni/http/EccezioneHttpGeneric.py +7 -7
- modulitiz_micro/files/ModuloFiles.py +173 -173
- modulitiz_micro/files/ModuloLogging.py +69 -69
- modulitiz_micro/files/ModuloZip.py +42 -42
- modulitiz_micro/files/cache/CacheBean.py +5 -5
- modulitiz_micro/files/cache/CacheRam.py +29 -29
- modulitiz_micro/files/cache/DatabaseCache.py +91 -91
- modulitiz_micro/files/cache/decorators/cacheRam.py +26 -26
- modulitiz_micro/files/git/ModuloGit.py +20 -20
- modulitiz_micro/files/git/decorators/catchAndRaiseGitExceptions.py +19 -19
- modulitiz_micro/files/git/exceptions/EccezioneGit.py +7 -7
- modulitiz_micro/gestionedom/GestioneDom.py +44 -44
- modulitiz_micro/init/AbstractBasicInit.py +27 -27
- modulitiz_micro/init/AbstractInit.py +11 -11
- modulitiz_micro/keylogger/EccezioneKeyLogger.py +7 -7
- modulitiz_micro/keylogger/ModuloKeylogger.py +73 -73
- modulitiz_micro/{multithreading → multithread}/ModuloThread.py +26 -26
- modulitiz_micro/{multithreading → multithread}/ModuloThreadLogger.py +8 -8
- modulitiz_micro/{multithreading → multithread}/ModuloThreadWithCallbackError.py +25 -25
- modulitiz_micro/nlp/ModuloNlp.py +36 -36
- modulitiz_micro/nlp/ModuloNlpDateAndTime.py +59 -59
- modulitiz_micro/rete/ModuloNetworking.py +67 -67
- modulitiz_micro/rete/ModuloOpenVpn.py +15 -15
- modulitiz_micro/rete/email/EmailBean.py +5 -5
- modulitiz_micro/rete/email/ModuloEmail.py +90 -90
- modulitiz_micro/rete/http/ModuloHttp.py +115 -115
- modulitiz_micro/rete/http/ModuloHttpConnectionSafe.py +91 -91
- modulitiz_micro/rete/http/ModuloHttpUtils.py +69 -69
- modulitiz_micro/rete/http/beans/HttpResponseBean.py +5 -5
- modulitiz_micro/rete/http/decorators/catchAndRaiseHttpExceptions.py +22 -22
- modulitiz_micro/rete/ntp/AbstractModuloNtp.py +73 -73
- modulitiz_micro/rete/ntp/ModuloNtpIt.py +8 -8
- modulitiz_micro/rete/socketserver/AbstractBasicGetSocketServer.py +35 -35
- modulitiz_micro/rete/socketserver/AbstractSocketServer.py +267 -267
- modulitiz_micro/rete/ssl/ModuloSsl.py +56 -56
- modulitiz_micro/sistema/EnvVarsEnum.py +9 -9
- modulitiz_micro/sistema/ModuloEnvVars.py +34 -34
- modulitiz_micro/sistema/ModuloSystem.py +298 -298
- modulitiz_micro/sistema/ModuloSystemPipe.py +67 -67
- modulitiz_micro/social/telegram/ModuloTelegram.py +52 -52
- modulitiz_micro/social/telegram/ModuloTelegramSimple.py +26 -26
- modulitiz_micro/util/beans/conf/AbstractBasicConfBean.py +11 -11
- modulitiz_micro/util/beans/conf/AbstractConfBean.py +16 -16
- modulitiz_micro/util/beans/fileconf/AbstractBasicFileConfBean.py +11 -11
- modulitiz_micro/util/beans/fileconf/AbstractFileConfBean.py +13 -13
- modulitiz_micro/util/beans/globalvar/AbstractBasicGlobalVarBean.py +15 -15
- modulitiz_micro/util/beans/globalvar/AbstractGlobalVarBean.py +34 -34
- modulitiz_micro/util/decorators/noAwait.py +23 -23
- modulitiz_micro/util/pip/AbstractModuloPip.py +41 -41
- modulitiz_micro/util/pip/ModuloPip.py +49 -49
- modulitiz_micro/util/scheduler/AbstractScheduler.py +32 -32
- modulitiz_micro/util/spooler/AbstractSpooler.py +14 -14
- modulitiz_micro/util/spooler/Spooler.py +18 -18
- modulitiz_micro/util/spooler/beans/QueueBean.py +8 -8
- modulitiz_micro/util/spooler/decorators/spooler.py +49 -49
- modulitiz_micro/util/spooler/eccezioni/EccezioneSpooler.py +7 -7
- modulitiz_micro/util/spooler/eccezioni/EccezioneSpoolerFull.py +7 -7
- modulitiz_micro/util/unittesting/AbstractOverrideTestUtil.py +31 -31
- modulitiz_micro/util/unittesting/AbstractTestUtil.py +11 -11
- modulitiz_micro/util/unittesting/ModuloRunUnitTest.py +25 -25
- modulitiz_micro/util/wheel/ModuloBuildWheel.py +131 -121
- modulitiz_micro/util/wheel/ModuloCheckTestNamingConvention.py +86 -0
- modulitiz_micro/util/wheel/ModuloToml.py +40 -40
- modulitiz_micro/util/wheel/ModuloWheel.py +12 -12
- {modulitiz_micro-2.31.0.dist-info → modulitiz_micro-2.32.0.dist-info}/LICENSE +21 -21
- {modulitiz_micro-2.31.0.dist-info → modulitiz_micro-2.32.0.dist-info}/METADATA +64 -64
- modulitiz_micro-2.32.0.dist-info/RECORD +104 -0
- modulitiz_micro-2.31.0.dist-info/RECORD +0 -103
- {modulitiz_micro-2.31.0.dist-info → modulitiz_micro-2.32.0.dist-info}/WHEEL +0 -0
- {modulitiz_micro-2.31.0.dist-info → modulitiz_micro-2.32.0.dist-info}/top_level.txt +0 -0
@@ -1,267 +1,267 @@
|
|
1
|
-
import errno
|
2
|
-
import os
|
3
|
-
import socket
|
4
|
-
import ssl
|
5
|
-
import time
|
6
|
-
from _io import BufferedReader
|
7
|
-
from abc import ABC
|
8
|
-
from abc import abstractmethod
|
9
|
-
|
10
|
-
from modulitiz_micro.ModuloListe import ModuloListe
|
11
|
-
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
12
|
-
from modulitiz_micro.eccezioni.EccezioneRuntime import EccezioneRuntime
|
13
|
-
from modulitiz_micro.files.ModuloFiles import ModuloFiles
|
14
|
-
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
15
|
-
from modulitiz_micro.multithreading.ModuloThread import ModuloThread
|
16
|
-
from modulitiz_micro.rete.ModuloNetworking import ModuloNetworking
|
17
|
-
from modulitiz_micro.rete.http.ModuloHttp import ModuloHttp
|
18
|
-
from modulitiz_micro.rete.http.ModuloHttpUtils import ModuloHttpUtils
|
19
|
-
from modulitiz_micro.rete.ssl.ModuloSsl import ModuloSsl
|
20
|
-
|
21
|
-
|
22
|
-
class AbstractSocketServer(ABC):
|
23
|
-
HTTP_CODES={
|
24
|
-
ModuloHttp.STATUS_OK:"OK",
|
25
|
-
404:"Not Found",
|
26
|
-
500:"Internal Server Error",
|
27
|
-
501:"Not Implemented",
|
28
|
-
}
|
29
|
-
|
30
|
-
MAX_CONNECTIONS=10
|
31
|
-
TIMEOUT_SERVER=30
|
32
|
-
TIMEOUT_CLIENT=60
|
33
|
-
|
34
|
-
TIPO_RESPONSE__PLAIN_TEXT=1
|
35
|
-
TIPO_RESPONSE__STREAM=2
|
36
|
-
|
37
|
-
def __init__(self,logger:ModuloLogging,porta:int,percorsoFileCertSsl:str):
|
38
|
-
self._logger=logger
|
39
|
-
self.__porta=porta
|
40
|
-
self.__percorsoFileCertSsl=percorsoFileCertSsl
|
41
|
-
|
42
|
-
self.isRequestedExit=False
|
43
|
-
self.countActiveConnections=0
|
44
|
-
self.contextSsl=None
|
45
|
-
self.serverSocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
46
|
-
|
47
|
-
def populate(self):
|
48
|
-
# controllo che la porta non sia gia' occupata
|
49
|
-
if self.isOpen():
|
50
|
-
raise EccezioneRuntime(f"La porta {self.__porta} e' gia' aperta")
|
51
|
-
# ssl
|
52
|
-
if self.__percorsoFileCertSsl is not None:
|
53
|
-
# se il certificato non esiste lo creo
|
54
|
-
if ModuloFiles.getFileSize(self.__percorsoFileCertSsl)==-1:
|
55
|
-
self._logger.info("Generazione certificato...")
|
56
|
-
moduloSsl=ModuloSsl()
|
57
|
-
moduloSsl.createSelfSignedCertAnonymous()
|
58
|
-
with ModuloFiles.open(self.__percorsoFileCertSsl,"w+t") as fp:
|
59
|
-
fp.write(moduloSsl.certStr)
|
60
|
-
contextSsl=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
61
|
-
contextSsl.load_cert_chain(certfile=self.__percorsoFileCertSsl)
|
62
|
-
self.contextSsl=contextSsl
|
63
|
-
# creo la socket
|
64
|
-
self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
65
|
-
self.serverSocket.bind(('0.0.0.0', self.__porta))
|
66
|
-
self.serverSocket.listen(self.MAX_CONNECTIONS)
|
67
|
-
self.serverSocket.settimeout(self.TIMEOUT_SERVER)
|
68
|
-
|
69
|
-
@abstractmethod
|
70
|
-
def callbackError(self,ex: Exception):
|
71
|
-
"""
|
72
|
-
Cosa fare in caso di errore della socket
|
73
|
-
"""
|
74
|
-
|
75
|
-
@abstractmethod
|
76
|
-
def handleRequest(self,method:str,url:str,path:str,params:dict,httpType:str,clientIp:str,clientPort:int)->tuple:
|
77
|
-
"""
|
78
|
-
Cosa fare se un client contatta il server.
|
79
|
-
Deve ritornare una tupla: (httpCode,header,body)
|
80
|
-
"""
|
81
|
-
|
82
|
-
def isOpen(self)->bool:
|
83
|
-
return ModuloNetworking.isHttpPortOpen(None, self.__porta)
|
84
|
-
|
85
|
-
def avvia(self):
|
86
|
-
protocolSuffix="S" if self.contextSsl is not None else ""
|
87
|
-
self._logger.info("Server in ascolto sulla porta HTTP%s %d",protocolSuffix,self.__porta)
|
88
|
-
ModuloThread.startDaemonWithCallbackError(self.callbackError,self.__avviaLoopNoThreaded,())
|
89
|
-
|
90
|
-
def chiudi(self):
|
91
|
-
if not self.isOpen():
|
92
|
-
return
|
93
|
-
self.isRequestedExit=True
|
94
|
-
while self.countActiveConnections>0:
|
95
|
-
time.sleep(0.5)
|
96
|
-
# qui socket.shutdown() non ci vuole perche' deve essere usato solo lato client
|
97
|
-
self.serverSocket.close()
|
98
|
-
|
99
|
-
def __avviaLoopNoThreaded(self):
|
100
|
-
while not self.isRequestedExit:
|
101
|
-
self.__avviaNoThreaded()
|
102
|
-
self.countActiveConnections=0
|
103
|
-
def __avviaNoThreaded(self):
|
104
|
-
# aspetto una richiesta
|
105
|
-
try:
|
106
|
-
clientConnection,(clientIp,clientPort)=self.serverSocket.accept()
|
107
|
-
except TimeoutError:
|
108
|
-
clientConnection=None
|
109
|
-
clientIp=None
|
110
|
-
clientPort=None
|
111
|
-
except OSError as osError:
|
112
|
-
if osError.errno not in(errno.ENOTSOCK,errno.EBADF):
|
113
|
-
self.callbackError(osError)
|
114
|
-
clientConnection=None
|
115
|
-
clientIp=None
|
116
|
-
clientPort=None
|
117
|
-
if clientConnection is None:
|
118
|
-
return
|
119
|
-
self.countActiveConnections+=1
|
120
|
-
# richiedo i dati
|
121
|
-
request=None
|
122
|
-
try:
|
123
|
-
clientConnection.settimeout(self.TIMEOUT_CLIENT)
|
124
|
-
if self.contextSsl is not None:
|
125
|
-
clientConnection=self.contextSsl.wrap_socket(clientConnection,server_side=True)
|
126
|
-
request=clientConnection.recv(1024)
|
127
|
-
except (ssl.SSLEOFError,ssl.SSLZeroReturnError,TimeoutError):
|
128
|
-
self.countActiveConnections-=1
|
129
|
-
except ssl.SSLError as ex:
|
130
|
-
if ex.reason not in(
|
131
|
-
"NO_SHARED_CIPHER",
|
132
|
-
"TLSV1_ALERT_UNKNOWN_CA",
|
133
|
-
"SSLV3_ALERT_BAD_CERTIFICATE","SSLV3_ALERT_CERTIFICATE_UNKNOWN",
|
134
|
-
"HTTP_REQUEST","HTTPS_PROXY_REQUEST",
|
135
|
-
"UNKNOWN_PROTOCOL","WRONG_VERSION_NUMBER","WRONG_SSL_VERSION"):
|
136
|
-
self.callbackError(ex)
|
137
|
-
self.countActiveConnections-=1
|
138
|
-
except OSError as ex:
|
139
|
-
self.__handleOsError(ex)
|
140
|
-
self.countActiveConnections-=1
|
141
|
-
if request is None:
|
142
|
-
clientConnection.close()
|
143
|
-
return
|
144
|
-
# decodifico la richiesta
|
145
|
-
try:
|
146
|
-
request=request.decode(ModuloStringhe.CODIFICA_UTF8)
|
147
|
-
except UnicodeDecodeError:
|
148
|
-
request = None
|
149
|
-
self.countActiveConnections -= 1
|
150
|
-
if request is None:
|
151
|
-
clientConnection.close()
|
152
|
-
return
|
153
|
-
ModuloThread.startDaemonWithCallbackError(self.callbackError,self.__handleRequestNoThreaded,(request,clientConnection,clientIp,clientPort))
|
154
|
-
|
155
|
-
def __handleRequestNoThreaded(self,request:str|None,clientConnection,clientIp:str,clientPort:int):
|
156
|
-
valori=self.__parseRequest(request)
|
157
|
-
if valori is not None:
|
158
|
-
httpCode,responseHeader,responseBody=self.handleRequest(*valori,clientIp,clientPort)
|
159
|
-
response,tipo=self.__parseResponse(httpCode,responseHeader,responseBody)
|
160
|
-
else:
|
161
|
-
responseBody=None
|
162
|
-
response,tipo=self.__parseResponse(500,None,None)
|
163
|
-
|
164
|
-
if tipo==self.TIPO_RESPONSE__PLAIN_TEXT:
|
165
|
-
self.__sendResponse(clientConnection,response)
|
166
|
-
elif tipo==self.TIPO_RESPONSE__STREAM and responseBody is not None:
|
167
|
-
self.__sendResponse(clientConnection,response)
|
168
|
-
# se e' un file pointer leggo e invio un po' alla volta
|
169
|
-
fp=responseBody
|
170
|
-
chunk=fp.read(1024)
|
171
|
-
while chunk:
|
172
|
-
clientConnection.send(chunk)
|
173
|
-
chunk=fp.read(1024)
|
174
|
-
fp.close()
|
175
|
-
else:
|
176
|
-
response,tipo=self.__parseResponse(500,None,None)
|
177
|
-
self.__sendResponse(clientConnection,response)
|
178
|
-
try:
|
179
|
-
clientConnection.shutdown(socket.SHUT_RDWR)
|
180
|
-
except OSError as e:
|
181
|
-
if e.errno!=errno.ENOTCONN:
|
182
|
-
raise e
|
183
|
-
clientConnection.close()
|
184
|
-
self.countActiveConnections-=1
|
185
|
-
|
186
|
-
@staticmethod
|
187
|
-
def __parseRequest(request:str)->tuple|None:
|
188
|
-
request=ModuloStringhe.normalizzaEol(request)
|
189
|
-
index=request.find('\n\n')
|
190
|
-
if index==-1:
|
191
|
-
return None
|
192
|
-
headers=request[0:index]
|
193
|
-
headers=headers.split("\n")
|
194
|
-
# la prima riga dell'header deve contenere 3 info
|
195
|
-
header=headers[0]
|
196
|
-
if ModuloStringhe.isEmpty(header):
|
197
|
-
return None
|
198
|
-
arr=header.split()
|
199
|
-
method=ModuloListe.collectionSafeGet(arr,0)
|
200
|
-
url=ModuloListe.collectionSafeGet(arr,1)
|
201
|
-
httpType=ModuloListe.collectionSafeGet(arr,2)
|
202
|
-
if method is None or url is None or httpType is None:
|
203
|
-
return None
|
204
|
-
|
205
|
-
url=ModuloHttpUtils.decodeUrl(url)
|
206
|
-
if not ModuloStringhe.contains(url,"?"):
|
207
|
-
return method,url,url,{},httpType
|
208
|
-
# controllo url
|
209
|
-
path,params=url.split("?",1)
|
210
|
-
path=path[1:]
|
211
|
-
params=params.split("&")
|
212
|
-
params=[x.split('=') for x in params]
|
213
|
-
params=dict(zip([x[0] for x in params],[x[1] if len(x)>=2 else None for x in params]))
|
214
|
-
return method,url,path,params,httpType
|
215
|
-
|
216
|
-
def __parseResponse(self,httpCode:int,responseHeader:str|None,responseBody)->tuple:
|
217
|
-
if responseHeader is None:
|
218
|
-
responseHeader=""
|
219
|
-
if responseBody is None:
|
220
|
-
responseBody=""
|
221
|
-
|
222
|
-
lunghezza=None
|
223
|
-
tipo=None
|
224
|
-
filename=None
|
225
|
-
if isinstance(responseBody,str):
|
226
|
-
contentType="text/plain; charset=UTF-8"#TODO: se cors usare contentType dell'origine
|
227
|
-
tipo=self.TIPO_RESPONSE__PLAIN_TEXT
|
228
|
-
responseBody=ModuloStringhe.normalizzaEol(responseBody.rstrip())
|
229
|
-
lunghezza=len(responseBody.encode(ModuloStringhe.CODIFICA_UTF8))
|
230
|
-
elif isinstance(responseBody,BufferedReader):
|
231
|
-
contentType="application/octet-stream"
|
232
|
-
tipo=self.TIPO_RESPONSE__STREAM
|
233
|
-
filename=os.path.basename(responseBody.name)
|
234
|
-
else:
|
235
|
-
contentType="text/plain; charset=UTF-8"
|
236
|
-
httpCode=501
|
237
|
-
|
238
|
-
responseHeader+=("HTTP/1.1 %d %s"%(httpCode,self.HTTP_CODES.get(httpCode,"")))
|
239
|
-
if httpCode==ModuloHttp.STATUS_OK:
|
240
|
-
if lunghezza is not None:
|
241
|
-
responseHeader+="\nContent-Length: "+str(lunghezza)
|
242
|
-
if filename is not None:
|
243
|
-
responseHeader+='\nContent-Disposition: attachment; filename="'+filename+'"'
|
244
|
-
responseHeader+=f"""
|
245
|
-
Access-Control-Allow-Origin: *
|
246
|
-
Content-Type: {contentType}
|
247
|
-
Connection: close
|
248
|
-
X-Robots-Tag: noindex, nofollow"""
|
249
|
-
#compongo response
|
250
|
-
response=responseHeader+"\n\n"
|
251
|
-
if tipo==self.TIPO_RESPONSE__PLAIN_TEXT:
|
252
|
-
response+=responseBody
|
253
|
-
response=ModuloStringhe.normalizzaEol(response)
|
254
|
-
response=response.encode(ModuloStringhe.CODIFICA_UTF8)
|
255
|
-
return response,tipo
|
256
|
-
|
257
|
-
@staticmethod
|
258
|
-
def __sendResponse(clientConnection,response):
|
259
|
-
try:
|
260
|
-
clientConnection.sendall(response)
|
261
|
-
except (ConnectionResetError,BrokenPipeError,ssl.SSLEOFError,ssl.SSLZeroReturnError):
|
262
|
-
pass
|
263
|
-
|
264
|
-
def __handleOsError(self,ex:OSError):
|
265
|
-
if ex.errno in (errno.ECONNRESET,errno.ETIMEDOUT):
|
266
|
-
return
|
267
|
-
self.callbackError(ex)
|
1
|
+
import errno
|
2
|
+
import os
|
3
|
+
import socket
|
4
|
+
import ssl
|
5
|
+
import time
|
6
|
+
from _io import BufferedReader
|
7
|
+
from abc import ABC
|
8
|
+
from abc import abstractmethod
|
9
|
+
|
10
|
+
from modulitiz_micro.ModuloListe import ModuloListe
|
11
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
12
|
+
from modulitiz_micro.eccezioni.EccezioneRuntime import EccezioneRuntime
|
13
|
+
from modulitiz_micro.files.ModuloFiles import ModuloFiles
|
14
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
15
|
+
from modulitiz_micro.multithreading.ModuloThread import ModuloThread
|
16
|
+
from modulitiz_micro.rete.ModuloNetworking import ModuloNetworking
|
17
|
+
from modulitiz_micro.rete.http.ModuloHttp import ModuloHttp
|
18
|
+
from modulitiz_micro.rete.http.ModuloHttpUtils import ModuloHttpUtils
|
19
|
+
from modulitiz_micro.rete.ssl.ModuloSsl import ModuloSsl
|
20
|
+
|
21
|
+
|
22
|
+
class AbstractSocketServer(ABC):
|
23
|
+
HTTP_CODES={
|
24
|
+
ModuloHttp.STATUS_OK:"OK",
|
25
|
+
404:"Not Found",
|
26
|
+
500:"Internal Server Error",
|
27
|
+
501:"Not Implemented",
|
28
|
+
}
|
29
|
+
|
30
|
+
MAX_CONNECTIONS=10
|
31
|
+
TIMEOUT_SERVER=30
|
32
|
+
TIMEOUT_CLIENT=60
|
33
|
+
|
34
|
+
TIPO_RESPONSE__PLAIN_TEXT=1
|
35
|
+
TIPO_RESPONSE__STREAM=2
|
36
|
+
|
37
|
+
def __init__(self,logger:ModuloLogging,porta:int,percorsoFileCertSsl:str):
|
38
|
+
self._logger=logger
|
39
|
+
self.__porta=porta
|
40
|
+
self.__percorsoFileCertSsl=percorsoFileCertSsl
|
41
|
+
|
42
|
+
self.isRequestedExit=False
|
43
|
+
self.countActiveConnections=0
|
44
|
+
self.contextSsl=None
|
45
|
+
self.serverSocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
46
|
+
|
47
|
+
def populate(self):
|
48
|
+
# controllo che la porta non sia gia' occupata
|
49
|
+
if self.isOpen():
|
50
|
+
raise EccezioneRuntime(f"La porta {self.__porta} e' gia' aperta")
|
51
|
+
# ssl
|
52
|
+
if self.__percorsoFileCertSsl is not None:
|
53
|
+
# se il certificato non esiste lo creo
|
54
|
+
if ModuloFiles.getFileSize(self.__percorsoFileCertSsl)==-1:
|
55
|
+
self._logger.info("Generazione certificato...")
|
56
|
+
moduloSsl=ModuloSsl()
|
57
|
+
moduloSsl.createSelfSignedCertAnonymous()
|
58
|
+
with ModuloFiles.open(self.__percorsoFileCertSsl,"w+t") as fp:
|
59
|
+
fp.write(moduloSsl.certStr)
|
60
|
+
contextSsl=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
61
|
+
contextSsl.load_cert_chain(certfile=self.__percorsoFileCertSsl)
|
62
|
+
self.contextSsl=contextSsl
|
63
|
+
# creo la socket
|
64
|
+
self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
65
|
+
self.serverSocket.bind(('0.0.0.0', self.__porta))
|
66
|
+
self.serverSocket.listen(self.MAX_CONNECTIONS)
|
67
|
+
self.serverSocket.settimeout(self.TIMEOUT_SERVER)
|
68
|
+
|
69
|
+
@abstractmethod
|
70
|
+
def callbackError(self,ex: Exception):
|
71
|
+
"""
|
72
|
+
Cosa fare in caso di errore della socket
|
73
|
+
"""
|
74
|
+
|
75
|
+
@abstractmethod
|
76
|
+
def handleRequest(self,method:str,url:str,path:str,params:dict,httpType:str,clientIp:str,clientPort:int)->tuple:
|
77
|
+
"""
|
78
|
+
Cosa fare se un client contatta il server.
|
79
|
+
Deve ritornare una tupla: (httpCode,header,body)
|
80
|
+
"""
|
81
|
+
|
82
|
+
def isOpen(self)->bool:
|
83
|
+
return ModuloNetworking.isHttpPortOpen(None, self.__porta)
|
84
|
+
|
85
|
+
def avvia(self):
|
86
|
+
protocolSuffix="S" if self.contextSsl is not None else ""
|
87
|
+
self._logger.info("Server in ascolto sulla porta HTTP%s %d",protocolSuffix,self.__porta)
|
88
|
+
ModuloThread.startDaemonWithCallbackError(self.callbackError,self.__avviaLoopNoThreaded,())
|
89
|
+
|
90
|
+
def chiudi(self):
|
91
|
+
if not self.isOpen():
|
92
|
+
return
|
93
|
+
self.isRequestedExit=True
|
94
|
+
while self.countActiveConnections>0:
|
95
|
+
time.sleep(0.5)
|
96
|
+
# qui socket.shutdown() non ci vuole perche' deve essere usato solo lato client
|
97
|
+
self.serverSocket.close()
|
98
|
+
|
99
|
+
def __avviaLoopNoThreaded(self):
|
100
|
+
while not self.isRequestedExit:
|
101
|
+
self.__avviaNoThreaded()
|
102
|
+
self.countActiveConnections=0
|
103
|
+
def __avviaNoThreaded(self):
|
104
|
+
# aspetto una richiesta
|
105
|
+
try:
|
106
|
+
clientConnection,(clientIp,clientPort)=self.serverSocket.accept()
|
107
|
+
except TimeoutError:
|
108
|
+
clientConnection=None
|
109
|
+
clientIp=None
|
110
|
+
clientPort=None
|
111
|
+
except OSError as osError:
|
112
|
+
if osError.errno not in(errno.ENOTSOCK,errno.EBADF):
|
113
|
+
self.callbackError(osError)
|
114
|
+
clientConnection=None
|
115
|
+
clientIp=None
|
116
|
+
clientPort=None
|
117
|
+
if clientConnection is None:
|
118
|
+
return
|
119
|
+
self.countActiveConnections+=1
|
120
|
+
# richiedo i dati
|
121
|
+
request=None
|
122
|
+
try:
|
123
|
+
clientConnection.settimeout(self.TIMEOUT_CLIENT)
|
124
|
+
if self.contextSsl is not None:
|
125
|
+
clientConnection=self.contextSsl.wrap_socket(clientConnection,server_side=True)
|
126
|
+
request=clientConnection.recv(1024)
|
127
|
+
except (ssl.SSLEOFError,ssl.SSLZeroReturnError,TimeoutError):
|
128
|
+
self.countActiveConnections-=1
|
129
|
+
except ssl.SSLError as ex:
|
130
|
+
if ex.reason not in(
|
131
|
+
"NO_SHARED_CIPHER",
|
132
|
+
"TLSV1_ALERT_UNKNOWN_CA",
|
133
|
+
"SSLV3_ALERT_BAD_CERTIFICATE","SSLV3_ALERT_CERTIFICATE_UNKNOWN",
|
134
|
+
"HTTP_REQUEST","HTTPS_PROXY_REQUEST",
|
135
|
+
"UNKNOWN_PROTOCOL","WRONG_VERSION_NUMBER","WRONG_SSL_VERSION"):
|
136
|
+
self.callbackError(ex)
|
137
|
+
self.countActiveConnections-=1
|
138
|
+
except OSError as ex:
|
139
|
+
self.__handleOsError(ex)
|
140
|
+
self.countActiveConnections-=1
|
141
|
+
if request is None:
|
142
|
+
clientConnection.close()
|
143
|
+
return
|
144
|
+
# decodifico la richiesta
|
145
|
+
try:
|
146
|
+
request=request.decode(ModuloStringhe.CODIFICA_UTF8)
|
147
|
+
except UnicodeDecodeError:
|
148
|
+
request = None
|
149
|
+
self.countActiveConnections -= 1
|
150
|
+
if request is None:
|
151
|
+
clientConnection.close()
|
152
|
+
return
|
153
|
+
ModuloThread.startDaemonWithCallbackError(self.callbackError,self.__handleRequestNoThreaded,(request,clientConnection,clientIp,clientPort))
|
154
|
+
|
155
|
+
def __handleRequestNoThreaded(self,request:str|None,clientConnection,clientIp:str,clientPort:int):
|
156
|
+
valori=self.__parseRequest(request)
|
157
|
+
if valori is not None:
|
158
|
+
httpCode,responseHeader,responseBody=self.handleRequest(*valori,clientIp,clientPort)
|
159
|
+
response,tipo=self.__parseResponse(httpCode,responseHeader,responseBody)
|
160
|
+
else:
|
161
|
+
responseBody=None
|
162
|
+
response,tipo=self.__parseResponse(500,None,None)
|
163
|
+
|
164
|
+
if tipo==self.TIPO_RESPONSE__PLAIN_TEXT:
|
165
|
+
self.__sendResponse(clientConnection,response)
|
166
|
+
elif tipo==self.TIPO_RESPONSE__STREAM and responseBody is not None:
|
167
|
+
self.__sendResponse(clientConnection,response)
|
168
|
+
# se e' un file pointer leggo e invio un po' alla volta
|
169
|
+
fp=responseBody
|
170
|
+
chunk=fp.read(1024)
|
171
|
+
while chunk:
|
172
|
+
clientConnection.send(chunk)
|
173
|
+
chunk=fp.read(1024)
|
174
|
+
fp.close()
|
175
|
+
else:
|
176
|
+
response,tipo=self.__parseResponse(500,None,None)
|
177
|
+
self.__sendResponse(clientConnection,response)
|
178
|
+
try:
|
179
|
+
clientConnection.shutdown(socket.SHUT_RDWR)
|
180
|
+
except OSError as e:
|
181
|
+
if e.errno!=errno.ENOTCONN:
|
182
|
+
raise e
|
183
|
+
clientConnection.close()
|
184
|
+
self.countActiveConnections-=1
|
185
|
+
|
186
|
+
@staticmethod
|
187
|
+
def __parseRequest(request:str)->tuple|None:
|
188
|
+
request=ModuloStringhe.normalizzaEol(request)
|
189
|
+
index=request.find('\n\n')
|
190
|
+
if index==-1:
|
191
|
+
return None
|
192
|
+
headers=request[0:index]
|
193
|
+
headers=headers.split("\n")
|
194
|
+
# la prima riga dell'header deve contenere 3 info
|
195
|
+
header=headers[0]
|
196
|
+
if ModuloStringhe.isEmpty(header):
|
197
|
+
return None
|
198
|
+
arr=header.split()
|
199
|
+
method=ModuloListe.collectionSafeGet(arr,0)
|
200
|
+
url=ModuloListe.collectionSafeGet(arr,1)
|
201
|
+
httpType=ModuloListe.collectionSafeGet(arr,2)
|
202
|
+
if method is None or url is None or httpType is None:
|
203
|
+
return None
|
204
|
+
|
205
|
+
url=ModuloHttpUtils.decodeUrl(url)
|
206
|
+
if not ModuloStringhe.contains(url,"?"):
|
207
|
+
return method,url,url,{},httpType
|
208
|
+
# controllo url
|
209
|
+
path,params=url.split("?",1)
|
210
|
+
path=path[1:]
|
211
|
+
params=params.split("&")
|
212
|
+
params=[x.split('=') for x in params]
|
213
|
+
params=dict(zip([x[0] for x in params],[x[1] if len(x)>=2 else None for x in params]))
|
214
|
+
return method,url,path,params,httpType
|
215
|
+
|
216
|
+
def __parseResponse(self,httpCode:int,responseHeader:str|None,responseBody)->tuple:
|
217
|
+
if responseHeader is None:
|
218
|
+
responseHeader=""
|
219
|
+
if responseBody is None:
|
220
|
+
responseBody=""
|
221
|
+
|
222
|
+
lunghezza=None
|
223
|
+
tipo=None
|
224
|
+
filename=None
|
225
|
+
if isinstance(responseBody,str):
|
226
|
+
contentType="text/plain; charset=UTF-8"#TODO: se cors usare contentType dell'origine
|
227
|
+
tipo=self.TIPO_RESPONSE__PLAIN_TEXT
|
228
|
+
responseBody=ModuloStringhe.normalizzaEol(responseBody.rstrip())
|
229
|
+
lunghezza=len(responseBody.encode(ModuloStringhe.CODIFICA_UTF8))
|
230
|
+
elif isinstance(responseBody,BufferedReader):
|
231
|
+
contentType="application/octet-stream"
|
232
|
+
tipo=self.TIPO_RESPONSE__STREAM
|
233
|
+
filename=os.path.basename(responseBody.name)
|
234
|
+
else:
|
235
|
+
contentType="text/plain; charset=UTF-8"
|
236
|
+
httpCode=501
|
237
|
+
|
238
|
+
responseHeader+=("HTTP/1.1 %d %s"%(httpCode,self.HTTP_CODES.get(httpCode,"")))
|
239
|
+
if httpCode==ModuloHttp.STATUS_OK:
|
240
|
+
if lunghezza is not None:
|
241
|
+
responseHeader+="\nContent-Length: "+str(lunghezza)
|
242
|
+
if filename is not None:
|
243
|
+
responseHeader+='\nContent-Disposition: attachment; filename="'+filename+'"'
|
244
|
+
responseHeader+=f"""
|
245
|
+
Access-Control-Allow-Origin: *
|
246
|
+
Content-Type: {contentType}
|
247
|
+
Connection: close
|
248
|
+
X-Robots-Tag: noindex, nofollow"""
|
249
|
+
#compongo response
|
250
|
+
response=responseHeader+"\n\n"
|
251
|
+
if tipo==self.TIPO_RESPONSE__PLAIN_TEXT:
|
252
|
+
response+=responseBody
|
253
|
+
response=ModuloStringhe.normalizzaEol(response)
|
254
|
+
response=response.encode(ModuloStringhe.CODIFICA_UTF8)
|
255
|
+
return response,tipo
|
256
|
+
|
257
|
+
@staticmethod
|
258
|
+
def __sendResponse(clientConnection,response):
|
259
|
+
try:
|
260
|
+
clientConnection.sendall(response)
|
261
|
+
except (ConnectionResetError,BrokenPipeError,ssl.SSLEOFError,ssl.SSLZeroReturnError):
|
262
|
+
pass
|
263
|
+
|
264
|
+
def __handleOsError(self,ex:OSError):
|
265
|
+
if ex.errno in (errno.ECONNRESET,errno.ETIMEDOUT):
|
266
|
+
return
|
267
|
+
self.callbackError(ex)
|
@@ -1,56 +1,56 @@
|
|
1
|
-
from OpenSSL import crypto
|
2
|
-
|
3
|
-
from modulitiz_micro.ModuloDate import ModuloDate
|
4
|
-
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
5
|
-
|
6
|
-
|
7
|
-
class ModuloSsl(object):
|
8
|
-
CA_CERT_FILE="cert.pem"
|
9
|
-
|
10
|
-
def __init__(self):
|
11
|
-
self.cert: crypto.X509|None=None
|
12
|
-
self.certStr: str|None=None
|
13
|
-
|
14
|
-
def createSelfSignedCertAnonymous(self):
|
15
|
-
return self.createSelfSignedCert("XX","XX","Xxx","Xxx","Xx","Xx","x@x.com")
|
16
|
-
|
17
|
-
def createSelfSignedCert(self,stato: str,provincia: str,via: str,organizzazione: str,organizzazioneUnita: str,nomeComune: str,email: str):
|
18
|
-
privateKey=crypto.PKey()
|
19
|
-
privateKey.generate_key(crypto.TYPE_RSA,2048)
|
20
|
-
|
21
|
-
certReq=crypto.X509Req()
|
22
|
-
certReq.get_subject().C=stato
|
23
|
-
certReq.get_subject().ST=provincia
|
24
|
-
certReq.get_subject().L=via
|
25
|
-
certReq.get_subject().O=organizzazione
|
26
|
-
certReq.get_subject().OU=organizzazioneUnita
|
27
|
-
certReq.get_subject().CN=nomeComune
|
28
|
-
certReq.get_subject().emailAddress=email
|
29
|
-
certReq.set_pubkey(privateKey)
|
30
|
-
certReq.sign(privateKey,'sha256')
|
31
|
-
|
32
|
-
certIssuer=crypto.X509Req()
|
33
|
-
certIssuer.get_subject().C=stato
|
34
|
-
certIssuer.get_subject().ST=provincia
|
35
|
-
certIssuer.get_subject().L=via
|
36
|
-
certIssuer.get_subject().O=organizzazione
|
37
|
-
certIssuer.get_subject().OU=organizzazioneUnita
|
38
|
-
certIssuer.get_subject().CN=nomeComune
|
39
|
-
certIssuer.get_subject().emailAddress=email
|
40
|
-
certIssuer.set_pubkey(privateKey)
|
41
|
-
certIssuer.sign(privateKey,'sha256')
|
42
|
-
|
43
|
-
cert=crypto.X509()
|
44
|
-
cert.set_serial_number(ModuloDate.getSecs())
|
45
|
-
cert.gmtime_adj_notBefore(0)
|
46
|
-
cert.gmtime_adj_notAfter(5*365*24*60*60)
|
47
|
-
cert.set_issuer(certIssuer.get_subject())
|
48
|
-
cert.set_subject(certReq.get_subject())
|
49
|
-
cert.set_pubkey(certReq.get_pubkey())
|
50
|
-
cert.sign(privateKey,'sha256')
|
51
|
-
self.cert=cert
|
52
|
-
|
53
|
-
certStr=crypto.dump_certificate(crypto.FILETYPE_PEM,cert).decode(ModuloStringhe.CODIFICA_UTF8)
|
54
|
-
certStr+=certStr
|
55
|
-
certStr+=crypto.dump_privatekey(crypto.FILETYPE_PEM,privateKey).decode(ModuloStringhe.CODIFICA_UTF8)
|
56
|
-
self.certStr=certStr
|
1
|
+
from OpenSSL import crypto
|
2
|
+
|
3
|
+
from modulitiz_micro.ModuloDate import ModuloDate
|
4
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
5
|
+
|
6
|
+
|
7
|
+
class ModuloSsl(object):
|
8
|
+
CA_CERT_FILE="cert.pem"
|
9
|
+
|
10
|
+
def __init__(self):
|
11
|
+
self.cert: crypto.X509|None=None
|
12
|
+
self.certStr: str|None=None
|
13
|
+
|
14
|
+
def createSelfSignedCertAnonymous(self):
|
15
|
+
return self.createSelfSignedCert("XX","XX","Xxx","Xxx","Xx","Xx","x@x.com")
|
16
|
+
|
17
|
+
def createSelfSignedCert(self,stato: str,provincia: str,via: str,organizzazione: str,organizzazioneUnita: str,nomeComune: str,email: str):
|
18
|
+
privateKey=crypto.PKey()
|
19
|
+
privateKey.generate_key(crypto.TYPE_RSA,2048)
|
20
|
+
|
21
|
+
certReq=crypto.X509Req()
|
22
|
+
certReq.get_subject().C=stato
|
23
|
+
certReq.get_subject().ST=provincia
|
24
|
+
certReq.get_subject().L=via
|
25
|
+
certReq.get_subject().O=organizzazione
|
26
|
+
certReq.get_subject().OU=organizzazioneUnita
|
27
|
+
certReq.get_subject().CN=nomeComune
|
28
|
+
certReq.get_subject().emailAddress=email
|
29
|
+
certReq.set_pubkey(privateKey)
|
30
|
+
certReq.sign(privateKey,'sha256')
|
31
|
+
|
32
|
+
certIssuer=crypto.X509Req()
|
33
|
+
certIssuer.get_subject().C=stato
|
34
|
+
certIssuer.get_subject().ST=provincia
|
35
|
+
certIssuer.get_subject().L=via
|
36
|
+
certIssuer.get_subject().O=organizzazione
|
37
|
+
certIssuer.get_subject().OU=organizzazioneUnita
|
38
|
+
certIssuer.get_subject().CN=nomeComune
|
39
|
+
certIssuer.get_subject().emailAddress=email
|
40
|
+
certIssuer.set_pubkey(privateKey)
|
41
|
+
certIssuer.sign(privateKey,'sha256')
|
42
|
+
|
43
|
+
cert=crypto.X509()
|
44
|
+
cert.set_serial_number(ModuloDate.getSecs())
|
45
|
+
cert.gmtime_adj_notBefore(0)
|
46
|
+
cert.gmtime_adj_notAfter(5*365*24*60*60)
|
47
|
+
cert.set_issuer(certIssuer.get_subject())
|
48
|
+
cert.set_subject(certReq.get_subject())
|
49
|
+
cert.set_pubkey(certReq.get_pubkey())
|
50
|
+
cert.sign(privateKey,'sha256')
|
51
|
+
self.cert=cert
|
52
|
+
|
53
|
+
certStr=crypto.dump_certificate(crypto.FILETYPE_PEM,cert).decode(ModuloStringhe.CODIFICA_UTF8)
|
54
|
+
certStr+=certStr
|
55
|
+
certStr+=crypto.dump_privatekey(crypto.FILETYPE_PEM,privateKey).decode(ModuloStringhe.CODIFICA_UTF8)
|
56
|
+
self.certStr=certStr
|
@@ -1,9 +1,9 @@
|
|
1
|
-
from enum import StrEnum
|
2
|
-
from enum import unique
|
3
|
-
|
4
|
-
|
5
|
-
@unique
|
6
|
-
class EnvVarsEnum(StrEnum):
|
7
|
-
MODULITIZ_IS_DEBUG="MODULITIZ_IS_DEBUG"
|
8
|
-
PATH="PATH"
|
9
|
-
TMP="TMP"
|
1
|
+
from enum import StrEnum
|
2
|
+
from enum import unique
|
3
|
+
|
4
|
+
|
5
|
+
@unique
|
6
|
+
class EnvVarsEnum(StrEnum):
|
7
|
+
MODULITIZ_IS_DEBUG="MODULITIZ_IS_DEBUG"
|
8
|
+
PATH="PATH"
|
9
|
+
TMP="TMP"
|