First commit
This commit is contained in:
parent
6fef4d3fc6
commit
cdcc03d0bf
90
fsockets.py
Normal file
90
fsockets.py
Normal file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
p = os.path.join
|
||||
pUp = os.path.dirname
|
||||
s = False
|
||||
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||||
s = os.path.realpath(sys.executable)
|
||||
else:
|
||||
s = os.path.realpath(__file__)
|
||||
sp = pUp(s)
|
||||
|
||||
# script start
|
||||
import socket
|
||||
import threading
|
||||
import queue
|
||||
|
||||
# IMPORTANT! Obtain locks in this order, if you need multiple at once:
|
||||
# - clientDataLock
|
||||
# - clientsLock
|
||||
# - serverThreadsLock
|
||||
# - fileLock
|
||||
modulePath = p(sp,"modules")
|
||||
|
||||
mainQueue = queue.Queue()
|
||||
clientsLock = threading.Lock()
|
||||
clientID = 0
|
||||
clients = {}
|
||||
clientDataLock = threading.Lock()
|
||||
clientData = {}
|
||||
serverThreadsLock = threading.Lock()
|
||||
serverThreads = []
|
||||
fileLock = threading.Lock()
|
||||
|
||||
def runCode(str, lcs = False, description = "loose-code"):
|
||||
if lcs == False: lcs = {}
|
||||
code = compile(str,description,"exec")
|
||||
exec(code,globals(),lcs)
|
||||
return lcs
|
||||
|
||||
def runScript(sf, lcs = False):
|
||||
if lcs == False: lcs = {}
|
||||
|
||||
code = False
|
||||
with fileLock:
|
||||
with open(sf,"r",encoding="utf-8") as script:
|
||||
code = script.read()
|
||||
|
||||
runCode(code,lcs,sf)
|
||||
return lcs
|
||||
|
||||
def readModuleFile(path):
|
||||
with open(path,"r",encoding="utf-8") as modulesFile:
|
||||
for line in modulesFile:
|
||||
line = line.split("#",1)[0].strip(" \t\r\n")
|
||||
if line == "": continue
|
||||
modType = line.rsplit(".",1)[-1].lower()
|
||||
line = line.replace("\\","/")
|
||||
|
||||
if modType == "mods":
|
||||
print(">> " +line+ " <<")
|
||||
else:
|
||||
print("> " +line+ " ...")
|
||||
|
||||
line = line.replace("/",os.path.sep)
|
||||
if line.startswith("." +os.path.sep):
|
||||
line = pUp(path) + line[1:]
|
||||
else:
|
||||
line = p(modulePath,line)
|
||||
|
||||
if modType == "py":
|
||||
runScript(line,locals())
|
||||
|
||||
if modType == "mods":
|
||||
readModuleFile(line)
|
||||
|
||||
def main():
|
||||
if os.path.isfile(p(modulePath,"main.mods")):
|
||||
print("Loading modules...")
|
||||
print(">> main.mods <<")
|
||||
readModuleFile(p(modulePath,"main.mods"))
|
||||
print("OK.\n")
|
||||
|
||||
with serverThreadsLock:
|
||||
for server in servers:
|
||||
makeServer(*server)
|
||||
|
||||
print("Serving!\n")
|
||||
|
||||
main()
|
6
index/index.html
Normal file
6
index/index.html
Normal file
@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>Hello world!</h1>
|
||||
</body>
|
||||
</html>
|
2
index/secret folder/.fhtpyaccess
Normal file
2
index/secret folder/.fhtpyaccess
Normal file
@ -0,0 +1,2 @@
|
||||
env["handler"] = handle404 # Always pretend as if the file wasn't found
|
||||
env["htaccessPropagate"] = False # Do not read .fhtpyaccess files in sub-folders
|
6
index/secret folder/another secret folder/index.html
Normal file
6
index/secret folder/another secret folder/index.html
Normal file
@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>You shouldn't be able to access this page.</h1>
|
||||
</body>
|
||||
</html>
|
33
index/test/index.pyp
Normal file
33
index/test/index.pyp
Normal file
@ -0,0 +1,33 @@
|
||||
global json
|
||||
import json
|
||||
|
||||
response = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #222222;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>'''
|
||||
|
||||
for key in env:
|
||||
value = str(env[key])
|
||||
if value == "": value = " "
|
||||
response += "<b>" +html.escape(key)+ ":</b><br>\n<code>" +html.escape(value)+ "</code><br>\n"
|
||||
response += "</html>"
|
||||
|
||||
simpleResponse(
|
||||
env["self"].connection,"200 OK",
|
||||
{
|
||||
"Content-Type": "text/html; charset=UTF-8",
|
||||
},(response).encode("utf-8")
|
||||
)
|
95
modules/clients.py
Normal file
95
modules/clients.py
Normal file
@ -0,0 +1,95 @@
|
||||
global clientThreadIn
|
||||
class clientThreadIn(threading.Thread):
|
||||
def __init__(self,cID,connection,address):
|
||||
threading.Thread.__init__(self)
|
||||
self.cID = cID
|
||||
self.connection = connection
|
||||
self.address = address
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
clientLoopIn(self)
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
with clientDataLock:
|
||||
with clientsLock:
|
||||
closeClient(self.cID,0)
|
||||
|
||||
global clientThreadOut
|
||||
class clientThreadOut(threading.Thread):
|
||||
def __init__(self,cID,connection,address):
|
||||
threading.Thread.__init__(self)
|
||||
self.cID = cID
|
||||
self.connection = connection
|
||||
self.address = address
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
clientLoopOut(self)
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
with clientDataLock:
|
||||
with clientsLock:
|
||||
closeClient(self.cID,1)
|
||||
|
||||
global closeClient
|
||||
def closeClient(cID,threadType = None):
|
||||
try: # Close connection
|
||||
clients[cID][0].close()
|
||||
except:
|
||||
pass
|
||||
|
||||
try: # Set reference of connection to false, to denote the client is to not be served
|
||||
clients[cID][0] = False
|
||||
except:
|
||||
pass
|
||||
|
||||
try: # Set reference of the thread to false, to denote that it is closed
|
||||
if threadType != None:
|
||||
clients[cID][1 + threadType] = False
|
||||
except:
|
||||
pass
|
||||
|
||||
try: # Get rid of leftover data to free memory
|
||||
if clients[cID] == [False,False,False]:
|
||||
del clients[cID]
|
||||
del clientData[cID]
|
||||
except:
|
||||
pass
|
||||
|
||||
global setClientData
|
||||
def setClientData(cID,key,data):
|
||||
clientData[cID][key] = data
|
||||
|
||||
global getClientData
|
||||
def getClientData(cID,key):
|
||||
if not key in clientData[cID]: return None
|
||||
return clientData[cID][key]
|
||||
|
||||
def main():
|
||||
def onConnectionEvent(event,eEnv,connection,address):
|
||||
with clientDataLock:
|
||||
with clientsLock:
|
||||
global clientID
|
||||
clientID += 1
|
||||
cID = str(clientID)
|
||||
threadIn = clientThreadIn(cID,connection,address)
|
||||
threadOut = False
|
||||
if enableOutThread:
|
||||
threadOut = clientThreadOut(cID,connection,address)
|
||||
clients[cID] = [connection,threadIn,threadOut]
|
||||
clientData[cID] = {"address":address}
|
||||
threadIn.start()
|
||||
if enableOutThread:
|
||||
threadOut.start()
|
||||
|
||||
if clientDebug:
|
||||
print("---")
|
||||
print("Clients: " +str(len(clients)))
|
||||
print("Threads: " +str(threading.active_count()))
|
||||
return True
|
||||
addEventHandler("onConnection",onConnectionEvent)
|
||||
|
||||
main()
|
11
modules/connlimit.py
Normal file
11
modules/connlimit.py
Normal file
@ -0,0 +1,11 @@
|
||||
def main():
|
||||
def onConnectionEvent(event,eEnv,connection,address):
|
||||
count = 0
|
||||
with clientDataLock:
|
||||
for cID in clientData:
|
||||
if getClientData(cID,"address") == address:
|
||||
count += 1
|
||||
if count >= maxConnections: return False
|
||||
return True
|
||||
addEventHandler("onConnection",onConnectionEvent)
|
||||
main()
|
30
modules/events.py
Normal file
30
modules/events.py
Normal file
@ -0,0 +1,30 @@
|
||||
global eventHandlers
|
||||
eventHandlers = {}
|
||||
|
||||
global addEventHandler
|
||||
def addEventHandler(event,handler):
|
||||
if not event in eventHandlers: eventHandlers[event] = []
|
||||
try:
|
||||
eventHandlers[event].remove(handler)
|
||||
except:
|
||||
pass
|
||||
eventHandlers[event].append(handler)
|
||||
|
||||
global removeEventHandler
|
||||
def removeEventHandler(event,handler):
|
||||
if not event in eventHandlers: return
|
||||
try:
|
||||
eventHandlers[event].remove(handler)
|
||||
except:
|
||||
pass
|
||||
if len(eventHandlers[event]) == 0:
|
||||
del eventHandlers[event]
|
||||
|
||||
global triggerEvent
|
||||
def triggerEvent(event,*args,eEnv=False,**kwargs):
|
||||
if not eEnv: eEnv = {}
|
||||
if not event in eventHandlers: return
|
||||
for func in eventHandlers[event]:
|
||||
result = func(event,eEnv,*args,**kwargs)
|
||||
if result == False: return False
|
||||
return True
|
19
modules/exceptions.py
Normal file
19
modules/exceptions.py
Normal file
@ -0,0 +1,19 @@
|
||||
global traceback
|
||||
import traceback
|
||||
|
||||
global excConnectionClosed
|
||||
class excConnectionClosed(Exception): pass
|
||||
|
||||
global handleException
|
||||
def handleException(e):
|
||||
try:
|
||||
if printExceptions:
|
||||
print(traceback.format_exc())
|
||||
except:
|
||||
try:
|
||||
print(e)
|
||||
except:
|
||||
try:
|
||||
print("Printing exception failed!")
|
||||
except:
|
||||
pass
|
15
modules/helpers.py
Normal file
15
modules/helpers.py
Normal file
@ -0,0 +1,15 @@
|
||||
global time
|
||||
import time
|
||||
|
||||
global recv
|
||||
def recv(conn,l):
|
||||
start = time.process_time()
|
||||
timeo = conn.gettimeout()
|
||||
bytes = b""
|
||||
while l > 0:
|
||||
b = conn.recv(l)
|
||||
if b == b"": raise ConnectionResetError
|
||||
if time.process_time() - start > timeo: raise TimeoutError
|
||||
bytes += b
|
||||
l -= len(b)
|
||||
return bytes
|
8
modules/http/404.py
Normal file
8
modules/http/404.py
Normal file
@ -0,0 +1,8 @@
|
||||
global handle404
|
||||
def handle404(env):
|
||||
newPath = "/" + pathToURL(env["pathFixed"])
|
||||
rawArgs = env["protocolHeaderList"][1].split("?",1)
|
||||
|
||||
if len(rawArgs) > 1:
|
||||
newPath += "?" +rawArgs[-1]
|
||||
notFound(env["self"].connection,newPath)
|
55
modules/http/file-handlers/binary.py
Normal file
55
modules/http/file-handlers/binary.py
Normal file
@ -0,0 +1,55 @@
|
||||
global handleBinary
|
||||
def handleBinary(env):
|
||||
filePath = env["fPath"]
|
||||
fileExt = env["fileExt"]
|
||||
connection = env["self"].connection
|
||||
length = 0
|
||||
with fileLock:
|
||||
length = os.path.getsize(filePath)
|
||||
|
||||
rangeDefined = False
|
||||
rangeStart = 0
|
||||
rangeEnd = None
|
||||
if "range" in env["headerList"]:
|
||||
rangeDefined = True
|
||||
rangeStart,rangeEnd = getRange(env["headerList"]["range"])
|
||||
|
||||
rangeStart,rangeEnd = convertRanges(rangeStart,rangeEnd,length)
|
||||
if rangeStart == None:
|
||||
raise # tell the client the request is invalid
|
||||
|
||||
mimetype = "application/octet-stream"
|
||||
if fileExt in mimetypesBinary:
|
||||
mimetype = mimetypesBinary[fileExt]
|
||||
|
||||
if not rangeDefined:
|
||||
simpleResponse(connection,"200 OK",{
|
||||
"Content-Length": str(length),
|
||||
"Content-Type": mimetype,
|
||||
"Accept-Ranges": "bytes"
|
||||
})
|
||||
else:
|
||||
simpleResponse(connection,"206 Partial Content",{
|
||||
"Content-Range": "bytes " +str(rangeStart)+ "-" +str(rangeEnd - 1)+ "/" +str(length),
|
||||
"Content-Length": str(rangeEnd - rangeStart),
|
||||
"Content-Type": mimetype,
|
||||
"Accept-Ranges": "bytes"
|
||||
})
|
||||
|
||||
print(rangeStart,rangeEnd)
|
||||
cByte = rangeStart
|
||||
while cByte < rangeEnd:
|
||||
bytes = b""
|
||||
rSize = readBufferSize
|
||||
if cByte + rSize > rangeEnd:
|
||||
rSize = rangeEnd - cByte
|
||||
with fileLock:
|
||||
with open(filePath,"rb") as file:
|
||||
file.seek(cByte)
|
||||
bytes = file.read(rSize)
|
||||
connection.sendall(bytes)
|
||||
cByte += rSize
|
||||
|
||||
fileHandlers[".*"] = handleBinary
|
||||
for t in mimetypesBinary:
|
||||
fileHandlers[t] = handleBinary
|
18
modules/http/file-handlers/htaccess.py
Normal file
18
modules/http/file-handlers/htaccess.py
Normal file
@ -0,0 +1,18 @@
|
||||
def main():
|
||||
def handleHTTP(event,eenv,env):
|
||||
env["htaccessPropagate"] = True
|
||||
paths = [indexPath] + env["lPath"].split(os.path.sep)[:-1]
|
||||
pathl = []
|
||||
for pathbit in paths:
|
||||
pathl.append(pathbit)
|
||||
path = p(*pathl,".fhtpyaccess")
|
||||
if not os.path.isfile(path): continue
|
||||
handlePYP(env,path)
|
||||
if env["htaccessPropagate"] == False:
|
||||
return True
|
||||
|
||||
addEventHandler("handleHTTP",handleHTTP)
|
||||
|
||||
main()
|
||||
fileHandlers["fhtpyaccess"] = handle404
|
||||
fileHandlers["htaccess"] = handle404
|
1180
modules/http/file-handlers/mimetypes.py
Normal file
1180
modules/http/file-handlers/mimetypes.py
Normal file
File diff suppressed because it is too large
Load Diff
17
modules/http/file-handlers/pyp.py
Normal file
17
modules/http/file-handlers/pyp.py
Normal file
@ -0,0 +1,17 @@
|
||||
global handlePYP
|
||||
def handlePYP(env,cpath = False,getlock = True):
|
||||
code = False
|
||||
if not cpath: cpath = env["fPath"]
|
||||
|
||||
if getlock:
|
||||
with fileLock:
|
||||
with open(cpath,encoding="utf-8") as cfile:
|
||||
code = cfile.read()
|
||||
else:
|
||||
with open(cpath,encoding="utf-8") as cfile:
|
||||
code = cfile.read()
|
||||
env = runCode(code,{"env":env},cpath)["env"]
|
||||
fileHandlers["pyp"] = handlePYP
|
||||
|
||||
global indexFiles
|
||||
indexFiles = ["index.pyp"] + indexFiles
|
19
modules/http/file-handlers/text.py
Normal file
19
modules/http/file-handlers/text.py
Normal file
@ -0,0 +1,19 @@
|
||||
global handleText
|
||||
def handleText(env):
|
||||
data = b""
|
||||
with fileLock:
|
||||
with open(env["fPath"],"rb") as textFile:
|
||||
data = textFile.read()
|
||||
|
||||
simpleResponse(
|
||||
env["self"].connection,"200 OK",
|
||||
{
|
||||
"Content-Type": mimetypesText[env["fileExt"]]+ "; charset=UTF-8",
|
||||
"Accept-Ranges": "bytes"
|
||||
},data
|
||||
)
|
||||
|
||||
for t in mimetypesText:
|
||||
fileHandlers[t] = handleText
|
||||
|
||||
indexFiles.append("index.html")
|
211
modules/http/helpers.py
Normal file
211
modules/http/helpers.py
Normal file
@ -0,0 +1,211 @@
|
||||
global time
|
||||
import time
|
||||
global urllib
|
||||
import urllib.parse
|
||||
global html
|
||||
import html
|
||||
|
||||
global getHeaderFromConnection
|
||||
def getHeaderFromConnection(connection):
|
||||
start = time.process_time()
|
||||
timeo = connection.gettimeout()
|
||||
l = 0
|
||||
nl = 0
|
||||
header = ""
|
||||
while True:
|
||||
b = connection.recv(1)
|
||||
if b == b"": raise ConnectionResetError
|
||||
if time.process_time() - start > timeo: raise TimeoutError
|
||||
l += 1
|
||||
if l > maxHeaderLength:
|
||||
connection.sendall("""\
|
||||
HTTP 1.1 413 Payload Too Large\r
|
||||
\r
|
||||
""".encode("ascii"))
|
||||
raise excConnectionClosed
|
||||
|
||||
bd = None
|
||||
try:
|
||||
bd = b.decode("ascii")
|
||||
except:
|
||||
connection.sendall("""\
|
||||
HTTP 1.1 400 Bad Request\r
|
||||
\r
|
||||
""".encode("ASCII"))
|
||||
raise excConnectionClosed
|
||||
|
||||
if bd == "\n":
|
||||
nl += 1
|
||||
if nl == 2:
|
||||
return header
|
||||
else:
|
||||
if bd != "\r":
|
||||
nl = 0
|
||||
header += bd
|
||||
|
||||
global parseHeader
|
||||
def parseHeader(headers):
|
||||
headers = headers.replace("\r","").split("\n")
|
||||
del headers[-1]
|
||||
for i in range(len(headers)):
|
||||
headers[i] = headers[i].strip(" \t")
|
||||
|
||||
mainHeader = headers.pop(0).split(" ")
|
||||
index = 0
|
||||
length = len(mainHeader)
|
||||
while index < length:
|
||||
val = mainHeader[index]
|
||||
val = val.strip(" \t")
|
||||
if val == "":
|
||||
del mainHeader[index]
|
||||
length -= 1
|
||||
continue
|
||||
index += 1
|
||||
mainHeader[0] = mainHeader[0].lower()
|
||||
mainHeader[-1] = mainHeader[-1].lower()
|
||||
|
||||
headerList = {}
|
||||
for header in headers:
|
||||
header = header.split(":",1)
|
||||
if len(header) != 2: continue
|
||||
headerKey = header[0].strip(" \t").lower()
|
||||
headerValue = header[1].strip(" \t")
|
||||
if headerKey in headerList:
|
||||
headers[headerKey] += ", " +headerValue
|
||||
else:
|
||||
headerList[headerKey] = headerValue
|
||||
|
||||
return mainHeader,headerList
|
||||
|
||||
global parseHeaderPath
|
||||
def parseHeaderPath(path):
|
||||
path = path.split("?",1)
|
||||
if len(path) < 2: path.append("")
|
||||
args = {}
|
||||
for arg in path[1].split("&"):
|
||||
arg = arg.split("=",1)
|
||||
if len(arg) < 2: arg.append("")
|
||||
args[urllib.parse.unquote(arg[0]).lower()] = urllib.parse.unquote(arg[1])
|
||||
return urllib.parse.unquote(path[0]),args
|
||||
|
||||
global fixUserPath
|
||||
def fixUserPath(path):
|
||||
path = path.replace("\\","/") # Replace backslash with forward slash
|
||||
path = path.lstrip("/")
|
||||
npath = ""
|
||||
for pathbit in path.split("/"):
|
||||
pathbit = pathbit.strip(" \t\r\n") # Remove spaces, tabs, line return, and new line
|
||||
if pathbit in [".",".."]: # Remove . and ..
|
||||
continue
|
||||
npath += pathbit + "/"
|
||||
npath = npath[:-1]
|
||||
|
||||
while "//" in npath: npath = npath.replace("//","/") # Remove double slashes
|
||||
return npath
|
||||
|
||||
global simpleResponse
|
||||
def simpleResponse(connection,status,headers = None,content = None,autolength = True):
|
||||
if headers == None:
|
||||
headers = {}
|
||||
|
||||
if not "Accept-Ranges" in headers:
|
||||
headers["Accept-Ranges"] = "none"
|
||||
|
||||
if content != None and autolength == True:
|
||||
headers["Content-Length"] = str(len(content))
|
||||
|
||||
response = 'HTTP/1.1 ' +status+ '\r\n'
|
||||
for header in headers:
|
||||
response += header + ": " +headers[header] + "\r\n"
|
||||
response += "\r\n"
|
||||
|
||||
connection.sendall(response.encode("ascii"))
|
||||
if content != None:
|
||||
connection.sendall(content)
|
||||
|
||||
global refer
|
||||
def refer(connection,path):
|
||||
simpleResponse(
|
||||
connection,"302 Found",
|
||||
{
|
||||
"Content-Type": "text/html; charset=ASCII",
|
||||
"Location": path
|
||||
},('''\
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
Referring you to <a href="''' +html.escape(path)+ '''">''' +html.escape(path)+ '''</a>...
|
||||
</body>
|
||||
</html>''').encode("ascii")
|
||||
)
|
||||
|
||||
global notFound
|
||||
def notFound(connection,path):
|
||||
simpleResponse(
|
||||
connection,"404 Not Found",
|
||||
{
|
||||
"Content-Type": "text/html; charset=ASCII"
|
||||
},('''\
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
Not found: <a href="''' +html.escape(path)+ '''">''' +html.escape(path)+ '''</a>
|
||||
</body>
|
||||
</html>''').encode("ascii")
|
||||
)
|
||||
|
||||
global pathToURL
|
||||
def pathToURL(path):
|
||||
path = path.split("/")
|
||||
length = len(path)
|
||||
index = 0
|
||||
while index < length:
|
||||
path[index] = urllib.parse.quote(path[index])
|
||||
index += 1
|
||||
path = "/".join(path)
|
||||
return path
|
||||
|
||||
# Can return the following:
|
||||
# positive integer, None: Send entire file content starting at arg 1
|
||||
# negative integer, None: Send entire file content starting at file end + arg 1
|
||||
# positive integer, positive integer: Send entire file, from arg 1 to arg 2, not including arg 2
|
||||
global getRange
|
||||
def getRange(range):
|
||||
try:
|
||||
range = range.split("=",1)
|
||||
if range[0].strip("\t ") != "bytes": return None,None
|
||||
range = range[1].split(",")[0].split("-")
|
||||
range[0] = range[0].strip("\t ")
|
||||
range[1] = range[1].strip("\t ")
|
||||
if range[0] == "":
|
||||
return 0 - int(range[1]),None
|
||||
|
||||
if range[1] == "":
|
||||
return int(range[0]),None
|
||||
|
||||
return int(range[0]),int(range[1]) + 1
|
||||
except:
|
||||
return 0,None
|
||||
|
||||
global convertRanges
|
||||
def convertRanges(rangeStart,rangeEnd,length):
|
||||
# Convert given ranges into complete ranges
|
||||
if rangeStart < 0:
|
||||
rangeStart = length - rangeStart
|
||||
rangeEnd = length
|
||||
else:
|
||||
if rangeEnd == None:
|
||||
rangeEnd = length
|
||||
|
||||
# Check if the ranges make sense
|
||||
if rangeStart < 0:
|
||||
return None,None
|
||||
|
||||
if rangeEnd > length:
|
||||
return None,None
|
||||
|
||||
if rangeStart > rangeEnd:
|
||||
return None,None
|
||||
|
||||
# OK
|
||||
return rangeStart,rangeEnd
|
11
modules/http/main.mods
Normal file
11
modules/http/main.mods
Normal file
@ -0,0 +1,11 @@
|
||||
./settings.py # Settings
|
||||
./helpers.py # Helper functions
|
||||
./main.py # Main loop and functions
|
||||
./404.py # 404 page
|
||||
|
||||
# File handlers:
|
||||
./file-handlers/mimetypes.py # List of file endings and the mimetypes they belong to
|
||||
./file-handlers/binary.py # Images, video, audio, executables, etc...
|
||||
./file-handlers/text.py # HTML, XML, TXT, etc...
|
||||
./file-handlers/pyp.py # pyp, fhttpy's script format
|
||||
./file-handlers/htaccess.py # .fhtpyaccess - can be used to override handlers on an entire folder
|
85
modules/http/main.py
Normal file
85
modules/http/main.py
Normal file
@ -0,0 +1,85 @@
|
||||
global email
|
||||
import email.utils
|
||||
|
||||
global fileHandlers
|
||||
fileHandlers = {}
|
||||
|
||||
global indexFiles
|
||||
indexFiles = []
|
||||
|
||||
global pathHandlers
|
||||
pathHandlers = {}
|
||||
|
||||
global clientLoopIn
|
||||
def clientLoopIn(self):
|
||||
env = {}
|
||||
env["self"] = self
|
||||
env["requestTime"] = time.time()
|
||||
env["header"] = getHeaderFromConnection(self.connection)
|
||||
env["protocolHeaderList"],env["headerList"] = parseHeader(env["header"])
|
||||
env["cmd"] = env["protocolHeaderList"][0]
|
||||
env["path"],env["args"] = parseHeaderPath(env["protocolHeaderList"][1])
|
||||
env["pathFixed"] = fixUserPath(env["path"])
|
||||
|
||||
env["lPath"] = env["pathFixed"].replace("/",os.path.sep)
|
||||
env["fPath"] = p(indexPath,env["lPath"])
|
||||
env["fileExt"] = "."
|
||||
|
||||
if not env["pathFixed"] == "" and not os.path.isfile(env["fPath"]) and os.path.isdir(env["fPath"]) and env["pathFixed"][-1] != "/": env["pathFixed"] += "/" # This is dirty, since it possibly circumvents .fhtpyaccess (You can see if a folder exists or not by probing)
|
||||
|
||||
if "/" + env["pathFixed"] != env["path"]:
|
||||
newPath = "/" + pathToURL(env["pathFixed"])
|
||||
rawArgs = env["protocolHeaderList"][1].split("?",1)
|
||||
|
||||
if len(rawArgs) > 1:
|
||||
newPath += "?" +rawArgs[-1]
|
||||
|
||||
refer(self.connection,newPath)
|
||||
return
|
||||
|
||||
if env["pathFixed"] in pathHandlers:
|
||||
pathHandlers[env["pathFixed"]](env)
|
||||
return
|
||||
|
||||
if not os.path.isfile(env["fPath"]):
|
||||
if not os.path.isdir(env["fPath"]):
|
||||
handle404(env)
|
||||
return
|
||||
|
||||
found = False
|
||||
for file in indexFiles:
|
||||
if os.path.isfile(p(env["fPath"],file)):
|
||||
found = file
|
||||
break
|
||||
|
||||
if found == False:
|
||||
env["fileExt"] = ".d"
|
||||
env["lPath"] = p(env["lPath"],".")
|
||||
env["fPath"] = p(indexPath,env["lPath"])
|
||||
else:
|
||||
env["lPath"] = p(env["lPath"],found)
|
||||
env["fPath"] = p(indexPath,env["lPath"])
|
||||
lPathSplit = env["lPath"].rsplit(os.path.sep,1)[-1].rsplit(".",1)
|
||||
if len(lPathSplit) > 1:
|
||||
env["fileExt"] = lPathSplit[-1].lower()
|
||||
else:
|
||||
lPathSplit = env["lPath"].rsplit(os.path.sep,1)[-1].rsplit(".",1)
|
||||
if len(lPathSplit) > 1:
|
||||
env["fileExt"] = lPathSplit[-1].lower()
|
||||
|
||||
env["fPathDir"] = pUp(env["fPath"])
|
||||
env["requestTimeFormatted"] = email.utils.formatdate(int(env["requestTime"])).replace("-0000","GMT")
|
||||
|
||||
env["handler"] = False
|
||||
if env["fileExt"] in fileHandlers:
|
||||
env["handler"] = fileHandlers[env["fileExt"]]
|
||||
elif ".*" in fileHandlers:
|
||||
env["handler"] = fileHandlers[".*"]
|
||||
|
||||
if triggerEvent("handleHTTP",env) == False: return
|
||||
|
||||
if env["handler"]:
|
||||
env["handler"](env)
|
||||
else:
|
||||
handle404(env)
|
||||
return
|
6
modules/http/settings.py
Normal file
6
modules/http/settings.py
Normal file
@ -0,0 +1,6 @@
|
||||
global maxHeaderLength
|
||||
maxHeaderLength = 4096
|
||||
global indexPath
|
||||
indexPath = p(sp,"index")
|
||||
global readBufferSize
|
||||
readBufferSize = 32768
|
8
modules/main.mods
Normal file
8
modules/main.mods
Normal file
@ -0,0 +1,8 @@
|
||||
settings.py # User settings
|
||||
helpers.py # Helper functions
|
||||
events.py # Event/event handler implementation
|
||||
exceptions.py # Handle exceptions, close connections
|
||||
servers.py # Create sockets, optionally with SSL/TLS
|
||||
connlimit.py # Optional: Limit the amount of connections made by one IP
|
||||
clients.py # Create and remove client sessions and connections
|
||||
http/main.mods # HTTP server
|
45
modules/servers.py
Normal file
45
modules/servers.py
Normal file
@ -0,0 +1,45 @@
|
||||
global ssl
|
||||
import ssl
|
||||
|
||||
global serverThread
|
||||
class serverThread(threading.Thread):
|
||||
def __init__(self,socket):
|
||||
threading.Thread.__init__(self)
|
||||
self.socket = socket
|
||||
|
||||
def run(self):
|
||||
connection = False
|
||||
address = False
|
||||
while True:
|
||||
try:
|
||||
connection,address = self.socket.accept()
|
||||
except:
|
||||
continue
|
||||
|
||||
try:
|
||||
connection.settimeout(timeout)
|
||||
if not triggerEvent("onConnection",connection,address): raise excConnectionClosed
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
try:
|
||||
connection.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
global makeServer
|
||||
def makeServer(host,port,https):
|
||||
print("Opening " +str(host)+ ":" +str(port)+ " (" +str(https)+ ") ...")
|
||||
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
serverSocket.bind((host,port))
|
||||
serverSocket.settimeout(5)
|
||||
if https:
|
||||
serverSocket = ssl.wrap_socket(
|
||||
serverSocket,
|
||||
server_side = True,
|
||||
certfile = https,
|
||||
ssl_version = ssl.PROTOCOL_TLS
|
||||
)
|
||||
serverSocket.listen(65535)
|
||||
thread = serverThread(serverSocket)
|
||||
serverThreads.append(thread)
|
||||
thread.start()
|
17
modules/settings.py
Normal file
17
modules/settings.py
Normal file
@ -0,0 +1,17 @@
|
||||
global servers
|
||||
servers = [
|
||||
# Host Port SSL Certificate
|
||||
("127.0.0.1", 80, False),
|
||||
# ("127.0.0.1", 443, "localhost.pem")
|
||||
]
|
||||
|
||||
global timeout
|
||||
timeout = 15 # Seconds until the connection should be timed out
|
||||
global maxConnections
|
||||
maxConnections = 50 # Maximum connections per IP, needs connlimit.py to be activated
|
||||
global enableOutThread
|
||||
enableOutThread = False # Use a seperate thread for data output?
|
||||
global printExceptions
|
||||
printExceptions = False # Print exceptions as they happen, enable if you're developing
|
||||
global clientDebug
|
||||
clientDebug = False # Print how many clients and threads there are
|
Loading…
Reference in New Issue
Block a user