用 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!