diff --git a/fsockets.py b/fsockets.py
new file mode 100644
index 0000000..aca0123
--- /dev/null
+++ b/fsockets.py
@@ -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()
\ No newline at end of file
diff --git a/index/index.html b/index/index.html
new file mode 100644
index 0000000..e1326f7
--- /dev/null
+++ b/index/index.html
@@ -0,0 +1,6 @@
+
+
+
+Hello world!
+
+
\ No newline at end of file
diff --git a/index/secret folder/.fhtpyaccess b/index/secret folder/.fhtpyaccess
new file mode 100644
index 0000000..b1df8ad
--- /dev/null
+++ b/index/secret folder/.fhtpyaccess
@@ -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
\ No newline at end of file
diff --git a/index/secret folder/another secret folder/index.html b/index/secret folder/another secret folder/index.html
new file mode 100644
index 0000000..33a5d16
--- /dev/null
+++ b/index/secret folder/another secret folder/index.html
@@ -0,0 +1,6 @@
+
+
+
+You shouldn't be able to access this page.
+
+
\ No newline at end of file
diff --git a/index/test/index.pyp b/index/test/index.pyp
new file mode 100644
index 0000000..bad766d
--- /dev/null
+++ b/index/test/index.pyp
@@ -0,0 +1,33 @@
+global json
+import json
+
+response = '''
+
+
+
+
+'''
+
+for key in env:
+ value = str(env[key])
+ if value == "": value = " "
+ response += "" +html.escape(key)+ ":
\n" +html.escape(value)+ "
\n"
+response += ""
+
+simpleResponse(
+ env["self"].connection,"200 OK",
+ {
+ "Content-Type": "text/html; charset=UTF-8",
+ },(response).encode("utf-8")
+)
\ No newline at end of file
diff --git a/modules/clients.py b/modules/clients.py
new file mode 100644
index 0000000..567909a
--- /dev/null
+++ b/modules/clients.py
@@ -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()
\ No newline at end of file
diff --git a/modules/connlimit.py b/modules/connlimit.py
new file mode 100644
index 0000000..0259902
--- /dev/null
+++ b/modules/connlimit.py
@@ -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()
\ No newline at end of file
diff --git a/modules/events.py b/modules/events.py
new file mode 100644
index 0000000..6c17f80
--- /dev/null
+++ b/modules/events.py
@@ -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
\ No newline at end of file
diff --git a/modules/exceptions.py b/modules/exceptions.py
new file mode 100644
index 0000000..f15dcf4
--- /dev/null
+++ b/modules/exceptions.py
@@ -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
\ No newline at end of file
diff --git a/modules/helpers.py b/modules/helpers.py
new file mode 100644
index 0000000..50013fe
--- /dev/null
+++ b/modules/helpers.py
@@ -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
\ No newline at end of file
diff --git a/modules/http/404.py b/modules/http/404.py
new file mode 100644
index 0000000..fc9a598
--- /dev/null
+++ b/modules/http/404.py
@@ -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)
\ No newline at end of file
diff --git a/modules/http/file-handlers/binary.py b/modules/http/file-handlers/binary.py
new file mode 100644
index 0000000..6de361f
--- /dev/null
+++ b/modules/http/file-handlers/binary.py
@@ -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
\ No newline at end of file
diff --git a/modules/http/file-handlers/htaccess.py b/modules/http/file-handlers/htaccess.py
new file mode 100644
index 0000000..f2b64ba
--- /dev/null
+++ b/modules/http/file-handlers/htaccess.py
@@ -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
\ No newline at end of file
diff --git a/modules/http/file-handlers/mimetypes.py b/modules/http/file-handlers/mimetypes.py
new file mode 100644
index 0000000..42ca030
--- /dev/null
+++ b/modules/http/file-handlers/mimetypes.py
@@ -0,0 +1,1180 @@
+global mimetypesBinary
+mimetypesBinary = {
+ "123": "application/vnd.lotus-1-2-3",
+ "1km": "application/vnd.1000minds.decision-model+xml",
+ "3ds": "image/x-3ds",
+ "3g2": "video/3gpp2",
+ "3gp": "video/3gpp",
+ "3gpp": "video/3gpp",
+ "3mf": "model/3mf",
+ "7z": "application/x-7z-compressed",
+ "aab": "application/x-authorware-bin",
+ "aac": "audio/x-aac",
+ "aam": "application/x-authorware-map",
+ "aas": "application/x-authorware-seg",
+ "abw": "application/x-abiword",
+ "ac": "application/vnd.nokia.n-gage.ac+xml",
+ "acc": "application/vnd.americandynamics.acc",
+ "ace": "application/x-ace-compressed",
+ "acu": "application/vnd.acucobol",
+ "acutc": "application/vnd.acucorp",
+ "adp": "audio/adpcm",
+ "aep": "application/vnd.audiograph",
+ "afm": "application/x-font-type1",
+ "afp": "application/vnd.ibm.modcap",
+ "ahead": "application/vnd.ahead.space",
+ "ai": "application/postscript",
+ "aif": "audio/x-aiff",
+ "aifc": "audio/x-aiff",
+ "aiff": "audio/x-aiff",
+ "air": "application/vnd.adobe.air-application-installer-package+zip",
+ "ait": "application/vnd.dvb.ait",
+ "ami": "application/vnd.amiga.ami",
+ "amr": "audio/amr",
+ "apk": "application/vnd.android.package-archive",
+ "apng": "image/apng",
+ "application": "application/x-ms-application",
+ "apr": "application/vnd.lotus-approach",
+ "arc": "application/x-freearc",
+ "arj": "application/x-arj",
+ "asc": "application/pgp-signature",
+ "asf": "video/x-ms-asf",
+ "aso": "application/vnd.accpac.simply.aso",
+ "asx": "video/x-ms-asf",
+ "atc": "application/vnd.acucorp",
+ "atom": "application/atom+xml",
+ "atomcat": "application/atomcat+xml",
+ "atomdeleted": "application/atomdeleted+xml",
+ "atomsvc": "application/atomsvc+xml",
+ "atx": "application/vnd.antix.game-component",
+ "au": "audio/basic",
+ "avi": "video/x-msvideo",
+ "avif": "image/avif",
+ "aw": "application/applixware",
+ "azf": "application/vnd.airzip.filesecure.azf",
+ "azs": "application/vnd.airzip.filesecure.azs",
+ "azv": "image/vnd.airzip.accelerator.azv",
+ "azw": "application/vnd.amazon.ebook",
+ "b16": "image/vnd.pco.b16",
+ "bat": "application/x-msdownload",
+ "bcpio": "application/x-bcpio",
+ "bdf": "application/x-font-bdf",
+ "bdm": "application/vnd.syncml.dm+wbxml",
+ "bdoc": "application/x-bdoc",
+ "bed": "application/vnd.realvnc.bed",
+ "bh2": "application/vnd.fujitsu.oasysprs",
+ "bin": "application/octet-stream",
+ "blb": "application/x-blorb",
+ "blorb": "application/x-blorb",
+ "bmi": "application/vnd.bmi",
+ "bmml": "application/vnd.balsamiq.bmml+xml",
+ "bmp": "image/x-ms-bmp",
+ "book": "application/vnd.framemaker",
+ "box": "application/vnd.previewsystems.box",
+ "boz": "application/x-bzip2",
+ "bpk": "application/octet-stream",
+ "bsp": "model/vnd.valve.source.compiled-map",
+ "btif": "image/prs.btif",
+ "buffer": "application/octet-stream",
+ "bz": "application/x-bzip",
+ "bz2": "application/x-bzip2",
+ "c11amc": "application/vnd.cluetrust.cartomobile-config",
+ "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
+ "c4d": "application/vnd.clonk.c4group",
+ "c4f": "application/vnd.clonk.c4group",
+ "c4g": "application/vnd.clonk.c4group",
+ "c4p": "application/vnd.clonk.c4group",
+ "c4u": "application/vnd.clonk.c4group",
+ "cab": "application/vnd.ms-cab-compressed",
+ "caf": "audio/x-caf",
+ "cap": "application/vnd.tcpdump.pcap",
+ "car": "application/vnd.curl.car",
+ "cat": "application/vnd.ms-pki.seccat",
+ "cb7": "application/x-cbr",
+ "cba": "application/x-cbr",
+ "cbr": "application/x-cbr",
+ "cbt": "application/x-cbr",
+ "cbz": "application/x-cbr",
+ "cco": "application/x-cocoa",
+ "cct": "application/x-director",
+ "ccxml": "application/ccxml+xml",
+ "cdbcmsg": "application/vnd.contact.cmsg",
+ "cdf": "application/x-netcdf",
+ "cdfx": "application/cdfx+xml",
+ "cdkey": "application/vnd.mediastation.cdkey",
+ "cdmia": "application/cdmi-capability",
+ "cdmic": "application/cdmi-container",
+ "cdmid": "application/cdmi-domain",
+ "cdmio": "application/cdmi-object",
+ "cdmiq": "application/cdmi-queue",
+ "cdx": "chemical/x-cdx",
+ "cdxml": "application/vnd.chemdraw+xml",
+ "cdy": "application/vnd.cinderella",
+ "cer": "application/pkix-cert",
+ "cfs": "application/x-cfs-compressed",
+ "cgm": "image/cgm",
+ "chat": "application/x-chat",
+ "chm": "application/vnd.ms-htmlhelp",
+ "chrt": "application/vnd.kde.kchart",
+ "cif": "chemical/x-cif",
+ "cii": "application/vnd.anser-web-certificate-issue-initiation",
+ "cil": "application/vnd.ms-artgalry",
+ "cjs": "application/node",
+ "cla": "application/vnd.claymore",
+ "class": "application/java-vm",
+ "clkk": "application/vnd.crick.clicker.keyboard",
+ "clkp": "application/vnd.crick.clicker.palette",
+ "clkt": "application/vnd.crick.clicker.template",
+ "clkw": "application/vnd.crick.clicker.wordbank",
+ "clkx": "application/vnd.crick.clicker",
+ "clp": "application/x-msclip",
+ "cmc": "application/vnd.cosmocaller",
+ "cmdf": "chemical/x-cmdf",
+ "cml": "chemical/x-cml",
+ "cmp": "application/vnd.yellowriver-custom-menu",
+ "cmx": "image/x-cmx",
+ "cod": "application/vnd.rim.cod",
+ "com": "application/x-msdownload",
+ "cpio": "application/x-cpio",
+ "cpt": "application/mac-compactpro",
+ "crd": "application/x-mscardfile",
+ "crl": "application/pkix-crl",
+ "crt": "application/x-x509-ca-cert",
+ "crx": "application/x-chrome-extension",
+ "cryptonote": "application/vnd.rig.cryptonote",
+ "csh": "application/x-csh",
+ "csl": "application/vnd.citationstyles.style+xml",
+ "csml": "chemical/x-csml",
+ "csp": "application/vnd.commonspace",
+ "cst": "application/x-director",
+ "cu": "application/cu-seeme",
+ "cww": "application/prs.cww",
+ "cxt": "application/x-director",
+ "dae": "model/vnd.collada+xml",
+ "daf": "application/vnd.mobius.daf",
+ "dart": "application/vnd.dart",
+ "dataless": "application/vnd.fdsn.seed",
+ "davmount": "application/davmount+xml",
+ "dbf": "application/vnd.dbf",
+ "dbk": "application/docbook+xml",
+ "dcr": "application/x-director",
+ "dd2": "application/vnd.oma.dd2+xml",
+ "ddd": "application/vnd.fujixerox.ddd",
+ "ddf": "application/vnd.syncml.dmddf+xml",
+ "dds": "image/vnd.ms-dds",
+ "deb": "application/x-debian-package",
+ "deploy": "application/octet-stream",
+ "der": "application/x-x509-ca-cert",
+ "dfac": "application/vnd.dreamfactory",
+ "dgc": "application/x-dgc-compressed",
+ "dir": "application/x-director",
+ "dis": "application/vnd.mobius.dis",
+ "disposition-notification": "message/disposition-notification",
+ "dist": "application/octet-stream",
+ "distz": "application/octet-stream",
+ "djv": "image/vnd.djvu",
+ "djvu": "image/vnd.djvu",
+ "dll": "application/x-msdownload",
+ "dmg": "application/x-apple-diskimage",
+ "dmp": "application/vnd.tcpdump.pcap",
+ "dms": "application/octet-stream",
+ "dna": "application/vnd.dna",
+ "doc": "application/msword",
+ "docm": "application/vnd.ms-word.document.macroenabled.12",
+ "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "dot": "application/msword",
+ "dotm": "application/vnd.ms-word.template.macroenabled.12",
+ "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ "dp": "application/vnd.osgi.dp",
+ "dpg": "application/vnd.dpgraph",
+ "dra": "audio/vnd.dra",
+ "drle": "image/dicom-rle",
+ "dssc": "application/dssc+der",
+ "dtb": "application/x-dtbook+xml",
+ "dtd": "application/xml-dtd",
+ "dts": "audio/vnd.dts",
+ "dtshd": "audio/vnd.dts.hd",
+ "dump": "application/octet-stream",
+ "dvb": "video/vnd.dvb.file",
+ "dvi": "application/x-dvi",
+ "dwd": "application/atsc-dwd+xml",
+ "dwf": "model/vnd.dwf",
+ "dwg": "image/vnd.dwg",
+ "dxf": "image/vnd.dxf",
+ "dxp": "application/vnd.spotfire.dxp",
+ "dxr": "application/x-director",
+ "ear": "application/java-archive",
+ "ecelp4800": "audio/vnd.nuera.ecelp4800",
+ "ecelp7470": "audio/vnd.nuera.ecelp7470",
+ "ecelp9600": "audio/vnd.nuera.ecelp9600",
+ "ecma": "application/ecmascript",
+ "edm": "application/vnd.novadigm.edm",
+ "edx": "application/vnd.novadigm.edx",
+ "efif": "application/vnd.picsel",
+ "ei6": "application/vnd.pg.osasli",
+ "elc": "application/octet-stream",
+ "emf": "image/emf",
+ "eml": "message/rfc822",
+ "emma": "application/emma+xml",
+ "emotionml": "application/emotionml+xml",
+ "emz": "application/x-msmetafile",
+ "eol": "audio/vnd.digital-winds",
+ "eot": "application/vnd.ms-fontobject",
+ "eps": "application/postscript",
+ "epub": "application/epub+zip",
+ "es": "application/ecmascript",
+ "es3": "application/vnd.eszigno3+xml",
+ "esa": "application/vnd.osgi.subsystem",
+ "esf": "application/vnd.epson.esf",
+ "et3": "application/vnd.eszigno3+xml",
+ "eva": "application/x-eva",
+ "evy": "application/x-envoy",
+ "exe": "application/x-msdownload",
+ "exi": "application/exi",
+ "exp": "application/express",
+ "exr": "image/aces",
+ "ext": "application/vnd.novadigm.ext",
+ "ez": "application/andrew-inset",
+ "ez2": "application/vnd.ezpix-album",
+ "ez3": "application/vnd.ezpix-package",
+ "f4v": "video/x-f4v",
+ "fbs": "image/vnd.fastbidsheet",
+ "fcdt": "application/vnd.adobe.formscentral.fcdt",
+ "fcs": "application/vnd.isac.fcs",
+ "fdf": "application/vnd.fdf",
+ "fdt": "application/fdt+xml",
+ "fe_launch": "application/vnd.denovo.fcselayout-link",
+ "fg5": "application/vnd.fujitsu.oasysgp",
+ "fgd": "application/x-director",
+ "fh": "image/x-freehand",
+ "fh4": "image/x-freehand",
+ "fh5": "image/x-freehand",
+ "fh7": "image/x-freehand",
+ "fhc": "image/x-freehand",
+ "fig": "application/x-xfig",
+ "fits": "image/fits",
+ "flac": "audio/x-flac",
+ "fli": "video/x-fli",
+ "flo": "application/vnd.micrografx.flo",
+ "flv": "video/x-flv",
+ "flw": "application/vnd.kde.kivio",
+ "fm": "application/vnd.framemaker",
+ "fnc": "application/vnd.frogans.fnc",
+ "fo": "application/vnd.software602.filler.form+xml",
+ "fpx": "image/vnd.fpx",
+ "frame": "application/vnd.framemaker",
+ "fsc": "application/vnd.fsc.weblaunch",
+ "fst": "image/vnd.fst",
+ "ftc": "application/vnd.fluxtime.clip",
+ "fti": "application/vnd.anser-web-funds-transfer-initiation",
+ "fvt": "video/vnd.fvt",
+ "fxp": "application/vnd.adobe.fxp",
+ "fxpl": "application/vnd.adobe.fxp",
+ "fzs": "application/vnd.fuzzysheet",
+ "g2w": "application/vnd.geoplan",
+ "g3": "image/g3fax",
+ "g3w": "application/vnd.geospace",
+ "gac": "application/vnd.groove-account",
+ "gam": "application/x-tads",
+ "gbr": "application/rpki-ghostbusters",
+ "gca": "application/x-gca-compressed",
+ "gdl": "model/vnd.gdl",
+ "gdoc": "application/vnd.google-apps.document",
+ "geo": "application/vnd.dynageo",
+ "geojson": "application/geo+json",
+ "gex": "application/vnd.geometry-explorer",
+ "ggb": "application/vnd.geogebra.file",
+ "ggt": "application/vnd.geogebra.tool",
+ "ghf": "application/vnd.groove-help",
+ "gif": "image/gif",
+ "gim": "application/vnd.groove-identity-message",
+ "glb": "model/gltf-binary",
+ "gltf": "model/gltf+json",
+ "gml": "application/gml+xml",
+ "gmx": "application/vnd.gmx",
+ "gnumeric": "application/x-gnumeric",
+ "gph": "application/vnd.flographit",
+ "gpx": "application/gpx+xml",
+ "gqf": "application/vnd.grafeq",
+ "gqs": "application/vnd.grafeq",
+ "gram": "application/srgs",
+ "gramps": "application/x-gramps-xml",
+ "gre": "application/vnd.geometry-explorer",
+ "grv": "application/vnd.groove-injector",
+ "grxml": "application/srgs+xml",
+ "gsf": "application/x-font-ghostscript",
+ "gsheet": "application/vnd.google-apps.spreadsheet",
+ "gslides": "application/vnd.google-apps.presentation",
+ "gtar": "application/x-gtar",
+ "gtm": "application/vnd.groove-tool-message",
+ "gtw": "model/vnd.gtw",
+ "gxf": "application/gxf",
+ "gxt": "application/vnd.geonext",
+ "gz": "application/gzip",
+ "h261": "video/h261",
+ "h263": "video/h263",
+ "h264": "video/h264",
+ "hal": "application/vnd.hal+xml",
+ "hbci": "application/vnd.hbci",
+ "hdd": "application/x-virtualbox-hdd",
+ "hdf": "application/x-hdf",
+ "heic": "image/heic",
+ "heics": "image/heic-sequence",
+ "heif": "image/heif",
+ "heifs": "image/heif-sequence",
+ "hej2": "image/hej2k",
+ "held": "application/atsc-held+xml",
+ "hjson": "application/hjson",
+ "hlp": "application/winhlp",
+ "hpgl": "application/vnd.hp-hpgl",
+ "hpid": "application/vnd.hp-hpid",
+ "hps": "application/vnd.hp-hps",
+ "hqx": "application/mac-binhex40",
+ "hsj2": "image/hsj2",
+ "htke": "application/vnd.kenameaapp",
+ "hvd": "application/vnd.yamaha.hv-dic",
+ "hvp": "application/vnd.yamaha.hv-voice",
+ "hvs": "application/vnd.yamaha.hv-script",
+ "i2g": "application/vnd.intergeo",
+ "icc": "application/vnd.iccprofile",
+ "ice": "x-conference/x-cooltalk",
+ "icm": "application/vnd.iccprofile",
+ "ico": "image/x-icon",
+ "ief": "image/ief",
+ "ifm": "application/vnd.shana.informed.formdata",
+ "iges": "model/iges",
+ "igl": "application/vnd.igloader",
+ "igm": "application/vnd.insors.igm",
+ "igs": "model/iges",
+ "igx": "application/vnd.micrografx.igx",
+ "iif": "application/vnd.shana.informed.interchange",
+ "img": "application/octet-stream",
+ "imp": "application/vnd.accpac.simply.imp",
+ "ims": "application/vnd.ms-ims",
+ "ink": "application/inkml+xml",
+ "inkml": "application/inkml+xml",
+ "install": "application/x-install-instructions",
+ "iota": "application/vnd.astraea-software.iota",
+ "ipfix": "application/ipfix",
+ "ipk": "application/vnd.shana.informed.package",
+ "irm": "application/vnd.ibm.rights-management",
+ "irp": "application/vnd.irepository.package+xml",
+ "iso": "application/x-iso9660-image",
+ "itp": "application/vnd.shana.informed.formtemplate",
+ "its": "application/its+xml",
+ "ivp": "application/vnd.immervision-ivp",
+ "ivu": "application/vnd.immervision-ivu",
+ "jam": "application/vnd.jam",
+ "jar": "application/java-archive",
+ "jardiff": "application/x-java-archive-diff",
+ "jhc": "image/jphc",
+ "jisp": "application/vnd.jisp",
+ "jls": "image/jls",
+ "jlt": "application/vnd.hp-jlyt",
+ "jng": "image/x-jng",
+ "jnlp": "application/x-java-jnlp-file",
+ "joda": "application/vnd.joost.joda-archive",
+ "jp2": "image/jp2",
+ "jpe": "image/jpeg",
+ "jpeg": "image/jpeg",
+ "jpf": "image/jpx",
+ "jpg": "image/jpeg",
+ "jpg2": "image/jp2",
+ "jpgm": "video/jpm",
+ "jpgv": "video/jpeg",
+ "jph": "image/jph",
+ "jpm": "video/jpm",
+ "jpx": "image/jpx",
+ "js": "application/javascript",
+ "json": "application/json",
+ "json5": "application/json5",
+ "jsonld": "application/ld+json",
+ "jsonml": "application/jsonml+json",
+ "jxr": "image/jxr",
+ "jxra": "image/jxra",
+ "jxrs": "image/jxrs",
+ "jxs": "image/jxs",
+ "jxsc": "image/jxsc",
+ "jxsi": "image/jxsi",
+ "jxss": "image/jxss",
+ "kar": "audio/midi",
+ "karbon": "application/vnd.kde.karbon",
+ "kdbx": "application/x-keepass2",
+ "key": "application/x-iwork-keynote-sffkey",
+ "kfo": "application/vnd.kde.kformula",
+ "kia": "application/vnd.kidspiration",
+ "kml": "application/vnd.google-earth.kml+xml",
+ "kmz": "application/vnd.google-earth.kmz",
+ "kne": "application/vnd.kinar",
+ "knp": "application/vnd.kinar",
+ "kon": "application/vnd.kde.kontour",
+ "kpr": "application/vnd.kde.kpresenter",
+ "kpt": "application/vnd.kde.kpresenter",
+ "kpxx": "application/vnd.ds-keypoint",
+ "ksp": "application/vnd.kde.kspread",
+ "ktr": "application/vnd.kahootz",
+ "ktx": "image/ktx",
+ "ktx2": "image/ktx2",
+ "ktz": "application/vnd.kahootz",
+ "kwd": "application/vnd.kde.kword",
+ "kwt": "application/vnd.kde.kword",
+ "lasxml": "application/vnd.las.las+xml",
+ "latex": "application/x-latex",
+ "lbd": "application/vnd.llamagraphics.life-balance.desktop",
+ "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
+ "les": "application/vnd.hhe.lesson-player",
+ "lgr": "application/lgr+xml",
+ "lha": "application/x-lzh-compressed",
+ "link66": "application/vnd.route66.link66+xml",
+ "list3820": "application/vnd.ibm.modcap",
+ "listafp": "application/vnd.ibm.modcap",
+ "lnk": "application/x-ms-shortcut",
+ "lostxml": "application/lost+xml",
+ "lrf": "application/octet-stream",
+ "lrm": "application/vnd.ms-lrm",
+ "ltf": "application/vnd.frogans.ltf",
+ "luac": "application/x-lua-bytecode",
+ "lvp": "audio/vnd.lucent.voice",
+ "lwp": "application/vnd.lotus-wordpro",
+ "lzh": "application/x-lzh-compressed",
+ "m13": "application/x-msmediaview",
+ "m14": "application/x-msmediaview",
+ "m1v": "video/mpeg",
+ "m21": "application/mp21",
+ "m2a": "audio/mpeg",
+ "m2v": "video/mpeg",
+ "m3a": "audio/mpeg",
+ "m3u": "audio/x-mpegurl",
+ "m3u8": "application/vnd.apple.mpegurl",
+ "m4a": "audio/x-m4a",
+ "m4p": "application/mp4",
+ "m4s": "video/iso.segment",
+ "m4u": "video/vnd.mpegurl",
+ "m4v": "video/x-m4v",
+ "ma": "application/mathematica",
+ "mads": "application/mads+xml",
+ "maei": "application/mmt-aei+xml",
+ "mag": "application/vnd.ecowin.chart",
+ "maker": "application/vnd.framemaker",
+ "map": "application/json",
+ "mar": "application/octet-stream",
+ "mathml": "application/mathml+xml",
+ "mb": "application/mathematica",
+ "mbk": "application/vnd.mobius.mbk",
+ "mbox": "application/mbox",
+ "mc1": "application/vnd.medcalcdata",
+ "mcd": "application/vnd.mcd",
+ "mdb": "application/x-msaccess",
+ "mdi": "image/vnd.ms-modi",
+ "mesh": "model/mesh",
+ "meta4": "application/metalink4+xml",
+ "metalink": "application/metalink+xml",
+ "mets": "application/mets+xml",
+ "mfm": "application/vnd.mfmp",
+ "mft": "application/rpki-manifest",
+ "mgp": "application/vnd.osgeo.mapguide.package",
+ "mgz": "application/vnd.proteus.magazine",
+ "mid": "audio/midi",
+ "midi": "audio/midi",
+ "mie": "application/x-mie",
+ "mif": "application/vnd.mif",
+ "mime": "message/rfc822",
+ "mj2": "video/mj2",
+ "mjp2": "video/mj2",
+ "mjs": "application/javascript",
+ "mk3d": "video/x-matroska",
+ "mka": "audio/x-matroska",
+ "mks": "video/x-matroska",
+ "mkv": "video/x-matroska",
+ "mlp": "application/vnd.dolby.mlp",
+ "mmd": "application/vnd.chipnuts.karaoke-mmd",
+ "mmf": "application/vnd.smaf",
+ "mmr": "image/vnd.fujixerox.edmics-mmr",
+ "mng": "video/x-mng",
+ "mny": "application/x-msmoney",
+ "mobi": "application/x-mobipocket-ebook",
+ "mods": "application/mods+xml",
+ "mov": "video/quicktime",
+ "movie": "video/x-sgi-movie",
+ "mp2": "audio/mpeg",
+ "mp21": "application/mp21",
+ "mp2a": "audio/mpeg",
+ "mp3": "audio/mpeg",
+ "mp4": "video/mp4",
+ "mp4a": "audio/mp4",
+ "mp4s": "application/mp4",
+ "mp4v": "video/mp4",
+ "mpc": "application/vnd.mophun.certificate",
+ "mpd": "application/dash+xml",
+ "mpe": "video/mpeg",
+ "mpeg": "video/mpeg",
+ "mpg": "video/mpeg",
+ "mpg4": "video/mp4",
+ "mpga": "audio/mpeg",
+ "mpkg": "application/vnd.apple.installer+xml",
+ "mpm": "application/vnd.blueice.multipass",
+ "mpn": "application/vnd.mophun.application",
+ "mpp": "application/vnd.ms-project",
+ "mpt": "application/vnd.ms-project",
+ "mpy": "application/vnd.ibm.minipay",
+ "mqy": "application/vnd.mobius.mqy",
+ "mrc": "application/marc",
+ "mrcx": "application/marcxml+xml",
+ "mscml": "application/mediaservercontrol+xml",
+ "mseed": "application/vnd.fdsn.mseed",
+ "mseq": "application/vnd.mseq",
+ "msf": "application/vnd.epson.msf",
+ "msg": "application/vnd.ms-outlook",
+ "msh": "model/mesh",
+ "msi": "application/x-msdownload",
+ "msl": "application/vnd.mobius.msl",
+ "msm": "application/octet-stream",
+ "msp": "application/octet-stream",
+ "msty": "application/vnd.muvee.style",
+ "mtl": "model/mtl",
+ "mts": "model/vnd.mts",
+ "mus": "application/vnd.musician",
+ "musd": "application/mmt-usd+xml",
+ "musicxml": "application/vnd.recordare.musicxml+xml",
+ "mvb": "application/x-msmediaview",
+ "mvt": "application/vnd.mapbox-vector-tile",
+ "mwf": "application/vnd.mfer",
+ "mxf": "application/mxf",
+ "mxl": "application/vnd.recordare.musicxml",
+ "mxmf": "audio/mobile-xmf",
+ "mxml": "application/xv+xml",
+ "mxs": "application/vnd.triscape.mxs",
+ "mxu": "video/vnd.mpegurl",
+ "n-gage": "application/vnd.nokia.n-gage.symbian.install",
+ "nb": "application/mathematica",
+ "nbp": "application/vnd.wolfram.player",
+ "nc": "application/x-netcdf",
+ "ncx": "application/x-dtbncx+xml",
+ "ngdat": "application/vnd.nokia.n-gage.data",
+ "nitf": "application/vnd.nitf",
+ "nlu": "application/vnd.neurolanguage.nlu",
+ "nml": "application/vnd.enliven",
+ "nnd": "application/vnd.noblenet-directory",
+ "nns": "application/vnd.noblenet-sealer",
+ "nnw": "application/vnd.noblenet-web",
+ "npx": "image/vnd.net-fpx",
+ "nq": "application/n-quads",
+ "nsc": "application/x-conference",
+ "nsf": "application/vnd.lotus-notes",
+ "nt": "application/n-triples",
+ "ntf": "application/vnd.nitf",
+ "numbers": "application/x-iwork-numbers-sffnumbers",
+ "nzb": "application/x-nzb",
+ "oa2": "application/vnd.fujitsu.oasys2",
+ "oa3": "application/vnd.fujitsu.oasys3",
+ "oas": "application/vnd.fujitsu.oasys",
+ "obd": "application/x-msbinder",
+ "obgx": "application/vnd.openblox.game+xml",
+ "obj": "model/obj",
+ "oda": "application/oda",
+ "odb": "application/vnd.oasis.opendocument.database",
+ "odc": "application/vnd.oasis.opendocument.chart",
+ "odf": "application/vnd.oasis.opendocument.formula",
+ "odft": "application/vnd.oasis.opendocument.formula-template",
+ "odg": "application/vnd.oasis.opendocument.graphics",
+ "odi": "application/vnd.oasis.opendocument.image",
+ "odm": "application/vnd.oasis.opendocument.text-master",
+ "odp": "application/vnd.oasis.opendocument.presentation",
+ "ods": "application/vnd.oasis.opendocument.spreadsheet",
+ "odt": "application/vnd.oasis.opendocument.text",
+ "oga": "audio/ogg",
+ "ogex": "model/vnd.opengex",
+ "ogg": "audio/ogg",
+ "ogv": "video/ogg",
+ "ogx": "application/ogg",
+ "omdoc": "application/omdoc+xml",
+ "onepkg": "application/onenote",
+ "onetmp": "application/onenote",
+ "onetoc": "application/onenote",
+ "onetoc2": "application/onenote",
+ "opf": "application/oebps-package+xml",
+ "oprc": "application/vnd.palm",
+ "opus": "audio/ogg",
+ "osf": "application/vnd.yamaha.openscoreformat",
+ "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
+ "osm": "application/vnd.openstreetmap.data+xml",
+ "otc": "application/vnd.oasis.opendocument.chart-template",
+ "otf": "font/otf",
+ "otg": "application/vnd.oasis.opendocument.graphics-template",
+ "oth": "application/vnd.oasis.opendocument.text-web",
+ "oti": "application/vnd.oasis.opendocument.image-template",
+ "otp": "application/vnd.oasis.opendocument.presentation-template",
+ "ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+ "ott": "application/vnd.oasis.opendocument.text-template",
+ "ova": "application/x-virtualbox-ova",
+ "ovf": "application/x-virtualbox-ovf",
+ "owl": "application/rdf+xml",
+ "oxps": "application/oxps",
+ "oxt": "application/vnd.openofficeorg.extension",
+ "p10": "application/pkcs10",
+ "p12": "application/x-pkcs12",
+ "p7b": "application/x-pkcs7-certificates",
+ "p7c": "application/pkcs7-mime",
+ "p7m": "application/pkcs7-mime",
+ "p7r": "application/x-pkcs7-certreqresp",
+ "p7s": "application/pkcs7-signature",
+ "p8": "application/pkcs8",
+ "pac": "application/x-ns-proxy-autoconfig",
+ "pages": "application/x-iwork-pages-sffpages",
+ "paw": "application/vnd.pawaafile",
+ "pbd": "application/vnd.powerbuilder6",
+ "pbm": "image/x-portable-bitmap",
+ "pcap": "application/vnd.tcpdump.pcap",
+ "pcf": "application/x-font-pcf",
+ "pcl": "application/vnd.hp-pcl",
+ "pclxl": "application/vnd.hp-pclxl",
+ "pct": "image/x-pict",
+ "pcurl": "application/vnd.curl.pcurl",
+ "pcx": "image/x-pcx",
+ "pdb": "application/x-pilot",
+ "pdf": "application/pdf",
+ "pem": "application/x-x509-ca-cert",
+ "pfa": "application/x-font-type1",
+ "pfb": "application/x-font-type1",
+ "pfm": "application/x-font-type1",
+ "pfr": "application/font-tdpfr",
+ "pfx": "application/x-pkcs12",
+ "pgm": "image/x-portable-graymap",
+ "pgn": "application/x-chess-pgn",
+ "pgp": "application/pgp-encrypted",
+ "php": "application/x-httpd-php",
+ "pic": "image/x-pict",
+ "pkg": "application/octet-stream",
+ "pki": "application/pkixcmp",
+ "pkipath": "application/pkix-pkipath",
+ "pkpass": "application/vnd.apple.pkpass",
+ "pl": "application/x-perl",
+ "plb": "application/vnd.3gpp.pic-bw-large",
+ "plc": "application/vnd.mobius.plc",
+ "plf": "application/vnd.pocketlearn",
+ "pls": "application/pls+xml",
+ "pm": "application/x-perl",
+ "pml": "application/vnd.ctc-posml",
+ "png": "image/png",
+ "pnm": "image/x-portable-anymap",
+ "portpkg": "application/vnd.macports.portpkg",
+ "pot": "application/vnd.ms-powerpoint",
+ "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
+ "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+ "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
+ "ppd": "application/vnd.cups-ppd",
+ "ppm": "image/x-portable-pixmap",
+ "pps": "application/vnd.ms-powerpoint",
+ "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
+ "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ "ppt": "application/vnd.ms-powerpoint",
+ "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
+ "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "pqa": "application/vnd.palm",
+ "prc": "application/x-pilot",
+ "pre": "application/vnd.lotus-freelance",
+ "prf": "application/pics-rules",
+ "provx": "application/provenance+xml",
+ "ps": "application/postscript",
+ "psb": "application/vnd.3gpp.pic-bw-small",
+ "psd": "image/vnd.adobe.photoshop",
+ "psf": "application/x-font-linux-psf",
+ "pskcxml": "application/pskc+xml",
+ "pti": "image/prs.pti",
+ "ptid": "application/vnd.pvi.ptid1",
+ "pub": "application/x-mspublisher",
+ "pvb": "application/vnd.3gpp.pic-bw-var",
+ "pwn": "application/vnd.3m.post-it-notes",
+ "pya": "audio/vnd.ms-playready.media.pya",
+ "pyv": "video/vnd.ms-playready.media.pyv",
+ "qam": "application/vnd.epson.quickanime",
+ "qbo": "application/vnd.intu.qbo",
+ "qfx": "application/vnd.intu.qfx",
+ "qps": "application/vnd.publishare-delta-tree",
+ "qt": "video/quicktime",
+ "qwd": "application/vnd.quark.quarkxpress",
+ "qwt": "application/vnd.quark.quarkxpress",
+ "qxb": "application/vnd.quark.quarkxpress",
+ "qxd": "application/vnd.quark.quarkxpress",
+ "qxl": "application/vnd.quark.quarkxpress",
+ "qxt": "application/vnd.quark.quarkxpress",
+ "ra": "audio/x-realaudio",
+ "ram": "audio/x-pn-realaudio",
+ "raml": "application/raml+yaml",
+ "rapd": "application/route-apd+xml",
+ "rar": "application/x-rar-compressed",
+ "ras": "image/x-cmu-raster",
+ "rcprofile": "application/vnd.ipunplugged.rcprofile",
+ "rdf": "application/rdf+xml",
+ "rdz": "application/vnd.data-vision.rdz",
+ "relo": "application/p2p-overlay+xml",
+ "rep": "application/vnd.businessobjects",
+ "res": "application/x-dtbresource+xml",
+ "rgb": "image/x-rgb",
+ "rif": "application/reginfo+xml",
+ "rip": "audio/vnd.rip",
+ "ris": "application/x-research-info-systems",
+ "rl": "application/resource-lists+xml",
+ "rlc": "image/vnd.fujixerox.edmics-rlc",
+ "rld": "application/resource-lists-diff+xml",
+ "rm": "application/vnd.rn-realmedia",
+ "rmi": "audio/midi",
+ "rmp": "audio/x-pn-realaudio-plugin",
+ "rms": "application/vnd.jcp.javame.midlet-rms",
+ "rmvb": "application/vnd.rn-realmedia-vbr",
+ "rnc": "application/relax-ng-compact-syntax",
+ "rng": "application/xml",
+ "roa": "application/rpki-roa",
+ "rp9": "application/vnd.cloanto.rp9",
+ "rpm": "application/x-redhat-package-manager",
+ "rpss": "application/vnd.nokia.radio-presets",
+ "rpst": "application/vnd.nokia.radio-preset",
+ "rq": "application/sparql-query",
+ "rs": "application/rls-services+xml",
+ "rsat": "application/atsc-rsat+xml",
+ "rsd": "application/rsd+xml",
+ "rsheet": "application/urc-ressheet+xml",
+ "rss": "application/rss+xml",
+ "run": "application/x-makeself",
+ "rusd": "application/route-usd+xml",
+ "s3m": "audio/s3m",
+ "saf": "application/vnd.yamaha.smaf-audio",
+ "sbml": "application/sbml+xml",
+ "sc": "application/vnd.ibm.secure-container",
+ "scd": "application/x-msschedule",
+ "scm": "application/vnd.lotus-screencam",
+ "scq": "application/scvp-cv-request",
+ "scs": "application/scvp-cv-response",
+ "sda": "application/vnd.stardivision.draw",
+ "sdc": "application/vnd.stardivision.calc",
+ "sdd": "application/vnd.stardivision.impress",
+ "sdkd": "application/vnd.solent.sdkm+xml",
+ "sdkm": "application/vnd.solent.sdkm+xml",
+ "sdp": "application/sdp",
+ "sdw": "application/vnd.stardivision.writer",
+ "sea": "application/x-sea",
+ "see": "application/vnd.seemail",
+ "seed": "application/vnd.fdsn.seed",
+ "sema": "application/vnd.sema",
+ "semd": "application/vnd.semd",
+ "semf": "application/vnd.semf",
+ "senmlx": "application/senml+xml",
+ "sensmlx": "application/sensml+xml",
+ "ser": "application/java-serialized-object",
+ "setpay": "application/set-payment-initiation",
+ "setreg": "application/set-registration-initiation",
+ "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
+ "sfs": "application/vnd.spotfire.sfs",
+ "sgi": "image/sgi",
+ "sgl": "application/vnd.stardivision.writer-global",
+ "sh": "application/x-sh",
+ "shar": "application/x-shar",
+ "shf": "application/shf+xml",
+ "sid": "image/x-mrsid-image",
+ "sieve": "application/sieve",
+ "sig": "application/pgp-signature",
+ "sil": "audio/silk",
+ "silo": "model/mesh",
+ "sis": "application/vnd.symbian.install",
+ "sisx": "application/vnd.symbian.install",
+ "sit": "application/x-stuffit",
+ "sitx": "application/x-stuffitx",
+ "siv": "application/sieve",
+ "skd": "application/vnd.koan",
+ "skm": "application/vnd.koan",
+ "skp": "application/vnd.koan",
+ "skt": "application/vnd.koan",
+ "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
+ "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ "sls": "application/route-s-tsid+xml",
+ "slt": "application/vnd.epson.salt",
+ "sm": "application/vnd.stepmania.stepchart",
+ "smf": "application/vnd.stardivision.math",
+ "smi": "application/smil+xml",
+ "smil": "application/smil+xml",
+ "smv": "video/x-smv",
+ "smzip": "application/vnd.stepmania.package",
+ "snd": "audio/basic",
+ "snf": "application/x-font-snf",
+ "so": "application/octet-stream",
+ "spc": "application/x-pkcs7-certificates",
+ "spf": "application/vnd.yamaha.smaf-phrase",
+ "spl": "application/x-futuresplash",
+ "spp": "application/scvp-vp-response",
+ "spq": "application/scvp-vp-request",
+ "spx": "audio/ogg",
+ "sql": "application/x-sql",
+ "src": "application/x-wais-source",
+ "srt": "application/x-subrip",
+ "sru": "application/sru+xml",
+ "srx": "application/sparql-results+xml",
+ "ssdl": "application/ssdl+xml",
+ "sse": "application/vnd.kodak-descriptor",
+ "ssf": "application/vnd.epson.ssf",
+ "ssml": "application/ssml+xml",
+ "st": "application/vnd.sailingtracker.track",
+ "stc": "application/vnd.sun.xml.calc.template",
+ "std": "application/vnd.sun.xml.draw.template",
+ "stf": "application/vnd.wt.stf",
+ "sti": "application/vnd.sun.xml.impress.template",
+ "stk": "application/hyperstudio",
+ "stl": "model/stl",
+ "stpx": "model/step+xml",
+ "stpxz": "model/step-xml+zip",
+ "stpz": "model/step+zip",
+ "str": "application/vnd.pg.format",
+ "stw": "application/vnd.sun.xml.writer.template",
+ "sus": "application/vnd.sus-calendar",
+ "susp": "application/vnd.sus-calendar",
+ "sv4cpio": "application/x-sv4cpio",
+ "sv4crc": "application/x-sv4crc",
+ "svc": "application/vnd.dvb.service",
+ "svd": "application/vnd.svd",
+ "svg": "image/svg+xml",
+ "svgz": "image/svg+xml",
+ "swa": "application/x-director",
+ "swf": "application/x-shockwave-flash",
+ "swi": "application/vnd.aristanetworks.swi",
+ "swidtag": "application/swid+xml",
+ "sxc": "application/vnd.sun.xml.calc",
+ "sxd": "application/vnd.sun.xml.draw",
+ "sxg": "application/vnd.sun.xml.writer.global",
+ "sxi": "application/vnd.sun.xml.impress",
+ "sxm": "application/vnd.sun.xml.math",
+ "sxw": "application/vnd.sun.xml.writer",
+ "t3": "application/x-t3vm-image",
+ "t38": "image/t38",
+ "taglet": "application/vnd.mynfc",
+ "tao": "application/vnd.tao.intent-module-archive",
+ "tap": "image/vnd.tencent.tap",
+ "tar": "application/x-tar",
+ "tcap": "application/vnd.3gpp2.tcap",
+ "tcl": "application/x-tcl",
+ "td": "application/urc-targetdesc+xml",
+ "teacher": "application/vnd.smart.teacher",
+ "tei": "application/tei+xml",
+ "teicorpus": "application/tei+xml",
+ "tex": "application/x-tex",
+ "texi": "application/x-texinfo",
+ "texinfo": "application/x-texinfo",
+ "tfi": "application/thraud+xml",
+ "tfm": "application/x-tex-tfm",
+ "tfx": "image/tiff-fx",
+ "tga": "image/x-tga",
+ "thmx": "application/vnd.ms-officetheme",
+ "tif": "image/tiff",
+ "tiff": "image/tiff",
+ "tk": "application/x-tcl",
+ "tmo": "application/vnd.tmobile-livetv",
+ "toml": "application/toml",
+ "torrent": "application/x-bittorrent",
+ "tpl": "application/vnd.groove-tool-template",
+ "tpt": "application/vnd.trid.tpt",
+ "tra": "application/vnd.trueapp",
+ "trig": "application/trig",
+ "trm": "application/x-msterminal",
+ "ts": "video/mp2t",
+ "tsd": "application/timestamped-data",
+ "ttc": "font/collection",
+ "ttf": "font/ttf",
+ "ttml": "application/ttml+xml",
+ "twd": "application/vnd.simtech-mindmapper",
+ "twds": "application/vnd.simtech-mindmapper",
+ "txd": "application/vnd.genomatix.tuxedo",
+ "txf": "application/vnd.mobius.txf",
+ "u32": "application/x-authorware-bin",
+ "u8dsn": "message/global-delivery-status",
+ "u8hdr": "message/global-headers",
+ "u8mdn": "message/global-disposition-notification",
+ "u8msg": "message/global",
+ "ubj": "application/ubjson",
+ "udeb": "application/x-debian-package",
+ "ufd": "application/vnd.ufdl",
+ "ufdl": "application/vnd.ufdl",
+ "ulx": "application/x-glulx",
+ "umj": "application/vnd.umajin",
+ "unityweb": "application/vnd.unity",
+ "uoml": "application/vnd.uoml+xml",
+ "usdz": "model/vnd.usdz+zip",
+ "ustar": "application/x-ustar",
+ "utz": "application/vnd.uiq.theme",
+ "uva": "audio/vnd.dece.audio",
+ "uvd": "application/vnd.dece.data",
+ "uvf": "application/vnd.dece.data",
+ "uvg": "image/vnd.dece.graphic",
+ "uvh": "video/vnd.dece.hd",
+ "uvi": "image/vnd.dece.graphic",
+ "uvm": "video/vnd.dece.mobile",
+ "uvp": "video/vnd.dece.pd",
+ "uvs": "video/vnd.dece.sd",
+ "uvt": "application/vnd.dece.ttml+xml",
+ "uvu": "video/vnd.uvvu.mp4",
+ "uvv": "video/vnd.dece.video",
+ "uvva": "audio/vnd.dece.audio",
+ "uvvd": "application/vnd.dece.data",
+ "uvvf": "application/vnd.dece.data",
+ "uvvg": "image/vnd.dece.graphic",
+ "uvvh": "video/vnd.dece.hd",
+ "uvvi": "image/vnd.dece.graphic",
+ "uvvm": "video/vnd.dece.mobile",
+ "uvvp": "video/vnd.dece.pd",
+ "uvvs": "video/vnd.dece.sd",
+ "uvvt": "application/vnd.dece.ttml+xml",
+ "uvvu": "video/vnd.uvvu.mp4",
+ "uvvv": "video/vnd.dece.video",
+ "uvvx": "application/vnd.dece.unspecified",
+ "uvvz": "application/vnd.dece.zip",
+ "uvx": "application/vnd.dece.unspecified",
+ "uvz": "application/vnd.dece.zip",
+ "vbox": "application/x-virtualbox-vbox",
+ "vbox-extpack": "application/x-virtualbox-vbox-extpack",
+ "vcd": "application/x-cdlink",
+ "vcg": "application/vnd.groove-vcard",
+ "vcx": "application/vnd.vcx",
+ "vdi": "application/x-virtualbox-vdi",
+ "vds": "model/vnd.sap.vds",
+ "vhd": "application/x-virtualbox-vhd",
+ "vis": "application/vnd.visionary",
+ "viv": "video/vnd.vivo",
+ "vmdk": "application/x-virtualbox-vmdk",
+ "vob": "video/x-ms-vob",
+ "vor": "application/vnd.stardivision.writer",
+ "vox": "application/x-authorware-bin",
+ "vrml": "model/vrml",
+ "vsd": "application/vnd.visio",
+ "vsf": "application/vnd.vsf",
+ "vss": "application/vnd.visio",
+ "vst": "application/vnd.visio",
+ "vsw": "application/vnd.visio",
+ "vtf": "image/vnd.valve.source.texture",
+ "vtu": "model/vnd.vtu",
+ "vxml": "application/voicexml+xml",
+ "w3d": "application/x-director",
+ "wad": "application/x-doom",
+ "wadl": "application/vnd.sun.wadl+xml",
+ "war": "application/java-archive",
+ "wasm": "application/wasm",
+ "wav": "audio/x-wav",
+ "wax": "audio/x-ms-wax",
+ "wbmp": "image/vnd.wap.wbmp",
+ "wbs": "application/vnd.criticaltools.wbs+xml",
+ "wbxml": "application/vnd.wap.wbxml",
+ "wcm": "application/vnd.ms-works",
+ "wdb": "application/vnd.ms-works",
+ "wdp": "image/vnd.ms-photo",
+ "weba": "audio/webm",
+ "webapp": "application/x-web-app-manifest+json",
+ "webm": "video/webm",
+ "webmanifest": "application/manifest+json",
+ "webp": "image/webp",
+ "wg": "application/vnd.pmi.widget",
+ "wgt": "application/widget",
+ "wks": "application/vnd.ms-works",
+ "wm": "video/x-ms-wm",
+ "wma": "audio/x-ms-wma",
+ "wmd": "application/x-ms-wmd",
+ "wmf": "image/wmf",
+ "wmlc": "application/vnd.wap.wmlc",
+ "wmlsc": "application/vnd.wap.wmlscriptc",
+ "wmv": "video/x-ms-wmv",
+ "wmx": "video/x-ms-wmx",
+ "wmz": "application/x-msmetafile",
+ "woff": "font/woff",
+ "woff2": "font/woff2",
+ "wpd": "application/vnd.wordperfect",
+ "wpl": "application/vnd.ms-wpl",
+ "wps": "application/vnd.ms-works",
+ "wqd": "application/vnd.wqd",
+ "wri": "application/x-mswrite",
+ "wrl": "model/vrml",
+ "wsc": "message/vnd.wfa.wsc",
+ "wsdl": "application/wsdl+xml",
+ "wspolicy": "application/wspolicy+xml",
+ "wtb": "application/vnd.webturbo",
+ "wvx": "video/x-ms-wvx",
+ "x32": "application/x-authorware-bin",
+ "x3d": "model/x3d+xml",
+ "x3db": "model/x3d+fastinfoset",
+ "x3dbz": "model/x3d+binary",
+ "x3dv": "model/x3d-vrml",
+ "x3dvz": "model/x3d+vrml",
+ "x3dz": "model/x3d+xml",
+ "x_b": "model/vnd.parasolid.transmit.binary",
+ "x_t": "model/vnd.parasolid.transmit.text",
+ "xaml": "application/xaml+xml",
+ "xap": "application/x-silverlight-app",
+ "xar": "application/vnd.xara",
+ "xav": "application/xcap-att+xml",
+ "xbap": "application/x-ms-xbap",
+ "xbd": "application/vnd.fujixerox.docuworks.binder",
+ "xbm": "image/x-xbitmap",
+ "xca": "application/xcap-caps+xml",
+ "xcs": "application/calendar+xml",
+ "xdf": "application/xcap-diff+xml",
+ "xdm": "application/vnd.syncml.dm+xml",
+ "xdp": "application/vnd.adobe.xdp+xml",
+ "xdssc": "application/dssc+xml",
+ "xdw": "application/vnd.fujixerox.docuworks",
+ "xel": "application/xcap-el+xml",
+ "xenc": "application/xenc+xml",
+ "xer": "application/patch-ops-error+xml",
+ "xfdf": "application/vnd.adobe.xfdf",
+ "xfdl": "application/vnd.xfdl",
+ "xht": "application/xhtml+xml",
+ "xhtml": "application/xhtml+xml",
+ "xhvml": "application/xv+xml",
+ "xif": "image/vnd.xiff",
+ "xla": "application/vnd.ms-excel",
+ "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
+ "xlc": "application/vnd.ms-excel",
+ "xlf": "application/xliff+xml",
+ "xlm": "application/vnd.ms-excel",
+ "xls": "application/vnd.ms-excel",
+ "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
+ "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
+ "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "xlt": "application/vnd.ms-excel",
+ "xltm": "application/vnd.ms-excel.template.macroenabled.12",
+ "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ "xlw": "application/vnd.ms-excel",
+ "xm": "audio/xm",
+ "xns": "application/xcap-ns+xml",
+ "xo": "application/vnd.olpc-sugar",
+ "xop": "application/xop+xml",
+ "xpi": "application/x-xpinstall",
+ "xpl": "application/xproc+xml",
+ "xpm": "image/x-xpixmap",
+ "xpr": "application/vnd.is-xpr",
+ "xps": "application/vnd.ms-xpsdocument",
+ "xpw": "application/vnd.intercon.formnet",
+ "xpx": "application/vnd.intercon.formnet",
+ "xsd": "application/xml",
+ "xsl": "application/xslt+xml",
+ "xslt": "application/xslt+xml",
+ "xsm": "application/vnd.syncml+xml",
+ "xspf": "application/xspf+xml",
+ "xul": "application/vnd.mozilla.xul+xml",
+ "xvm": "application/xv+xml",
+ "xvml": "application/xv+xml",
+ "xwd": "image/x-xwindowdump",
+ "xyz": "chemical/x-xyz",
+ "xz": "application/x-xz",
+ "yang": "application/yang",
+ "yin": "application/yin+xml",
+ "z1": "application/x-zmachine",
+ "z2": "application/x-zmachine",
+ "z3": "application/x-zmachine",
+ "z4": "application/x-zmachine",
+ "z5": "application/x-zmachine",
+ "z6": "application/x-zmachine",
+ "z7": "application/x-zmachine",
+ "z8": "application/x-zmachine",
+ "zaz": "application/vnd.zzazz.deck+xml",
+ "zip": "application/zip",
+ "zir": "application/vnd.zul",
+ "zirz": "application/vnd.zul",
+ "zmm": "application/vnd.handheld-entertainment+xml"
+}
+
+global mimetypesText
+mimetypesText = {
+ "3dml": "text/vnd.in3d.3dml",
+ "appcache": "text/cache-manifest",
+ "asm": "text/x-asm",
+ "c": "text/x-c",
+ "cc": "text/x-c",
+ "coffee": "text/coffeescript",
+ "conf": "text/plain",
+ "cpp": "text/x-c",
+ "css": "text/css",
+ "csv": "text/csv",
+ "curl": "text/vnd.curl",
+ "cxx": "text/x-c",
+ "dcurl": "text/vnd.curl.dcurl",
+ "def": "text/plain",
+ "dic": "text/x-c",
+ "dsc": "text/prs.lines.tag",
+ "etx": "text/x-setext",
+ "f": "text/x-fortran",
+ "f77": "text/x-fortran",
+ "f90": "text/x-fortran",
+ "flx": "text/vnd.fmi.flexstor",
+ "fly": "text/vnd.fly",
+ "for": "text/x-fortran",
+ "gv": "text/vnd.graphviz",
+ "h": "text/x-c",
+ "hbs": "text/x-handlebars-template",
+ "hh": "text/x-c",
+ "htc": "text/x-component",
+ "htm": "text/html",
+ "html": "text/html",
+ "ics": "text/calendar",
+ "ifb": "text/calendar",
+ "in": "text/plain",
+ "ini": "text/plain",
+ "jad": "text/vnd.sun.j2me.app-descriptor",
+ "jade": "text/jade",
+ "java": "text/x-java-source",
+ "jsx": "text/jsx",
+ "less": "text/less",
+ "list": "text/plain",
+ "litcoffee": "text/coffeescript",
+ "log": "text/plain",
+ "lua": "text/x-lua",
+ "man": "text/troff",
+ "manifest": "text/cache-manifest",
+ "markdown": "text/markdown",
+ "mcurl": "text/vnd.curl.mcurl",
+ "md": "text/markdown",
+ "mdx": "text/mdx",
+ "me": "text/troff",
+ "mkd": "text/x-markdown",
+ "mml": "text/mathml",
+ "ms": "text/troff",
+ "n3": "text/n3",
+ "nfo": "text/x-nfo",
+ "opml": "text/x-opml",
+ "org": "text/x-org",
+ "p": "text/x-pascal",
+ "pas": "text/x-pascal",
+ "pde": "text/x-processing",
+ "roff": "text/troff",
+ "rtf": "text/rtf",
+ "rtx": "text/richtext",
+ "s": "text/x-asm",
+ "sass": "text/x-sass",
+ "scss": "text/x-scss",
+ "scurl": "text/vnd.curl.scurl",
+ "sfv": "text/x-sfv",
+ "sgm": "text/sgml",
+ "sgml": "text/sgml",
+ "shex": "text/shex",
+ "shtml": "text/html",
+ "slim": "text/slim",
+ "slm": "text/slim",
+ "spdx": "text/spdx",
+ "spot": "text/vnd.in3d.spot",
+ "styl": "text/stylus",
+ "stylus": "text/stylus",
+ "sub": "text/vnd.dvb.subtitle",
+ "t": "text/troff",
+ "text": "text/plain",
+ "tr": "text/troff",
+ "tsv": "text/tab-separated-values",
+ "ttl": "text/turtle",
+ "txt": "text/plain",
+ "uri": "text/uri-list",
+ "uris": "text/uri-list",
+ "urls": "text/uri-list",
+ "uu": "text/x-uuencode",
+ "vcard": "text/vcard",
+ "vcf": "text/x-vcard",
+ "vcs": "text/x-vcalendar",
+ "vtt": "text/vtt",
+ "wml": "text/vnd.wap.wml",
+ "wmls": "text/vnd.wap.wmlscript",
+ "xml": "text/xml",
+ "yaml": "text/yaml",
+ "yml": "text/yaml",
+ "ymp": "text/x-suse-ymp"
+}
\ No newline at end of file
diff --git a/modules/http/file-handlers/pyp.py b/modules/http/file-handlers/pyp.py
new file mode 100644
index 0000000..a703d24
--- /dev/null
+++ b/modules/http/file-handlers/pyp.py
@@ -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
\ No newline at end of file
diff --git a/modules/http/file-handlers/text.py b/modules/http/file-handlers/text.py
new file mode 100644
index 0000000..cc75142
--- /dev/null
+++ b/modules/http/file-handlers/text.py
@@ -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")
\ No newline at end of file
diff --git a/modules/http/helpers.py b/modules/http/helpers.py
new file mode 100644
index 0000000..4859f5d
--- /dev/null
+++ b/modules/http/helpers.py
@@ -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
+ },('''\
+
+
+
+Referring you to ''' +html.escape(path)+ '''...
+
+''').encode("ascii")
+ )
+
+global notFound
+def notFound(connection,path):
+ simpleResponse(
+ connection,"404 Not Found",
+ {
+ "Content-Type": "text/html; charset=ASCII"
+ },('''\
+
+
+
+Not found: ''' +html.escape(path)+ '''
+
+''').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
\ No newline at end of file
diff --git a/modules/http/main.mods b/modules/http/main.mods
new file mode 100644
index 0000000..40832da
--- /dev/null
+++ b/modules/http/main.mods
@@ -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
\ No newline at end of file
diff --git a/modules/http/main.py b/modules/http/main.py
new file mode 100644
index 0000000..376f5e9
--- /dev/null
+++ b/modules/http/main.py
@@ -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
\ No newline at end of file
diff --git a/modules/http/settings.py b/modules/http/settings.py
new file mode 100644
index 0000000..4f83cea
--- /dev/null
+++ b/modules/http/settings.py
@@ -0,0 +1,6 @@
+global maxHeaderLength
+maxHeaderLength = 4096
+global indexPath
+indexPath = p(sp,"index")
+global readBufferSize
+readBufferSize = 32768
\ No newline at end of file
diff --git a/modules/main.mods b/modules/main.mods
new file mode 100644
index 0000000..e503eda
--- /dev/null
+++ b/modules/main.mods
@@ -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
\ No newline at end of file
diff --git a/modules/servers.py b/modules/servers.py
new file mode 100644
index 0000000..8b6103d
--- /dev/null
+++ b/modules/servers.py
@@ -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()
\ No newline at end of file
diff --git a/modules/settings.py b/modules/settings.py
new file mode 100644
index 0000000..a4910d8
--- /dev/null
+++ b/modules/settings.py
@@ -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
\ No newline at end of file