写一个简易的web服务器

版权声明:我的码云地址:https://gitee.com/ZzYangHao https://blog.csdn.net/qq_21049875/article/details/82662594

  在看完书后,有种想写webserver的感觉,虽然知道写webserver不是那么简单的一回事,设计很多协议以及接口,但是还是想动手体验一下,所以就用python写了一个,为什么用python的话,原因是python语法简短,写起来可能感觉轻松一些。
  代码上传到我的码云(中国的github)里了。
  看情况更新把T_T
  目前仅仅实现:
    处理GET请求静态文件,将静态文件放入static文件夹即可通过get请求访问到,当然还是得分文件权限的。
    2018.9.12:更新了GET请求动态事件,或者称为视图函数,本来想写一个类似于render_template(‘xxxxx.html’,xxx=xxx)这样的,但是这样就需要引用jinjia模板,或者自己重写一个类似作用的方法,然后映射进去,所以暂时就先实现一个简单的,如下面这样:(hello视图函数负责将url中传入的参数进行求和并返回)
相应的视图函数代码:


@app.route('/hello')
def sum_args(arg):
    sum=0
    for i in arg:
        sum+=int(i)
    return sum

对应的结果:
这里写图片描述
  待添加的功能:
    处理GET请求动态文件(我已经写了一个简单的路由,因为之前使用过一丁点flask,所以就模仿其外观实现了一个,其内部实现还是挺复杂的),已经快完成了。
    处理POST请求
  
  **待完善======================**
  PS:代码依旧写得很烂orz
  为了方便调试,设置是单线程运行。
  
代码如下:

import os,stat,sys
import socket,threading
import functools


maxline=2048
curdir=os.getcwd()

class netIO:
    datasize=0
    def __init__(self,fd):
        self.fd=fd

    def readline(self,bufsize):
        data=b''
        tmp=self.fd.recv(1)
        while tmp !=b'\n':
            data+=tmp
            tmp=self.fd.recv(1)
        return data.decode('utf-8')

    
        

class webServer:
    listenNum=10
    port=8080
    def __init__(self):
        self.serfd=socket.socket()
        self.view_func={}
    
    def setListenNum(self,num):
        self.listenNum=num

    def readrequestheader(self,io):
        hdr=io.readline(maxline)
        while hdr!='\r':
            hdr=io.readline(maxline)
            print(hdr)

    def add_url_rule(self,rule,f):
        self.view_func[rule]=f

    def findroute(self,rule):
        for key in self.view_func.keys():
            if rule==key:
                return True
        return False

    def route(self, rule, **options):
        def decorator(f):
            self.add_url_rule(rule, f)
            return f
        return decorator

    def parseUrl(self,url):
        pathindex=url.find('//')
        if pathindex>0:
            url=url[pathindex:]
        args=[]
        path=''
        isstatic=True
        pathindex=url.find('/')
        if pathindex>=0:
            argsindex=url.find('?')
            if argsindex>0:
                path+=url[pathindex:argsindex]
                tmpurl=url[argsindex+1:]
                args=tmpurl.split('&')
                isstatic=False
            else:
                path+=url[pathindex:]
                args.append(url[argsindex+1:])
            
        else:
            path='/'
        return (path,tuple(args),isstatic)
        
    
    def getfiletype(self,path):
        staticfile=['.html','.txt','.jpg','.png','.gif']
        i=0
        for o in staticfile:
            i+=1
            if path.find(o) >0:
                if i==1:
                    return 'text/'+o.replace('.','')
                else:
                    return 'image/'+o.replace('.','')


    def server_static(self,fd,path,length):
        filetype=self.getfiletype(path)
        responseline='HTTP/1.0 200 OK\r\n'
        severName='Server: Test Web\r\n'
        connection='Connection: close\r\n'
        content='Content-length: '+str(length)+'\r\n'
        contenttype='Content-type: '+filetype+'\r\n'
        ret=(responseline+severName+connection+content+contenttype+'\r\n').encode('utf-8')
        fd.send(ret)
        f=open(curdir+'/static'+path,'r')
        buf=f.read()
        f.close()
        fd.send(buf.encode('utf-8'))
    
    def server_dynamic(self,fd,path,args):
        responseline='HTTP/1.0 200 OK\r\n'
        severName='Server: Test Web\r\n'
        ret=responseline+severName+'\r\n'
        try:
            func=None
            for key in self.view_func.keys():
                if path==key:
                    func=self.view_func[key]
                    break
            if func !=None:
                fd.send(ret.encode('utf-8'))
                data=func(args)
                fd.send(str(data).encode('utf-8'))
            else:
                self.clienterror(fd,path,'404','Not Found')

        except TypeError:
            self.clienterror(fd,path,'403','Forbidden')
        finally:
            return


    def clienterror(self,fd,path,errmsgno,msg):
        s='<html><title>Error</title>'
        body='<body bgcolo=""ffffff"">\r\n'
        bodymsg=errmsgno+':'+msg+'\r\n'+'</body></html>\r\n'
        body+=bodymsg
        body=s+body
        ret='HTTP/1.0 '+errmsgno+' '+msg+'\r\n'
        contenttype='Content-type: text/html\r\n'
        contentlen='Content-length: '+str(len(body))+'\r\n'
        ret+=contenttype+contentlen+'\r\n'

        fd.send(ret.encode('utf-8'))
        fd.send(body.encode('utf-8'))

      


    def doit(self,fd):
        io=netIO(fd)
        reqline=io.readline(maxline)
        method_url_version=[]
        for s in reqline.replace('\r\n',' ').split(' '):
            method_url_version.append(s)
        print(method_url_version)
        self.readrequestheader(io)
        if(method_url_version[0].upper()=='GET'):
            path,args,isstatic=self.parseUrl(method_url_version[1])
            if isstatic==True:
                try:
                    static_file=os.stat(curdir+'/static'+path)
                except FileNotFoundError:
                    self.clienterror(fd,path,'404','Not Found')
                    return 
                if stat.S_ISREG(static_file.st_mode) !=True or (stat.S_IRUSR & static_file.st_mode) ==False:
                    self.clienterror(fd,path,'403','Forbidden')
                    return
                self.server_static(fd,path,static_file.st_size)
            else:
                if self.findroute(path) ==False:
                    self.clienterror(fd,path,'404','Not Found')
                    return
                self.server_dynamic(fd,path,args)
        fd.close()
      

    def run(self,ip='',port=8080):
        serfd=self.serfd
        serfd.bind((ip,port))
        serfd.listen(self.listenNum)
        while(1):
            confd,addr=serfd.accept()
            print('accept connection from : ',addr)
            #m_thread=threading.Thread(target=webServer.doit,args=(self,confd,))
            #m_thread.setDaemon(True)
            #m_thread.start()
            self.doit(confd)  


app=webServer()

@app.route('/hello')
def sum_args(arg):
    sum=0
    for i in arg:
        sum+=int(i)
    return sum

app.run()



猜你喜欢

转载自blog.csdn.net/qq_21049875/article/details/82662594