├── .gitignore
├── README.md
├── main.py
├── mvc
├── controllers
│ ├── routes.py
│ └── services
│ │ └── greet.py
├── models
│ └── inputModels.py
└── views
│ ├── static
│ ├── index.py
│ ├── inline.pyhtml
│ └── welcome.html
│ └── views.py
├── server
├── helper.py
├── internalModels.py
├── mimeTypes.py
├── misc.py
├── responseHandler.py
└── settings.py
└── tests
└── test_pyop_server.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | __pycache__
3 | temp.py
4 | __OLD__
5 | */.vscode
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python-On-Pails(PyOP)
2 | 
3 | ## You all have seen Ruby on Rails now get ready for python on pails.
4 | #### So what is Python on Pails?
5 | Q: Is it something like Ruby on Rails ,but in python?\
6 | Previous Answer : No Idea(Maybe). Never used Rails. All I can say is this program is a "framework wannabe".\
7 | Current Answer : I think so. Still haven't used Rails.
8 |
9 | # ⚠This is V2.0 (Still Under Delvelopment)⚠
10 | ## Please Checkout v1 branch for Stable Framework
11 |
12 |
13 | ## Our Philosphy
14 | Spend your time writing your business logic rather than writing server.
15 |
16 | ## Why PyOP?
17 | - No additional dependencies just Vanilla python3.
18 | - Python-On-Pails(PyOP) is just a simple "kind of a framework" programmed in python. It is cool and it also eliminates the need to install apache2, xampp ,wamp or lamp on systems that already have python(Python3 to be precise) installed.
19 | - PyOP can use python3 as the main server side language.
20 | - PyOP is like a ready-to-deploy framework. everything is already setup for you. You only need to write your business logic.
21 | - modifying behaviour of server does not require writing more code, simple changes to server/settings.py is enough.
22 | ## [Visit Official wiki for more Information](https://github.com/tg21/python-on-pails/wiki) (Incomplete)
23 |
24 | ## How is it different from version 1.
25 | - For starters, version 2 of PyOP will be based on MVC.
26 | - And Many more features which I will list once they are implemented.
27 | - Automatic type casting.
28 | - changed error responses to tackle information gathering attacks from softwares like nmap.
29 |
30 | ## TODOs
31 | Track TODOs, Inprogress and Completed Goals [here on project board](https://github.com/tg21/python-on-pails/projects/1)
32 |
33 | ### Something missing from TODOs?
34 | - join [discussions](https://github.com/tg21/python-on-pails/discussions/3).
35 | - Open Issues.
36 | - Make PRs.
37 |
38 | ## Installation
39 | PyOP requires [python3](https://www.python.org/) to run.
40 |
41 | ```sh
42 | $ cd python-on-pails
43 | $ sudo python3 main.py
44 | ```
45 |
46 | For production environments...
47 |
48 | ```sh
49 | do the same as above but at your own risk.
50 | ```
51 |
52 |
53 |
54 | ### Development
55 |
56 | Want to contribute? Great! \
57 | If you know how and want to contribute [contact](https://tg21.github.io) me.\
58 | Help is always welcome.
59 |
60 |
61 |
62 | License
63 | ----
64 |
65 | It has none.(You can fork it for personal use)
66 |
67 |
68 | **Free Software Open-Source Software, yeah enjoy!**
69 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | from http.server import BaseHTTPRequestHandler, HTTPServer
4 | import json
5 | dir_path = (str(os.path.dirname(os.path.realpath(__file__))) + "/")
6 | # sys.path.insert(0, '{}/server'.format(dir_path))
7 | from server.settings import config,server_info
8 | from server.responseHandler import ResponseHandler
9 | from server.misc import jsonify,dict2obj,parseJsonToClass
10 | from server.internalModels import Req,ResoponseClass,PyopReq
11 | # sys.path.insert(0, '{}/{}'.format(dir_path,config.views))
12 | # sys.path.insert(0, '{}/{}'.format(dir_path,config.controllers))
13 |
14 | from mvc.views.views import views
15 | from mvc.controllers.routes import getRoutes,postRoutes
16 | from http import HTTPStatus
17 | from socket import timeout
18 | from socketserver import ThreadingMixIn
19 |
20 |
21 | # from filehandler import response
22 | # from extraFunctions import initials
23 |
24 |
25 | # HTTPReuestHandler class
26 | # if(len(sys.argv) > 1 or True):
27 | # if(True or sys.argv[1] == "-g"): # g for global
28 | # dir_path = os.getcwd()
29 | # project_dir = ""
30 |
31 | def e(x):
32 | a = 3
33 |
34 |
35 | class PyOPSever(BaseHTTPRequestHandler):
36 |
37 | def FormatRequestData(self,model,types=None,argsCount=0):
38 | queryString = self.path.split("?")
39 | if(self.command == "GET"):
40 | if len(queryString) > 1:
41 | data = str(queryString[1])
42 | else:
43 | data = ""
44 | else:
45 | data = str(self.rfile.read(int(self.headers['Content-Length'])), 'utf-8')
46 |
47 | data = self.ParseRequestDataInToObject(data,model,types,argsCount)
48 | # data = dict2obj({
49 | # 'method':rtype,
50 | # 'data':data,
51 | # })
52 | return data
53 | # return Req(self.command,data)
54 |
55 |
56 | def ParseRequestDataInToObject(self,data,model,types=None,argsCount=0):
57 | if(type(data) == str and data!=""):
58 | try:
59 | data = json.loads(data)
60 | except Exception as e:
61 | print(e)
62 | data = jsonify(data)
63 | elif(type(data) is not dict):
64 | #TODO: Better Exception for unsupported format data
65 | raise Exception
66 | if model is not None:
67 | if types is None:
68 | # making object based on inputmodel key in route's dictionary
69 | data = parseJsonToClass(data,model)
70 | else:
71 | # making object based on parameters of controller function
72 | output = []
73 | if(argsCount == 1): # mapping one dict to one class object (most common case of POST APIs)
74 | tp = types.get(model[0],None)
75 | output.append(parseJsonToClass(data,tp))
76 | else:
77 | for i in range(argsCount): # mapping members in one dict to multiple function arguments (common case for GET APIs)
78 | tp = types.get(model[i],None)
79 | if tp is None:
80 | output.append(data[i])
81 | elif tp is PyopReq:
82 | output.append(self)
83 | else:
84 | output.append(parseJsonToClass(data[model[i]],tp))
85 | return output
86 | else:
87 | # data = jso
88 | data = dict2obj(data)
89 | return data
90 |
91 | def GETPOST(self): # rtype stands for request type
92 | # print("obj addr after int is :: ",id(self),"==>",int(str(id(self))))
93 | # print("Client= ",self.client_address)
94 | response:ResponseHandler = None
95 | queryString = self.path.split("?")
96 |
97 |
98 | #checking routes in views.py
99 | route = views.get(queryString[0], False)
100 | if route:
101 | if(callable(route)):
102 | response = ResponseHandler(route,'staticFunction',None).respond()
103 | elif(type(route) is str):
104 | request = dir_path+config.static+route
105 | response = ResponseHandler(request,'static',None).respond()
106 | else:
107 | #checking routes in routes.py(controllers)
108 | if(self.command == "GET"):
109 | route = getRoutes.get(queryString[0], False)
110 | else:
111 | route = postRoutes.get(queryString[0], False)
112 | if route:
113 | inputModel = None
114 | customResponse = None
115 | if(type(route) == dict):
116 | route,inputModel,customResponse = route.get('action'),route.get('input',None),route.get('customResponse',None)
117 | if(callable(route)):
118 | if inputModel is None:
119 | params = route.__code__.co_varnames
120 | types = route.__annotations__
121 | argsCount = route.__code__.co_argcount
122 | response = ResponseHandler(route,'controllerFunction',self.FormatRequestData(params,types,argsCount),customResponse,unpack=True).respond()
123 | else:
124 | response = ResponseHandler(route,'controllerFunction',self.FormatRequestData(inputModel),customResponse,unpack=False).respond()
125 | elif(type(route) == str and route.endswith('.py')):
126 | request = dir_path+config.controllers+route
127 | response = ResponseHandler(request,'controllerFile',self.FormatRequestData(inputModel),customResponse).respond()
128 | else:
129 | #TODO:better exception here
130 | raise Exception
131 | else:
132 | request = dir_path+config.static+queryString[0]
133 | response = ResponseHandler(request,'static',None).respond()
134 |
135 | self.send_custom_response(response.content,response.responseCode,response.mimeType)
136 |
137 |
138 | def send_custom_response(self,content:bytes,responseCode=200,mimeType='text/html'):
139 | self.log_request(responseCode)
140 | self.send_response_only(responseCode)
141 | self.send_header('date',self.date_time_string())
142 | self.send_header('content-type', mimeType)
143 | self.send_header('server',server_info.server_name + ' '+ server_info.server_version)
144 | self.end_headers()
145 | if(type(content) is not bytes):
146 | if(type(content) is not str):
147 | content = bytes(str(content), 'utf-8')
148 | else:
149 | content = bytes(content, 'utf-8')
150 | self.wfile.write(content)
151 |
152 | def handle_one_request(self):
153 | """Handle a single HTTP request.
154 | Overridden from base class
155 |
156 | """
157 | try:
158 | self.raw_requestline = self.rfile.readline(65537)
159 | if len(self.raw_requestline) > 65536:
160 | self.requestline = ''
161 | self.request_version = ''
162 | self.command = ''
163 | self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
164 | return
165 | if not self.raw_requestline:
166 | self.close_connection = True
167 | return
168 | if not self.parse_request():
169 | # An error code has been sent, just exit
170 | return
171 | mname = 'do_' + self.command
172 | if not hasattr(self, mname):
173 | if(config.fool_nmap):
174 | self.send_custom_response(config.fool_nmap_content,200,'application/json')
175 | else:
176 | self.send_error(
177 | HTTPStatus.NOT_IMPLEMENTED,
178 | "Unsupported method (%r)" % self.command)
179 | return
180 | method = getattr(self, mname)
181 | method()
182 | self.wfile.flush() #actually send the response if not already done.
183 | except timeout as e:
184 | #a read or a write timed out. Discard this connection
185 | self.log_error("Request timed out: %r", e)
186 | self.close_connection = True
187 | return
188 |
189 | # GET
190 | def do_GET(self):
191 | self.GETPOST()
192 | return
193 |
194 | def do_POST(self):
195 | self.GETPOST()
196 | return
197 |
198 | #making seperate class and not using one in http libary to support backward compatiblity with python versions
199 | class ThreadedPyOPServer(ThreadingMixIn, HTTPServer):
200 | daemon_threads = True
201 |
202 | def run():
203 | print("Starting server ...")
204 | server_address = (config.host, config.port)
205 | # httpd = HTTPServer(server_address, PyOPSever)
206 | httpd = ThreadedPyOPServer(server_address, PyOPSever)
207 | print("running server at ", server_address)
208 | print("WD :- ", dir_path + config.static)
209 | if config.enableTLS:
210 | from ssl import wrap_socket
211 | httpd.socket = wrap_socket (httpd.socket,
212 | keyfile= config.PATH_TLS_KEY_FILE,
213 | certfile= config.PATH_TLS_CERT_FILE, server_side=True)
214 | print("Listening Securely")
215 | httpd.serve_forever()
216 |
217 | if __name__ == '__main__':
218 | run()
219 |
--------------------------------------------------------------------------------
/mvc/controllers/routes.py:
--------------------------------------------------------------------------------
1 | """
2 | ===================================================================================================================================
3 | Specify routes in this file to handle API calls.
4 | **NOTE: if views.py in views folder has a same route then that will be given preference **
5 | give python file or python functions for their corresponding routes.
6 | python files must defin main(req) method
7 | every function or python file should accept atleast one parameter(req object)
8 | results obatined from these files will be serialized and retured as application/json unless customResponse in toggled for a route
9 | ***********************************************************************************************************************************
10 | remove this placeholder routes and make your own
11 | ===================================================================================================================================
12 | """
13 | import mvc.models.inputModels as md
14 | from server.internalModels import PyopReq
15 | # how files are executed
16 | # x = """
17 | # def main(n):
18 | # return "hello {}".format(n)
19 | # """
20 |
21 | # exec(x)
22 | # print(main("batman"))
23 | # class dat:
24 | # name = 'batman'
25 | # class req:
26 | # data = dat
27 | # from services.greet import main
28 |
29 | def sumNum(req):
30 | # data = req.data
31 | a, b = req.a, req.b
32 | return a+b
33 |
34 | def sumNum1(a: int, b: int):
35 | return a+b
36 |
37 | def sumCustom(req:md.productInputModel):
38 | #data = req.data
39 | res = req.a + req.b + req.c + sum(req.d)
40 | res = "
{}".format(res)
41 | return {
42 | 'content' : res,
43 | 'mimeType' : 'text/html',
44 | 'code': 200,
45 | }
46 |
47 | def typeCastedProduct(a:int,b:int,c:str,d:[int]):
48 | return(c + " :-> " + str((a*b)/sum(d)))
49 |
50 | def typeCastedProductFullControll(a:int,b:int,c:str,d:[int],req:PyopReq):
51 | client = str(req.client_address) # getting url
52 | content = "responding to request: "+ client + " \n " + c + " :-> " + str((a*b)/sum(d))
53 | # req.send_custom_response(content) # using default code(200) and mimeType(text/html)
54 | # req.send_custom_response(content,200) # using default mimeType
55 | req.send_custom_response(content,200,'text/html') #sepecifying all parameters
56 |
57 | req.close_connection = True
58 |
59 |
60 | postRoutes = {
61 | '/sum': {'action':sumNum,'input':md.sumInputModel},
62 | '/sumCustom': {'action':sumCustom,'customResponse': True},
63 | '/product': typeCastedProductFullControll,
64 | '/greet': {'action':'services/greet.py','input':md.UserDetails,'customResponse':{'mimeType':'text/html'}},
65 |
66 | }
67 | getRoutes = {
68 | '/sum': sumNum1,
69 | '/sumCustom': {'action':sumCustom,'input':md.sumInputModel,'customResponse': True},
70 | '/product': typeCastedProduct,
71 | '/greet': 'services/greet.py',
72 |
73 | }
74 |
75 |
76 | #print(services.greet.main(req))
77 |
--------------------------------------------------------------------------------
/mvc/controllers/services/greet.py:
--------------------------------------------------------------------------------
1 | # import os
2 | # import sys
3 | # c = sys.path
4 | # b = os.getcwd()
5 | # print(c)
6 | # print(b)
7 | # import mvc.models.inputModels as md
8 | from server.helper import getRequestData
9 | data = getRequestData()
10 | def main():
11 | print(
12 | """
13 |
14 |
15 |
16 | PyOP
17 |
18 |
19 |
20 |
21 | Hello There
22 | {}
23 | Age : {}
24 |
25 |
26 | """.format(data.name,data.age)
27 | )
28 |
29 | main()
--------------------------------------------------------------------------------
/mvc/models/inputModels.py:
--------------------------------------------------------------------------------
1 | def square(input):
2 | return int(input)*int(input)
3 | class sumInputModel:
4 | a = int
5 | b = int
6 | c = int
7 | d = square
8 |
9 | class productInputModel:
10 | a = int
11 | b = int
12 | c = int
13 | d = [int]
14 |
15 | class UserDetails:
16 | name = str
17 | age = int
18 |
--------------------------------------------------------------------------------
/mvc/views/static/index.py:
--------------------------------------------------------------------------------
1 | def main():
2 | return"""
3 |
4 |
5 |
6 | Index : PyOP
7 |
8 |
9 | This file is index.py
10 | This is Python-on-pails server
11 | rename this file to see direcory content at startup instead of this file
12 |
{}
13 |
14 |
15 | """.format(__file__)
16 | main()
--------------------------------------------------------------------------------
/mvc/views/static/inline.pyhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sum of Two Numbers
4 |
17 |
18 |
--------------------------------------------------------------------------------
/mvc/views/static/welcome.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PyOP
6 |
7 |
8 |
9 |
10 | Hello There
11 | This is Python On Pails
12 | Sum of Two Numbers
13 |
18 |
19 | Sum of 3 Numbers with square of 4th
20 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/mvc/views/views.py:
--------------------------------------------------------------------------------
1 | """
2 | =======================================================================
3 | Specify view routes in this file.
4 | give path to html file, python file or python functions.
5 | no parameters will be passed to python files or functions.(use routes.py in controllers folder if you need to do so)
6 | results obatined from these files will be retured as text/html
7 | ************************************************************************
8 | remove this placeholder routes and make your own
9 | =======================================================================
10 | """
11 | def properResponseToHelloThere():
12 | return "General Kenobi !"
13 | views = {
14 | '/helloThere': properResponseToHelloThere,
15 | '/hello': 'welcome.html',
16 | '/': 'welcome.html',#this url is relative,
17 | '/inline':'inline.pyhtml',
18 | }
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/server/helper.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import pickle
3 |
4 | def getRequestData():
5 | if(len(sys.argv) > 1):
6 | return pickle.loads(eval(sys.argv[1]))
7 | else:
8 | return None
--------------------------------------------------------------------------------
/server/internalModels.py:
--------------------------------------------------------------------------------
1 | from http.server import BaseHTTPRequestHandler
2 | class ResoponseClass:
3 | def __init__(self,response,responseCode,mimeType):
4 | self.content = response
5 | self.responseCode = responseCode
6 | self.mimeType = mimeType
7 |
8 | class Req:
9 | def __init__(self,method,data):
10 | self.method = method
11 | self.data = data
12 |
13 | class PyopReq(BaseHTTPRequestHandler):
14 | """Abstract class for usage as argument type in routes"""
15 |
16 | def FormatRequestData(self,model,types=None,argsCount=0):
17 | pass
18 |
19 | def ParseRequestDataInToObject(self,data,model,types=None,argsCount=0):
20 | pass
21 |
22 | def GETPOST(self):
23 | pass
24 |
25 | def send_custom_response(self,response:ResoponseClass):
26 | pass
27 |
28 | def handle_one_request(self):
29 | """Handle a single HTTP request.
30 | Overridden from base class
31 | """
32 | pass
33 |
34 | # GET
35 | def do_GET(self):
36 | pass
37 |
38 | def do_POST(self):
39 | pass
--------------------------------------------------------------------------------
/server/mimeTypes.py:
--------------------------------------------------------------------------------
1 | mimeTypes = {".x3d": "application/vnd.hzn-3d-crossword", ".3gp": "video/3gpp", ".3g2": "video/3gpp2", ".mseq": "application/vnd.mseq", ".pwn": "application/vnd.3m.post-it-notes", ".plb": "application/vnd.3gpp.pic-bw-large", ".psb": "application/vnd.3gpp.pic-bw-small", ".pvb": "application/vnd.3gpp.pic-bw-var", ".tcap": "application/vnd.3gpp2.tcap", ".7z": "application/x-7z-compressed", ".abw": "application/x-abiword", ".ace": "application/x-ace-compressed", ".acc": "application/vnd.americandynamics.acc", ".acu": "application/vnd.acucobol", ".atc": "application/vnd.acucorp", ".adp": "audio/adpcm", ".aab": "application/x-authorware-bin", ".aam": "application/x-authorware-map", ".aas": "application/x-authorware-seg", ".air": "application/vnd.adobe.air-application-installer-package+zip", ".swf": "application/x-shockwave-flash", ".fxp": "application/vnd.adobe.fxp", ".pdf": "application/pdf", ".ppd": "application/vnd.cups-ppd", ".dir": "application/x-director", ".xdp": "application/vnd.adobe.xdp+xml", ".xfdf": "application/vnd.adobe.xfdf", ".aac": "audio/x-aac", ".ahead": "application/vnd.ahead.space", ".azf": "application/vnd.airzip.filesecure.azf", ".azs": "application/vnd.airzip.filesecure.azs", ".azw": "application/vnd.amazon.ebook", ".ami": "application/vnd.amiga.ami", "N/A": "application/andrew-inset", ".apk": "application/vnd.android.package-archive", ".cii": "application/vnd.anser-web-certificate-issue-initiation", ".fti": "application/vnd.anser-web-funds-transfer-initiation", ".atx": "application/vnd.antix.game-component", ".dmg": "application/x-apple-diskimage", ".mpkg": "application/vnd.apple.installer+xml", ".aw": "application/applixware", ".les": "application/vnd.hhe.lesson-player", ".swi": "application/vnd.aristanetworks.swi", ".s": "text/x-asm", ".atomcat": "application/atomcat+xml", ".atomsvc": "application/atomsvc+xml", ".atom, .xml": "application/atom+xml", ".ac": "application/pkix-attr-cert", ".aif": "audio/x-aiff", ".avi": "video/x-msvideo", ".aep": "application/vnd.audiograph", ".dxf": "image/vnd.dxf", ".dwf": "model/vnd.dwf", ".par": "text/plain-bas", ".bcpio": "application/x-bcpio", ".bin": "application/octet-stream", ".bmp": "image/bmp", ".torrent": "application/x-bittorrent", ".cod": "application/vnd.rim.cod", ".mpm": "application/vnd.blueice.multipass", ".bmi": "application/vnd.bmi", ".sh": "application/x-sh", ".btif": "image/prs.btif", ".rep": "application/vnd.businessobjects", ".bz": "application/x-bzip", ".bz2": "application/x-bzip2", ".csh": "application/x-csh", ".c": "text/x-c", ".cdxml": "application/vnd.chemdraw+xml", ".css": "text/css", ".cdx": "chemical/x-cdx", ".cml": "chemical/x-cml", ".csml": "chemical/x-csml", ".cdbcmsg": "application/vnd.contact.cmsg", ".cla": "application/vnd.claymore", ".c4g": "application/vnd.clonk.c4group", ".sub": "image/vnd.dvb.subtitle", ".cdmia": "application/cdmi-capability", ".cdmic": "application/cdmi-container", ".cdmid": "application/cdmi-domain", ".cdmio": "application/cdmi-object", ".cdmiq": "application/cdmi-queue", ".c11amc": "application/vnd.cluetrust.cartomobile-config", ".c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", ".ras": "image/x-cmu-raster", ".dae": "model/vnd.collada+xml", ".csv": "text/csv", ".cpt": "application/mac-compactpro", ".wmlc": "application/vnd.wap.wmlc", ".cgm": "image/cgm", ".ice": "x-conference/x-cooltalk", ".cmx": "image/x-cmx", ".xar": "application/vnd.xara", ".cmc": "application/vnd.cosmocaller", ".cpio": "application/x-cpio", ".clkx": "application/vnd.crick.clicker", ".clkk": "application/vnd.crick.clicker.keyboard", ".clkp": "application/vnd.crick.clicker.palette", ".clkt": "application/vnd.crick.clicker.template", ".clkw": "application/vnd.crick.clicker.wordbank", ".wbs": "application/vnd.criticaltools.wbs+xml", ".cryptonote": "application/vnd.rig.cryptonote", ".cif": "chemical/x-cif", ".cmdf": "chemical/x-cmdf", ".cu": "application/cu-seeme", ".cww": "application/prs.cww", ".curl": "text/vnd.curl", ".dcurl": "text/vnd.curl.dcurl", ".mcurl": "text/vnd.curl.mcurl", ".scurl": "text/vnd.curl.scurl", ".car": "application/vnd.curl.car", ".pcurl": "application/vnd.curl.pcurl", ".cmp": "application/vnd.yellowriver-custom-menu", ".dssc": "application/dssc+der", ".xdssc": "application/dssc+xml", ".deb": "application/x-debian-package", ".uva": "audio/vnd.dece.audio", ".uvi": "image/vnd.dece.graphic", ".uvh": "video/vnd.dece.hd", ".uvm": "video/vnd.dece.mobile", ".uvu": "video/vnd.uvvu.mp4", ".uvp": "video/vnd.dece.pd", ".uvs": "video/vnd.dece.sd", ".uvv": "video/vnd.dece.video", ".dvi": "application/x-dvi", ".seed": "application/vnd.fdsn.seed", ".dtb": "application/x-dtbook+xml", ".res": "application/x-dtbresource+xml", ".ait": "application/vnd.dvb.ait", ".svc": "application/vnd.dvb.service", ".eol": "audio/vnd.digital-winds", ".djvu": "image/vnd.djvu", ".dtd": "application/xml-dtd", ".mlp": "application/vnd.dolby.mlp", ".wad": "application/x-doom", ".dpg": "application/vnd.dpgraph", ".dra": "audio/vnd.dra", ".dfac": "application/vnd.dreamfactory", ".dts": "audio/vnd.dts", ".dtshd": "audio/vnd.dts.hd", ".dwg": "image/vnd.dwg", ".geo": "application/vnd.dynageo", ".es": "application/ecmascript", ".mag": "application/vnd.ecowin.chart", ".mmr": "image/vnd.fujixerox.edmics-mmr", ".rlc": "image/vnd.fujixerox.edmics-rlc", ".exi": "application/exi", ".mgz": "application/vnd.proteus.magazine", ".epub": "application/epub+zip", ".eml": "message/rfc822", ".nml": "application/vnd.enliven", ".xpr": "application/vnd.is-xpr", ".xif": "image/vnd.xiff", ".xfdl": "application/vnd.xfdl", ".emma": "application/emma+xml", ".ez2": "application/vnd.ezpix-album", ".ez3": "application/vnd.ezpix-package", ".fst": "image/vnd.fst", ".fvt": "video/vnd.fvt", ".fbs": "image/vnd.fastbidsheet", ".fe_launch": "application/vnd.denovo.fcselayout-link", ".f4v": "video/x-f4v", ".flv": "video/x-flv", ".fpx": "image/vnd.fpx", ".npx": "image/vnd.net-fpx", ".flx": "text/vnd.fmi.flexstor", ".fli": "video/x-fli", ".ftc": "application/vnd.fluxtime.clip", ".fdf": "application/vnd.fdf", ".f": "text/x-fortran", ".mif": "application/vnd.mif", ".fm": "application/vnd.framemaker", ".fh": "image/x-freehand", ".fsc": "application/vnd.fsc.weblaunch", ".fnc": "application/vnd.frogans.fnc", ".ltf": "application/vnd.frogans.ltf", ".ddd": "application/vnd.fujixerox.ddd", ".xdw": "application/vnd.fujixerox.docuworks", ".xbd": "application/vnd.fujixerox.docuworks.binder", ".oas": "application/vnd.fujitsu.oasys", ".oa2": "application/vnd.fujitsu.oasys2", ".oa3": "application/vnd.fujitsu.oasys3", ".fg5": "application/vnd.fujitsu.oasysgp", ".bh2": "application/vnd.fujitsu.oasysprs", ".spl": "application/x-futuresplash", ".fzs": "application/vnd.fuzzysheet", ".g3": "image/g3fax", ".gmx": "application/vnd.gmx", ".gtw": "model/vnd.gtw", ".txd": "application/vnd.genomatix.tuxedo", ".ggb": "application/vnd.geogebra.file", ".ggt": "application/vnd.geogebra.tool", ".gdl": "model/vnd.gdl", ".gex": "application/vnd.geometry-explorer", ".gxt": "application/vnd.geonext", ".g2w": "application/vnd.geoplan", ".g3w": "application/vnd.geospace", ".gsf": "application/x-font-ghostscript", ".bdf": "application/x-font-bdf", ".gtar": "application/x-gtar", ".texinfo": "application/x-texinfo", ".gnumeric": "application/x-gnumeric", ".kml": "application/vnd.google-earth.kml+xml", ".kmz": "application/vnd.google-earth.kmz", ".gqf": "application/vnd.grafeq", ".gif": "image/gif", ".gv": "text/vnd.graphviz", ".gac": "application/vnd.groove-account", ".ghf": "application/vnd.groove-help", ".gim": "application/vnd.groove-identity-message", ".grv": "application/vnd.groove-injector", ".gtm": "application/vnd.groove-tool-message", ".tpl": "application/vnd.groove-tool-template", ".vcg": "application/vnd.groove-vcard", ".h261": "video/h261", ".h263": "video/h263", ".h264": "video/h264", ".hpid": "application/vnd.hp-hpid", ".hps": "application/vnd.hp-hps", ".hdf": "application/x-hdf", ".rip": "audio/vnd.rip", ".hbci": "application/vnd.hbci", ".jlt": "application/vnd.hp-jlyt", ".pcl": "application/vnd.hp-pcl", ".hpgl": "application/vnd.hp-hpgl", ".hvs": "application/vnd.yamaha.hv-script", ".hvd": "application/vnd.yamaha.hv-dic", ".hvp": "application/vnd.yamaha.hv-voice", ".sfd-hdstx": "application/vnd.hydrostatix.sof-data", ".stk": "application/hyperstudio", ".hal": "application/vnd.hal+xml", ".html": "text/html", ".irm": "application/vnd.ibm.rights-management", ".sc": "application/vnd.ibm.secure-container", ".ics": "text/calendar", ".icc": "application/vnd.iccprofile", ".ico": "image/x-icon", ".igl": "application/vnd.igloader", ".ief": "image/ief", ".ivp": "application/vnd.immervision-ivp", ".ivu": "application/vnd.immervision-ivu", ".rif": "application/reginfo+xml", ".3dml": "text/vnd.in3d.3dml", ".spot": "text/vnd.in3d.spot", ".igs": "model/iges", ".i2g": "application/vnd.intergeo", ".cdy": "application/vnd.cinderella", ".xpw": "application/vnd.intercon.formnet", ".fcs": "application/vnd.isac.fcs", ".ipfix": "application/ipfix", ".cer": "application/pkix-cert", ".pki": "application/pkixcmp", ".crl": "application/pkix-crl", ".pkipath": "application/pkix-pkipath", ".igm": "application/vnd.insors.igm", ".rcprofile": "application/vnd.ipunplugged.rcprofile", ".irp": "application/vnd.irepository.package+xml", ".jad": "text/vnd.sun.j2me.app-descriptor", ".jar": "application/java-archive", ".class": "application/java-vm", ".jnlp": "application/x-java-jnlp-file", ".ser": "application/java-serialized-object", ".java": "text/x-java-source,java", ".js": "application/javascript", ".json": "application/json", ".joda": "application/vnd.joost.joda-archive", ".jpm": "video/jpm", ".jpeg": "image/jpeg", ".jpg": "image/jpeg", ".pjpeg": "image/pjpeg", ".jpgv": "video/jpeg", ".ktz": "application/vnd.kahootz", ".mmd": "application/vnd.chipnuts.karaoke-mmd", ".karbon": "application/vnd.kde.karbon", ".chrt": "application/vnd.kde.kchart", ".kfo": "application/vnd.kde.kformula", ".flw": "application/vnd.kde.kivio", ".kon": "application/vnd.kde.kontour", ".kpr": "application/vnd.kde.kpresenter", ".ksp": "application/vnd.kde.kspread", ".kwd": "application/vnd.kde.kword", ".htke": "application/vnd.kenameaapp", ".kia": "application/vnd.kidspiration", ".kne": "application/vnd.kinar", ".sse": "application/vnd.kodak-descriptor", ".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", ".jam": "application/vnd.jam", "0.123": "application/vnd.lotus-1-2-3", ".apr": "application/vnd.lotus-approach", ".pre": "application/vnd.lotus-freelance", ".nsf": "application/vnd.lotus-notes", ".org": "application/vnd.lotus-organizer", ".scm": "application/vnd.lotus-screencam", ".lwp": "application/vnd.lotus-wordpro", ".lvp": "audio/vnd.lucent.voice", ".m3u": "audio/x-mpegurl", ".m4v": "video/x-m4v", ".hqx": "application/mac-binhex40", ".portpkg": "application/vnd.macports.portpkg", ".mgp": "application/vnd.osgeo.mapguide.package", ".mrc": "application/marc", ".mrcx": "application/marcxml+xml", ".mxf": "application/mxf", ".nbp": "application/vnd.wolfram.player", ".ma": "application/mathematica", ".mathml": "application/mathml+xml", ".mbox": "application/mbox", ".mc1": "application/vnd.medcalcdata", ".mscml": "application/mediaservercontrol+xml", ".cdkey": "application/vnd.mediastation.cdkey", ".mwf": "application/vnd.mfer", ".mfm": "application/vnd.mfmp", ".msh": "model/mesh", ".mads": "application/mads+xml", ".mets": "application/mets+xml", ".mods": "application/mods+xml", ".meta4": "application/metalink4+xml", ".mcd": "application/vnd.mcd", ".flo": "application/vnd.micrografx.flo", ".igx": "application/vnd.micrografx.igx", ".es3": "application/vnd.eszigno3+xml", ".mdb": "application/x-msaccess", ".asf": "video/x-ms-asf", ".exe": "application/x-msdownload", ".cil": "application/vnd.ms-artgalry", ".cab": "application/vnd.ms-cab-compressed", ".ims": "application/vnd.ms-ims", ".application": "application/x-ms-application", ".clp": "application/x-msclip", ".mdi": "image/vnd.ms-modi", ".eot": "application/vnd.ms-fontobject", ".xls": "application/vnd.ms-excel", ".xlam": "application/vnd.ms-excel.addin.macroenabled.12", ".xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", ".xltm": "application/vnd.ms-excel.template.macroenabled.12", ".xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", ".chm": "application/vnd.ms-htmlhelp", ".crd": "application/x-mscardfile", ".lrm": "application/vnd.ms-lrm", ".mvb": "application/x-msmediaview", ".mny": "application/x-msmoney", ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", ".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", ".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
2 | ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", ".obd": "application/x-msbinder", ".thmx": "application/vnd.ms-officetheme", ".onetoc": "application/onenote", ".pya": "audio/vnd.ms-playready.media.pya", ".pyv": "video/vnd.ms-playready.media.pyv", ".ppt": "application/vnd.ms-powerpoint", ".ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", ".sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", ".pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", ".ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", ".potm": "application/vnd.ms-powerpoint.template.macroenabled.12", ".mpp": "application/vnd.ms-project", ".pub": "application/x-mspublisher", ".scd": "application/x-msschedule", ".xap": "application/x-silverlight-app", ".stl": "application/vnd.ms-pki.stl", ".cat": "application/vnd.ms-pki.seccat", ".vsd": "application/vnd.visio", ".vsdx": "application/vnd.visio2013", ".wm": "video/x-ms-wm", ".wma": "audio/x-ms-wma", ".wax": "audio/x-ms-wax", ".wmx": "video/x-ms-wmx", ".wmd": "application/x-ms-wmd", ".wpl": "application/vnd.ms-wpl", ".wmz": "application/x-ms-wmz", ".wmv": "video/x-ms-wmv", ".wvx": "video/x-ms-wvx", ".wmf": "application/x-msmetafile", ".trm": "application/x-msterminal", ".doc": "application/msword", ".docm": "application/vnd.ms-word.document.macroenabled.12", ".dotm": "application/vnd.ms-word.template.macroenabled.12", ".wri": "application/x-mswrite", ".wps": "application/vnd.ms-works", ".xbap": "application/x-ms-xbap", ".xps": "application/vnd.ms-xpsdocument", ".mid": "audio/midi", ".mpy": "application/vnd.ibm.minipay", ".afp": "application/vnd.ibm.modcap", ".rms": "application/vnd.jcp.javame.midlet-rms", ".tmo": "application/vnd.tmobile-livetv", ".prc": "application/x-mobipocket-ebook", ".mbk": "application/vnd.mobius.mbk", ".dis": "application/vnd.mobius.dis", ".plc": "application/vnd.mobius.plc", ".mqy": "application/vnd.mobius.mqy", ".msl": "application/vnd.mobius.msl", ".txf": "application/vnd.mobius.txf", ".daf": "application/vnd.mobius.daf", ".fly": "text/vnd.fly", ".mpc": "application/vnd.mophun.certificate", ".mpn": "application/vnd.mophun.application", ".mj2": "video/mj2", ".mpga": "audio/mpeg", ".mxu": "video/vnd.mpegurl", ".mpeg": "video/mpeg", ".m21": "application/mp21", ".mp4": "video/mp4", ".m3u8": "application/vnd.apple.mpegurl", ".mus": "application/vnd.musician", ".msty": "application/vnd.muvee.style", ".mxml": "application/xv+xml", ".ngdat": "application/vnd.nokia.n-gage.data", ".n-gage": "application/vnd.nokia.n-gage.symbian.install", ".ncx": "application/x-dtbncx+xml", ".nc": "application/x-netcdf", ".nlu": "application/vnd.neurolanguage.nlu", ".dna": "application/vnd.dna", ".nnd": "application/vnd.noblenet-directory", ".nns": "application/vnd.noblenet-sealer", ".nnw": "application/vnd.noblenet-web", ".rpst": "application/vnd.nokia.radio-preset", ".rpss": "application/vnd.nokia.radio-presets", ".n3": "text/n3", ".edm": "application/vnd.novadigm.edm", ".edx": "application/vnd.novadigm.edx", ".ext": "application/vnd.novadigm.ext", ".gph": "application/vnd.flographit", ".ecelp4800": "audio/vnd.nuera.ecelp4800", ".ecelp7470": "audio/vnd.nuera.ecelp7470", ".ecelp9600": "audio/vnd.nuera.ecelp9600", ".oda": "application/oda", ".ogx": "application/ogg", ".oga": "audio/ogg", ".ogv": "video/ogg", ".dd2": "application/vnd.oma.dd2+xml", ".oth": "application/vnd.oasis.opendocument.text-web", ".opf": "application/oebps-package+xml", ".qbo": "application/vnd.intu.qbo", ".oxt": "application/vnd.openofficeorg.extension", ".osf": "application/vnd.yamaha.openscoreformat", ".weba": "audio/webm", ".webm": "video/webm", ".odc": "application/vnd.oasis.opendocument.chart", ".otc": "application/vnd.oasis.opendocument.chart-template", ".odb": "application/vnd.oasis.opendocument.database", ".odf": "application/vnd.oasis.opendocument.formula", ".odft": "application/vnd.oasis.opendocument.formula-template", ".odg": "application/vnd.oasis.opendocument.graphics", ".otg": "application/vnd.oasis.opendocument.graphics-template", ".odi": "application/vnd.oasis.opendocument.image", ".oti": "application/vnd.oasis.opendocument.image-template", ".odp": "application/vnd.oasis.opendocument.presentation", ".otp": "application/vnd.oasis.opendocument.presentation-template", ".ods": "application/vnd.oasis.opendocument.spreadsheet", ".ots": "application/vnd.oasis.opendocument.spreadsheet-template", ".odt": "application/vnd.oasis.opendocument.text", ".odm": "application/vnd.oasis.opendocument.text-master", ".ott": "application/vnd.oasis.opendocument.text-template", ".ktx": "image/ktx", ".sxc": "application/vnd.sun.xml.calc", ".stc": "application/vnd.sun.xml.calc.template", ".sxd": "application/vnd.sun.xml.draw", ".std": "application/vnd.sun.xml.draw.template", ".sxi": "application/vnd.sun.xml.impress", ".sti": "application/vnd.sun.xml.impress.template", ".sxm": "application/vnd.sun.xml.math", ".sxw": "application/vnd.sun.xml.writer", ".sxg": "application/vnd.sun.xml.writer.global", ".stw": "application/vnd.sun.xml.writer.template", ".otf": "application/x-font-otf", ".osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", ".dp": "application/vnd.osgi.dp", ".pdb": "application/vnd.palm", ".p": "text/x-pascal", ".paw": "application/vnd.pawaafile", ".pclxl": "application/vnd.hp-pclxl", ".efif": "application/vnd.picsel", ".pcx": "image/x-pcx", ".psd": "image/vnd.adobe.photoshop", ".prf": "application/pics-rules", ".pic": "image/x-pict", ".chat": "application/x-chat", ".p10": "application/pkcs10", ".p12": "application/x-pkcs12", ".p7m": "application/pkcs7-mime", ".p7s": "application/pkcs7-signature", ".p7r": "application/x-pkcs7-certreqresp", ".p7b": "application/x-pkcs7-certificates", ".p8": "application/pkcs8", ".plf": "application/vnd.pocketlearn", ".pnm": "image/x-portable-anymap", ".pbm": "image/x-portable-bitmap", ".pcf": "application/x-font-pcf", ".pfr": "application/font-tdpfr", ".pgn": "application/x-chess-pgn", ".pgm": "image/x-portable-graymap", ".png": "image/png", ".ppm": "image/x-portable-pixmap", ".pskcxml": "application/pskc+xml", ".pml": "application/vnd.ctc-posml", ".ai": "application/postscript", ".pfa": "application/x-font-type1", ".pbd": "application/vnd.powerbuilder6", ".pgp": "application/pgp-encrypted", ".pgp": "application/pgp-signature", ".box": "application/vnd.previewsystems.box", ".ptid": "application/vnd.pvi.ptid1", ".pls": "application/pls+xml", ".str": "application/vnd.pg.format", ".ei6": "application/vnd.pg.osasli", ".dsc": "text/prs.lines.tag", ".psf": "application/x-font-linux-psf", ".qps": "application/vnd.publishare-delta-tree", ".wg": "application/vnd.pmi.widget", ".qxd": "application/vnd.quark.quarkxpress", ".esf": "application/vnd.epson.esf", ".msf": "application/vnd.epson.msf", ".ssf": "application/vnd.epson.ssf", ".qam": "application/vnd.epson.quickanime", ".qfx": "application/vnd.intu.qfx", ".qt": "video/quicktime", ".rar": "application/x-rar-compressed", ".ram": "audio/x-pn-realaudio", ".rmp": "audio/x-pn-realaudio-plugin", ".rsd": "application/rsd+xml", ".rm": "application/vnd.rn-realmedia", ".bed": "application/vnd.realvnc.bed", ".mxl": "application/vnd.recordare.musicxml", ".musicxml": "application/vnd.recordare.musicxml+xml", ".rnc": "application/relax-ng-compact-syntax", ".rdz": "application/vnd.data-vision.rdz", ".rdf": "application/rdf+xml", ".rp9": "application/vnd.cloanto.rp9", ".jisp": "application/vnd.jisp", ".rtf": "application/rtf", ".rtx": "text/richtext", ".link66": "application/vnd.route66.link66+xml", ".rss, .xml": "application/rss+xml", ".shf": "application/shf+xml", ".st": "application/vnd.sailingtracker.track", ".svg": "image/svg+xml", ".sus": "application/vnd.sus-calendar", ".sru": "application/sru+xml", ".setpay": "application/set-payment-initiation", ".setreg": "application/set-registration-initiation", ".sema": "application/vnd.sema", ".semd": "application/vnd.semd", ".semf": "application/vnd.semf", ".see": "application/vnd.seemail", ".snf": "application/x-font-snf", ".spq": "application/scvp-vp-request", ".spp": "application/scvp-vp-response", ".scq": "application/scvp-cv-request", ".scs": "application/scvp-cv-response", ".sdp": "application/sdp", ".etx": "text/x-setext", ".movie": "video/x-sgi-movie", ".ifm": "application/vnd.shana.informed.formdata", ".itp": "application/vnd.shana.informed.formtemplate", ".iif": "application/vnd.shana.informed.interchange", ".ipk": "application/vnd.shana.informed.package", ".tfi": "application/thraud+xml", ".shar": "application/x-shar", ".rgb": "image/x-rgb", ".slt": "application/vnd.epson.salt", ".aso": "application/vnd.accpac.simply.aso", ".imp": "application/vnd.accpac.simply.imp", ".twd": "application/vnd.simtech-mindmapper", ".csp": "application/vnd.commonspace", ".saf": "application/vnd.yamaha.smaf-audio", ".mmf": "application/vnd.smaf", ".spf": "application/vnd.yamaha.smaf-phrase", ".teacher": "application/vnd.smart.teacher", ".svd": "application/vnd.svd", ".rq": "application/sparql-query", ".srx": "application/sparql-results+xml", ".gram": "application/srgs", ".grxml": "application/srgs+xml", ".ssml": "application/ssml+xml", ".skp": "application/vnd.koan", ".sgml": "text/sgml", ".sdc": "application/vnd.stardivision.calc", ".sda": "application/vnd.stardivision.draw", ".sdd": "application/vnd.stardivision.impress", ".smf": "application/vnd.stardivision.math", ".sdw": "application/vnd.stardivision.writer", ".sgl": "application/vnd.stardivision.writer-global", ".sm": "application/vnd.stepmania.stepchart", ".sit": "application/x-stuffit", ".sitx": "application/x-stuffitx", ".sdkm": "application/vnd.solent.sdkm+xml", ".xo": "application/vnd.olpc-sugar", ".au": "audio/basic", ".wqd": "application/vnd.wqd", ".sis": "application/vnd.symbian.install", ".smi": "application/smil+xml", ".xsm": "application/vnd.syncml+xml", ".bdm": "application/vnd.syncml.dm+wbxml", ".xdm": "application/vnd.syncml.dm+xml", ".sv4cpio": "application/x-sv4cpio", ".sv4crc": "application/x-sv4crc", ".sbml": "application/sbml+xml", ".tsv": "text/tab-separated-values", ".tiff": "image/tiff", ".tao": "application/vnd.tao.intent-module-archive", ".tar": "application/x-tar", ".tcl": "application/x-tcl", ".tex": "application/x-tex", ".tfm": "application/x-tex-tfm", ".tei": "application/tei+xml", ".txt": "text/plain", ".dxp": "application/vnd.spotfire.dxp", ".sfs": "application/vnd.spotfire.sfs", ".tsd": "application/timestamped-data", ".tpt": "application/vnd.trid.tpt", ".mxs": "application/vnd.triscape.mxs", ".t": "text/troff", ".tra": "application/vnd.trueapp", ".ttf": "application/x-font-ttf", ".ttl": "text/turtle", ".umj": "application/vnd.umajin", ".uoml": "application/vnd.uoml+xml", ".unityweb": "application/vnd.unity", ".ufd": "application/vnd.ufdl", ".uri": "text/uri-list", ".utz": "application/vnd.uiq.theme", ".ustar": "application/x-ustar", ".uu": "text/x-uuencode", ".vcs": "text/x-vcalendar", ".vcf": "text/x-vcard", ".vcd": "application/x-cdlink", ".vsf": "application/vnd.vsf", ".wrl": "model/vrml", ".vcx": "application/vnd.vcx", ".mts": "model/vnd.mts", ".vtu": "model/vnd.vtu", ".vis": "application/vnd.visionary", ".viv": "video/vnd.vivo", ".ccxml": "application/ccxml+xml,", ".vxml": "application/voicexml+xml", ".src": "application/x-wais-source", ".wbxml": "application/vnd.wap.wbxml", ".wbmp": "image/vnd.wap.wbmp", ".wav": "audio/x-wav", ".davmount": "application/davmount+xml", ".woff": "application/x-font-woff", ".wspolicy": "application/wspolicy+xml", ".webp": "image/webp", ".wtb": "application/vnd.webturbo", ".wgt": "application/widget", ".hlp": "application/winhlp", ".wml": "text/vnd.wap.wml", ".wmls": "text/vnd.wap.wmlscript", ".wmlsc": "application/vnd.wap.wmlscriptc", ".wpd": "application/vnd.wordperfect", ".stf": "application/vnd.wt.stf", ".wsdl": "application/wsdl+xml", ".xbm": "image/x-xbitmap", ".xpm": "image/x-xpixmap", ".xwd": "image/x-xwindowdump", ".der": "application/x-x509-ca-cert", ".fig": "application/x-xfig", ".xhtml": "application/xhtml+xml", ".xml": "application/xml", ".xdf": "application/xcap-diff+xml", ".xenc": "application/xenc+xml", ".xer": "application/patch-ops-error+xml", ".rl": "application/resource-lists+xml", ".rs": "application/rls-services+xml", ".rld": "application/resource-lists-diff+xml", ".xslt": "application/xslt+xml", ".xop": "application/xop+xml", ".xpi": "application/x-xpinstall", ".xspf": "application/xspf+xml", ".xul": "application/vnd.mozilla.xul+xml", ".xyz": "chemical/x-xyz", ".yaml": "text/yaml", ".yang": "application/yang", ".yin": "application/yin+xml", ".zir": "application/vnd.zul", ".zip": "application/zip", ".zmm": "application/vnd.handheld-entertainment+xml", ".zaz": "application/vnd.zzazz.deck+xml"}
3 |
--------------------------------------------------------------------------------
/server/misc.py:
--------------------------------------------------------------------------------
1 | # from inspect import getmembers
2 |
3 | # creates dictionary from query string a=1&b=2&c=sample
4 | def jsonify(data:str) -> dict:
5 | output = {}
6 | data = data.strip().split('&')
7 | for kv in data:
8 | kv = kv.strip().split('=')
9 | if(len(kv) == 2):
10 | output[kv[0]] = kv[1]
11 | return output
12 |
13 | class C:
14 | pass
15 |
16 | def dict2obj(d):
17 |
18 | # checking whether object d is a
19 | # instance of class list
20 | if isinstance(d, list):
21 | d = [dict2obj(x) for x in d]
22 |
23 | # if d is not a instance of dict then
24 | # directly object is returned
25 | if not isinstance(d, dict):
26 | return d
27 | # constructor of the class passed to obj
28 | obj = C()
29 |
30 | for k in d:
31 | obj.__dict__[k] = dict2obj(d[k])
32 |
33 | return obj
34 |
35 |
36 | def isSequence(ob):
37 | a = type(ob)
38 | if(type(ob) is list or type(ob) is tuple or type(ob) is set or type(ob) is frozenset):
39 | return True
40 | return False
41 |
42 | def isSequenceType(type):
43 | if(type is list or type is tuple or type is set or type is frozenset):
44 | return True
45 | return False
46 |
47 |
48 | ## to get types and names of parameters NOTE: won't work without types
49 | # a.__annotations__
50 | # > {'b': , 'c': }
51 |
52 | # just names of parameters
53 | # a.__code__.co_varnames
54 |
55 | def parseJsonToClass(input,model):
56 | output = None
57 | try:
58 | if(model is None or input is None):
59 | output = input
60 | elif isSequence(model) and isSequence(input):
61 | output = []
62 | for i in range(len(input)):
63 | # if(i > len(input) - 1):
64 | # obj = None
65 | # else:
66 | obj = parseJsonToClass(input[i],model[0])
67 | output.append(obj)
68 | elif type(model) is type and type(input) is dict:
69 | output = mapDictToClass(input,model)
70 | elif callable(model) and type(input) is not dict and not isSequence(input):
71 | output = model(input)
72 | else:
73 | output = None
74 | except Exception:
75 | #TODO:better Exceptions
76 | output = None
77 | return output
78 |
79 | def mapDictToClass(dic,cls):
80 | try:
81 | obj = cls()
82 | if(type(obj) is dict):
83 | return dic
84 | for prop,value in getattr(cls,'__dict__').items():
85 | if(not prop.startswith('_') and not callable(prop)):
86 | obj.__dict__[prop] = parseJsonToClass(dic.get(prop,None),value)
87 | return obj
88 | except Exception as ex:
89 | print(str(ex))
90 | #TODO:better Exceptions
91 | return None
92 |
93 |
94 |
95 |
96 | # objectFortype = {
97 | # 'str' : str,
98 | # 'int' : int,
99 | # 'float' : float,
100 | # 'complex' : complex,
101 | # 'list' : list,
102 | # 'tuple' : list,
103 | # 'dict' : dict,
104 | # 'set' : set,
105 | # 'frozenset' : frozenset,
106 | # 'bool' : bool,
107 | # # 'bytes' : bytes,
108 | # # 'bytearry' : bytearray,
109 | # # 'memoryview' : memoryview,
110 | # }
--------------------------------------------------------------------------------
/server/responseHandler.py:
--------------------------------------------------------------------------------
1 | from server.settings import config
2 | from html.parser import HTMLParser
3 | from server.mimeTypes import mimeTypes
4 | import subprocess
5 | from os import name as py,getcwd,environ
6 | import pickle
7 | from server.internalModels import ResoponseClass
8 | if(py=="posix"):
9 | py = "python3"
10 | else:
11 | py = "python"
12 | class ResponseHandler:
13 |
14 | def __init__(self,request,requestType,reqData,customResponse=None,unpack=None):
15 | self.request = request
16 | self.requestType = requestType
17 | self.reqData = reqData
18 | self.customResponse = customResponse
19 | self.unpack = unpack
20 |
21 | #for views
22 | def _serveStatic(self,request):
23 | return _read_bin(request)
24 |
25 | def _serverStaticPythonFunction(self,request):
26 | return request()
27 |
28 | def _serverStaticPythonFile(self,request):
29 | return _run_python_file(request)
30 |
31 | def _serverStaticPyHtml(self,request):
32 | return _processPyHtml(request)
33 |
34 | #for controllers
35 | def _executeAndServeFunction(self,request,reqData,unpack):
36 | if unpack:
37 | return request(*reqData)
38 | else:
39 | return request(reqData)
40 |
41 | def _executeAndServeFile(self,request,reqData):
42 | return subprocessPyFile(request,reqData)
43 |
44 | def respond(self):
45 | try:
46 | if(self.requestType == 'static'):
47 | if(self.request.endswith('.html')):
48 | self.response = ResoponseClass(self._serveStatic(self.request),200,'text/html')
49 | elif(self.request.endswith('.py')):
50 | self.response = ResoponseClass(self._serverStaticPythonFile(self.request),200,'text/html')
51 | elif(self.request.endswith('.pyhtml')):
52 | self.response = ResoponseClass(self._serverStaticPyHtml(self.request),200,'text/html')
53 | else:
54 | self.response = ResoponseClass(self._serveStatic(self.request),200,mimeTypes.get('.'+self.request.split('.')[-1],'application/octet-stream'))
55 | elif(self.requestType == 'staticFunction'):
56 | self.response = ResoponseClass(self._serverStaticPythonFunction(self.request),200,'text/html')
57 | elif(self.requestType == 'controllerFunction'):
58 | res = self._executeAndServeFunction(self.request,self.reqData,self.unpack)
59 | self.handleCustomResponse(res)
60 | elif(self.requestType == 'controllerFile'):
61 | res = self._executeAndServeFile(self.request,self.reqData)
62 | self.handleCustomResponse(res)
63 | else:
64 | raise BaseException
65 | except Exception as e:
66 | if config.logging:
67 | print(e)
68 | self.response = ResoponseClass(type(e).__name__,500,'text/html')
69 | return self.response
70 |
71 | def handleCustomResponse(self,res):
72 | if(self.customResponse == None):
73 | self.response = ResoponseClass(res,200,'application/json')
74 | elif(self.customResponse == True):
75 | self.response = ResoponseClass(res.get('content'),res.get('code',200),res.get('mimeType','application/json'))
76 | elif(type(self.customResponse) is dict):
77 | self.response = ResoponseClass(res,self.customResponse.get('code',200),self.customResponse.get('mimeType','application/json'))
78 | else:
79 | # TODO:Raise Custome Reponse Exception
80 | raise Exception
81 |
82 | def subprocessPyFile(request,reqData):
83 | # return sp.check_output([py, request,reqData])
84 | reqData = pickle.dumps(reqData,protocol=pickle.HIGHEST_PROTOCOL)
85 | # to access this reqData in file do "from server.helper import reqData"
86 | # res = subprocess.run(['python',request,str(reqData)])#stderr=subprocess.STDOUT,capture_output=True
87 | try:
88 | en = {
89 | **environ,
90 | "PYTHONPATH":getcwd()
91 | }
92 | # "export PYTHONPATH=$PYTHONPATH:{} && ".format(getcwd())+py,
93 | res = subprocess.run([py,request,str(reqData)],capture_output=True,env=en)
94 | except Exception as e:
95 | print(e)
96 | raise(e)
97 | return res.stdout
98 |
99 | def _read_bin(file):
100 | with open(file,"rb") as nfile:
101 | return nfile.read()
102 |
103 | def _run_python_file(file):
104 | ## not using read-text function here because not reading as binary here.
105 | readFile:str
106 | with open(file,"r") as f:
107 | readFile = f.read()
108 | lcl = {'main':None}
109 | exec(readFile,{},lcl)
110 | return lcl['main']()
111 |
112 | def _processPyHtml(file):
113 | ## not using read-text function here because not reading as binary here.
114 | readFile:str
115 | with open(file,"r") as f:
116 | readFile = f.read()
117 | parser = MyHTMLParser(readFile)
118 | parser.feed(readFile)
119 | return parser.processed_file
120 |
121 | ### class to process inline python
122 | class MyHTMLParser(HTMLParser):
123 | def __init__(self,file):
124 | HTMLParser.__init__(self)
125 | self.recording = 0
126 | self.processed_file = file
127 | # self.sdata = []
128 | def handle_starttag(self, tag, attrs):
129 | if tag=="py":
130 | self.recording += 1
131 |
132 | def handle_endtag(self, tag):
133 | if tag=="py":
134 | self.recording -= 1
135 |
136 | def handle_data(self,data):
137 | if self.recording == 1:
138 | place = data
139 | data = data.split("\n")
140 | data = list(filter(lambda x:x.strip()!="",data))
141 | min_tabs = 999
142 | for i in range(0,len(data)):
143 | tabs = len(data[i]) - len(data[i].lstrip(' '))
144 | if(tabs this behaviour can be changed if required with little cchange to code
69 | },
70 | ]
71 |
72 | class Test_REQUEST_DATA_TO_OBJECT(unittest.TestCase):
73 | def test_json_to_class_parse(self):
74 | test_number = 0
75 | for test in test_cases:
76 | #test.get('class variables').get('mode',None)
77 | data = test.get('data',None)
78 | model = test.get('model',None)
79 | args_count = test.get('argsCount',None)
80 | input_params = ['PSEUSO_SELF',data,model,test.get('types',None),args_count]
81 | processed_data = PyOPSever.ParseRequestDataInToObject(*input_params)
82 | #desired = []
83 |
84 | desired_type = None
85 | if(type(model) == type):
86 | desired_type = model
87 | else:
88 | desired_type = type(model)
89 | # self.assertAlmostEqual(desired_type,type(processed_data),"Type Equality Check Failed")
90 | type_test = (isSequenceType(desired_type) and isSequenceType(type(processed_data)) or (desired_type is type(processed_data)))
91 | if(type_test == False):
92 | print("Failed")
93 | self.assertEqual(type_test,True,"Type Equality Check Failed : Number {}".format(test_number))
94 | actual = []
95 | if(isSequenceType(desired_type)):
96 | actual = processed_data
97 | else:
98 | for prop,value in getattr(model,'__dict__').items():
99 | if(not prop.startswith('_') and not callable(prop)):
100 | #desired.append(test.get('data',None).get(prop,None))
101 | actual.append(getattr(processed_data,prop))
102 | self.assertSequenceEqual(test.get('output_as_list'),actual,"Values Equality Check Failed : Number {}".format(test_number))
103 |
104 | test_number += 1
105 |
--------------------------------------------------------------------------------