python实现web服务器-实现cookie和session(三)

import socket, os.path, threading,uuid

LEN=1024*64
HOME_PAGE='index.html'
HTML_404='404.html'
# web应用的根路径
WEB_ROOT=r'C:\Users\mym\Desktop\暑期上课'
KEY_SESSIONID='SESSIONID'

# url和自定义函数的映射
url_fun_mapping={}
'''
记录session信息

key: 随机字符串
value: { '自定义key':'自定义value' } 用户名、密码等等(自定义)
'''
sessions={}

def main(port=80):
    #默认就是ipv4,tcp
    server=socket.socket()
    server.bind(('0.0.0.0',port))
    # 与操作系统有很大关系,需要操作系统支持,有时设置了,也没用
    server.listen(5)
    print('启动成功!')
    while True:
        # client就是socket对象,表示该客户的
        client,addr=server.accept()
        threading.Thread(target=handle,args=(client,addr)).start()

def handle(client,addr):
    # 读取请求头信息
    fp=client.makefile('r',encoding='utf-8')
    line=fp.readline(LEN)

    if line=='':
        return
    # 读取第一行指令
    method,url,version=line.split(' ')
    print('method:',method)
    print('url:',url)
    print('version:',version)

    headers=readheaders(fp)
    print(headers)

    # 从header中解析cookie
    cookie=parsecookie(headers)

    sessionid,session,existsession=handlesession(cookie)

    # 发送数据
    if url=='/':
        url=HOME_PAGE

    # 从url中解析纯url,参数
    url2,params=parseUrl(url)
    # 根据纯url获取与之对应的自定函数
    fun=url_fun_mapping.get(url2)
    # 如果存在就调用,不存在就按原来的操作继续往下运行
    if fun:
        ret=fun(params,session=session)
        if isinstance(ret,str):
            if existsession:
                sendhtml(client,ret,None)
            else:
                sendhtml(client,ret,sessionid)
        else:
            print('未实现------------------')

    else:
        # 读文件并发送,采用二进制发送,读一部分发送一部分
        if url[0]=='/':
            url=url[1:]

        filepath=os.path.join(WEB_ROOT,url)
        sendfile(client,filepath)

    client.close()

def readheaders(fp):
    # 读取并封装后面的头信息
    headers={}
    while True:
        line=fp.readline()
        # print(line,end='')
        if line=='\n':
            break
        pos=line.index(':')
        headers[line[:pos]]=line[pos+2:-1]


    return headers  
# 发送文件
def sendfile(client,filepath):
    if os.path.exists(filepath):
        # 发送响应头 response headers
        client.send(b'HTTP/1.1 200 OK\r\n')
    else:
        # 发送响应头 response headers
        client.send(b'HTTP/1.1 404 NOT FOUND\r\n')
        filepath=os.path.join(WEB_ROOT,HTML_404)

    # 截取访问资源的后缀,并转换小写
    suffix=filepath[filepath.rindex('.')+1:].lower()
    content_type=get_content_type(suffix)
    client.send(content_type)
    filesize=os.path.getsize(filepath)
    client.send(('Content-Length: %s\r\n'%filesize).encode('utf-8'))
    client.send(b'Server: shanghaibaolei\r\n')
    client.send(b'\r\n')
    with open(filepath,'rb') as f:
        data=f.read(LEN)
        client.sendall(data)

    # client.close()
# 根据访问资源的后缀,获取content-type
def get_content_type(suffix):
    ret=None
    if suffix=='html':
        ret=b'Content-Type: text/html;charset=UTF-8\r\n'
    elif suffix=='png':
        ret=b'Content-Type: image/png\r\n'
    elif suffix=='gif':
        ret=b'Content-Type: image/gif\r\n'
    elif suffix in ['jpg','jpeg']:
        ret=b'Content-Type: image/jpeg\r\n'
    elif suffix =='css':
        ret=b'Content-Type: text/css\r\n'
    elif suffix =='js':
        ret=b'Content-Type: application/x-javascript\r\n'
    return ret

def parseUrl(url):
    '''
    解析url
    /login?username=mayumin&password=123456
    截取出url,并将url后的参数封装成dict类型
    并返回 url(纯url),dict
    '''
    params={}
    url2=None
    pos=url.find('?')
    if pos==-1:
        return url,params
    else:
        url2=url[:pos]
        params_str=url[pos+1:]
        array=params_str.split('&')
        for item in array:
            name,value=item.split('=')
            params[name]=value

    return url2,params

def sendhtml(client,html,sessionid):
    '''
    向浏览器发送html字符串
    '''
    ret_bytes=html.encode('utf-8')
    client.send(b'HTTP/1.1 200 OK\r\n')
    client.send(b'Content-Type: text/html;charset=UTF-8\r\n')
    client.send(('Content-Length: %s\r\n'%len(ret_bytes)).encode('utf-8'))
    client.send(b'Server: shanghaibaolei\r\n')
    if sessionid:
        cookie='Set-Cookie: %s=%s\n'%(KEY_SESSIONID,sessionid)
        client.send(cookie.encode('utf-8'))
    client.send(b'\r\n')
    client.sendall(ret_bytes)

def parsecookie(headers):
    '''
    从headers中,取出cookie,并封装成dict对象
    '''
    retcookie={}
    cookiestr=headers.get('Cookie')
    if cookiestr:
        cookiearray=cookiestr.split(';')
        for item in cookiearray:
            key,value=item.split('=')
            retcookie[key.strip()]=value.strip()

    return retcookie

def handlesession(cookie):
    '''
    1. 从响应头中找到cookie
    2. 从cookie中找到 sessionid
    3. 如果有sessionid,就不做操作,如果没有,就生成随机字符串,作为key,放入全局变量sessions,value是空的dict
    4. 如果没有sessionid,还要通知响应头,发送Set-Cookie: sessionid=123456
    '''
    global sessions
    sessionid=cookie.get(KEY_SESSIONID)
    exist=False
    session=None

    if sessionid==None:# 浏览器发来的cookie中没有sessionid
        exist=False
        sessionid=str(uuid.uuid1()).replace('-','')
        sessions[sessionid]={}
        session=sessions[sessionid]
    else: # 浏览器发来的cookie中有sessionid
        exist=True
        session=sessions.get(sessionid)
        if session==None: # sessions没有sessionid。场景:服务器重启
            sessions[sessionid]={}
            session=sessions[sessionid]

    return sessionid,session,exist


# 自定义函数
def login(params,session):
    print('执行login()---',params)
    '''
    1. 连接数据库
    2. select * from user where username=:1 and password=:2
    3. 判断用户是否登录成功
    '''
    username=params.get('username')
    print('username=',username)
    password=params.get('password')
    print('password=',password)
    # 登录成功后,一定要向session放些数据(唯一,可以识别用户,一般情况下:DB中user表的主键,还可以放登录名、性别、一句话介绍。。。。。)
    session['userid']='123456789'
    return '登录成功!'

def order(params,session):
    '''
    下订单
    '''
    # 从业务上:是否登录
    # session['userid']
    ret=''
    userid=session.get('userid')
    if userid==None:
        ret='未登录!'
    else:
        ret='已登录,userid=%s'%userid

    return ret

def reg(params):
    print('执行reg()---',params)


if __name__ == "__main__":
    # key:url,value:函数对象
    url_fun_mapping['/login']=login
    url_fun_mapping['/dologin']=login
    url_fun_mapping['/reg']=reg
    url_fun_mapping['/order']=order
    main()

原文出处:/show_1EF3w3m7LEYs.html