├── .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 | PyOP_LOGO
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 |
    5 | FirstName:
    6 | LastName:
    7 | 8 |

    return("hello there")

    9 |

    10 | 11 | import random 12 | return("hello there\ngeneral kenobi "+str(random.randint(1,101))) 13 | 14 |

    15 | 16 |
    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 |
    14 | Num1:
    15 | Num2:
    16 | 17 |
    18 | 19 |

    Sum of 3 Numbers with square of 4th

    20 |
    21 | Num1:
    22 | Num2:
    23 | Num3:
    24 | Num4:
    25 | 26 |
    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 | --------------------------------------------------------------------------------