用 Python 写了一个 WebServer , 有 BaseHTTPServer 可用, 但是用这个的感觉就像我用 vb.net 里面的 HttpListener , 因为我已经用这个写过一个 vb.net 的HttpServer。
test: http://new.twd2.net:88
#!/usr/bin/python import sys, socket, threading, os, urllib, mimetypes, time import fastcgi, getopt server_name='wds/1.0' listen_bind=('0.0.0.0', 0) conn_timeout=60 conn_list=[] datablock=8192 *5 host_list={} max_connection=1000 class HttpConnection(threading.Thread): def __init__(self, fd, addr): threading.Thread.__init__(self) self.server_name=server_name self.fd=fd self.addr=addr self.running=True self.alive_time=time.time() self.req={} self.serv_addr=listen_bind[0] self.serv_port=listen_bind[1] #print(addr[0]) def sendfile(self, filename, offset=0, maxend=-1): filesize=os.path.getsize(filename) if maxend>-1: filesize=min(filesize, maxend) fread=offset fileh=open(filename, 'rb') fileh.seek(offset) while fread+datablock<filesize: self.alive_time=time.time() buff=fileh.read(datablock) self.fd.send(buff) fread+=datablock else: self.alive_time=time.time() buff=fileh.read(filesize-fread) self.fd.send(buff) def kill(self): self.running=False self.fd.shutdown(socket.SHUT_RDWR) def readline(self): r='' while r.find('\n')<0: r+=self.fd.recv(1) return r def getrequest(self): #global method, req_path, httpver, req #fdfile=self.fd.makefile() mrh=self.readline()#fdfile.readline() if mrh=='': self.kill() return mrh=mrh.replace('\r', '') mrh=mrh.replace('\n', '') mrh=mrh.split(' ') #print(mrh) if len(mrh)<=1: self.kill() return self.method=mrh[0].upper() self.req_path=mrh[1] self.uri=self.req_path self.httpver=mrh[2] self.req={} #parse request data while True: self.alive_time=time.time() r=self.readline()#fdfile.readline() r=r.replace('\r', '') r=r.replace('\n', '') #print(r) if r=='': break r=r.split(':', 1) t=r[0] v=r[1].strip() self.req[t.lower()]=v if 'host' in self.req and ':' in self.req['host']: self.req['host']=self.req['host'][0:self.req['host'].find(':')] def abspath(self, root, path): os.chdir(root) if path[0:1]=='/': path=path[1:len(path)] r=os.path.abspath(path) if r.find(root)!=0: return root return r def sendresponse(self, res, code, des): #fdfile=self.fd.makefile() self.fd.send('HTTP/1.1 ' + str(code) + ' ' + des + '\r\n') #print(res) for t in res: #print(t) v=res[t] #print(v) #print(t, v) self.fd.send(t + ': ' + v + '\r\n') self.fd.send('\r\n') def sendtext(self, msg): fdfile=self.fd.makefile() fdfile.write(msg) def run(self): try: while self.running: self.res={'Server': server_name, 'Cache-Control': 'private'} code=200 des='OK' if len(conn_list)>max_connection: code=503 des='Service Unavailable' txt=des self.res['Content-Type']='text/html' self.res['Content-Length']=str(len(txt)) self.sendresponse(self.res, code, des) self.sendtext(txt) break #recv=self.fd.recv(1024) self.getrequest() if self.httpver!='HTTP/1.1': code=505 des='HTTP Version Not Supported' txt=des self.res['Content-Type']='text/html' self.res['Content-Length']=str(len(txt)) self.sendresponse(self.res, code, des) self.sendtext(txt) self.kill() break if not self.running: return if 'host' in self.req and self.req['host'] in host_list: host=host_list[self.req['host']] elif '*' in host_list: host=host_list['*'] else: code=400 des='Bad Request(Invalid Hostname)' txt=des self.res['Content-Type']='text/html' self.res['Content-Length']=str(len(txt)) self.sendresponse(self.res, code, des) self.sendtext(txt) continue #print(host) #print(req_path) id=self.req_path.find('?') self.querystring='' if id>=0: self.querystring=self.req_path[id+1:len(self.req_path)] self.req_path=self.req_path[0:id] #print(self.req_path) if os.path.isdir(self.abspath(host['path'], self.req_path)) and self.req_path[len(self.req_path)-1:len(self.req_path)]!='/': self.res['Location']='http://' + self.req['host'] if listen_bind[1]!=80: self.res['Location']+=':' + str(listen_bind[1]) self.res['Location']+=self.req_path + '/' if self.querystring!='': self.res['Location']+='?' + self.querystring code=301 des='Moved Permanently' self.res['Content-Length']='0' self.sendresponse(self.res, code, des) continue self.req_path=urllib.unquote(self.req_path) self.req_path=self.abspath(host['path'], self.req_path) #print(self.req_path) if os.path.isdir(self.req_path): #looking for default file for defile in host['default']: if(self.abspath(self.req_path, defile)): self.req_path=self.abspath(self.req_path, defile) break elif not os.path.isfile(self.req_path): script=host['path'] filelist=self.req_path[len(host['path']):len(self.req_path)].split('/') for file in filelist: if file=='': continue script+='/' + file #print(script) if os.path.isfile(script) and 'application/x-httpd-php' in mimetypes.guess_type(script): self.req_path=script break self.root=os.path.dirname(self.req_path) if self.root[len(self.root)-1:len(self.root)]!='/': self.root+='/' self.root=self.root[len(host['path']):len(self.root)] #print(self.req_path, self.root) #print(self.req_path) if os.path.isfile(self.req_path) and mimetypes.guess_type(self.req_path)[0]!=None: if 'application/x-httpd-php' in mimetypes.guess_type(self.req_path): #if 'expect' in self.req: # exp=self.req['expect'].split('-', 1) # code=int(exp[0]) # des=exp[1].capitalize() # self.sendresponse({}, code, des) self.script_name=self.req_path[len(host['path']):len(self.req_path)] self.res['Cache-Control']='no-cache' fcgi=fastcgi.fastcgi(('127.0.0.1', 9000), self) fcgi.process() break filestat=os.stat(self.req_path) last_modified=time.localtime(filestat.st_mtime) self.res['Last-Modified']=time.strftime('%a, %d %b %Y %H:%M:%S GMT', last_modified) if 'if-modified-since' in self.req: #print(res['Last-Modified'], self.req['if-modified-since']) if self.res['Last-Modified']==self.req['if-modified-since']: code=304 des='Not modified' self.res['Content-Length']='0' self.res['Content-Type']=mimetypes.guess_type(self.req_path)[0] self.sendresponse(self.res, code, des) continue if self.method!='GET': postlen=int(self.req['content-length']) code=405 des='Method Not Allowed' txt=des self.req_path=self.abspath(host['path'], host['500']) #self.fd.send(b'''HTTP/1.1 500 Not supported method\nServer: WDServer\nContent-Type: text/html\nContent-Length: 20\n\nNot supported method''') #self.fd.close() #return else: code=404 des='Not found' txt=des self.req_path=self.abspath(host['path'], host['404']) if os.path.isfile(self.req_path): offset=0 maxend=-1 self.res['Content-Type']=mimetypes.guess_type(self.req_path)[0] self.res['Content-Length']=str(os.path.getsize(self.req_path)) if 'range' in self.req: rdata=self.req['range'].split('=', 1) if rdata[0]=='bytes': rng=rdata[1].split('-', 1) offset=int(rng[0]) if rng[1]!='': maxend=int(rng[1]) if maxend>-1: self.res['Content-Length']=str(maxend-offset+1) self.res['Content-Range']='bytes ' + str(offset) + '-' + str(maxend) + '/' + str(os.path.getsize(self.req_path)) code=206 des='Partial Content' self.sendresponse(self.res, code, des) self.sendfile(self.req_path, offset, maxend) else: #txt='.conf error!' self.res['Content-Type']='text/html' self.res['Content-Length']=str(len(txt)) self.sendresponse(self.res, code, des) self.sendtext(txt) if not 'connection' in self.req or self.req['connection'].lower()!='keep-alive': self.kill() return #self.fd.close() except: print('err') try: self.kill() except: return def listen(): global conn_list http_listener=socket.socket(socket.AF_INET, socket.SOCK_STREAM) http_listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) http_listener.bind(listen_bind) http_listener.listen(1024) conn_lock=threading.Lock() while(True): conn_fd, addr=http_listener.accept() conn_lock.acquire() conn=HttpConnection(conn_fd, addr) conn.start() try: now=time.time() for conn in conn_list: if now-conn.alive_time > conn_timeout: if conn.running: conn.kill() conn_list=[conn for conn in conn_list if conn.running] #print(conn_list) #break except: print(sys.exc_info()) conn_list.append(conn) conn_lock.release() http_listener.close() def load_config(): global listen_bind confile=open('WS.conf', 'r') lines=confile.readlines() bind=lines[0] bind=bind.replace('\r', '') bind=bind.replace('\n', '') bind=bind.split(' ') del lines[0] listen_bind=(bind[0], int(bind[1])) for host in lines: host=host.replace('\r', '') host=host.replace('\n', '') host=host.split(' ') thishost={'name': host[0], 'path': os.path.abspath(host[2])} if os.path.isfile(thishost['path'] + '/' + host[0] + '.conf'): subconf=open(thishost['path'] + '/' + host[0] + '.conf', 'r') sublines=subconf.readlines() for i in range(0, 3): sublines[i]=sublines[i].replace('\r', '') sublines[i]=sublines[i].replace('\n', '') thishost['default']=sublines[0].split(', ') thishost['404']=sublines[1] thishost['500']=sublines[2] else: thishost['default']={'index.htm', 'index.html', 'index.php'} thishost['404']='' thishost['500']='' host_list[host[1]]=thishost #print(host_list) def start(): pid=0#os.fork() if pid==0: try: load_config() listen() except KeyboardInterrupt: print('Exit()') for conn in conn_list: if conn.running: try: conn.kill() except: print(sys.exc_info()) exit() else: print('PID: ' + str(pid)) fo=open('wds.pid', 'w') fo.write(str(pid)) fo.close() #sys.stdin.read(1) #os.system('kill -9 ' + str(pid)) #print('Killed') def main(): try: opts, args=getopt.getopt(sys.argv[1:], 'sp', ['start', 'stop']) for n, v in opts: if n in ('-s', '--start'): start() if n in ('-p', '--stop'): pid='0' if os.path.isfile('wds.pid'): fo=open('wds.pid', 'r') pid=fo.readline() fo.close if pid!='0': os.system('kill -9 ' + pid) print('Killed: ' + pid) else: print('Not running.') fo=open('wds.pid', 'w') fo.write('0') fo.close() except: print(sys.exc_info()) if __name__=='__main__': main()
唉,我没有写代码的时间。。。
羡慕你能有足够的时间写代码。
你怎么了呢
我们的初三下很惨很惨。老师们将布置超多作业。(其实本来就很多,现在更多了。。。)
膜拜
orz!