import socket
import os.path
IP = "127.0.0.1"
PORT = 80
DEFAULT_URL = "C:\webroot\index.html"
SOCKET_TIMEOUT = 0.2
def get_file_data(filename):
""" Get data from file """
source_file = open(filename, 'rb')
data = source_file.read()
source_file.close()
return data
def handle_client_request(resource, client_socket):
""" Check the required resource, generate proper HTTP response and send to client"""
if resource == '/':
url = DEFAULT_URL
else:
url = resource
if os.path.isfile(url):
http_header = "HTTP/1.0 200 OK\r\n"
else:
client_socket.send("404 (Not Found)\r\n" + "connection close")
client_socket.close()
file_type = url.split(".")[-1]
if file_type == 'html' or file_type == 'txt':
http_header += "Content-Type: text/html; charset=utf-8\r\n"
elif file_type == 'jpg':
http_header += "Content-Type: image/jpeg\r\n"
elif file_type == 'js':
http_header += "Content-Type: text/javascript; charset=UTF-8\r\n"
elif file_type == 'css':
http_header += "Content-Type: text/css\r\n"
data = get_file_data(url)
http_header += "Content-Length:" + str(len(data)) + "\r\n"
http_response = http_header + "\r\n" + data
client_socket.send(http_response)
def validate_http_request(request):
""" Check if request is a valid HTTP request and returns TRUE / FALSE and the requested URL """
request_li = request.split("\r\n")[0].split(" ")
if request_li[0] != "GET" or request_li[2] != "HTTP/1.1" '/':
return False, ''
return True, request_li[1]
def handle_client(client_socket):
""" Handles client requests: verifies client's requests are legal HTTP, calls function to handle the requests """
print 'Client connected'
try:
while True:
client_request = client_socket.recv(1024)
print client_request.split("\r\n")[0]
valid_http, resource = validate_http_request(client_request)
if valid_http:
print 'Got a valid HTTP request'
handle_client_request(resource, client_socket)
else:
print "Error: HTTP request isn't valid"
break
print "closing connection"
client_socket.close()
except socket.timeout:
print "closing connections"
client_socket.close()
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((IP, PORT))
server_socket.listen(10)
print "Listening for connections on port %d" % PORT
while True:
client_socket, client_address = server_socket.accept()
client_socket.settimeout(SOCKET_TIMEOUT)
print 'New connection received'
handle_client(client_socket)
if __name__ == "__main__":
main()
我正在为分配构建HTTP服务器,服务器应该在浏览器上从我的计算机运行本地文件。现在我正试图运行默认网址。
首先我得到" /"作为一个好的请求,但后来我收到一个空请求,这是一个关闭连接的无效请求。 在服务器创建新连接之后,它获得" /css/doremon.css"作为请求。 doreomn.css是我尝试运行的网站的文件。这将在get_file_data中创建一个错误,因为路径应该是:" C:\ webroot \ css \ doremon.css"。
这提出了两个问题: 1.为什么客户端向服务器发送空请求?如何防止它们中断连接? 2.从第三个请求看来客户端首先发送请求的URL然后请求与之相关的文件,有没有办法立即接收所有这些文件?如果没有,我如何修复所请求文件的路径?
答案 0 :(得分:2)
是。通常,Web服务器具有" web root"的概念。满足所有请求的路径。但这是由Web服务器创建和实施的概念。所以,让我们调用该路径WebRoot
(听起来这就是你想要的C:\webroot\
)。
通常默认网址为WebRoot/index.html
"。如果该页面引用了其他内容(例如" /css/doremon.css"),客户端将请求该资源(GET /css/doremon.css ...
)和服务器< / em>以WebRoot/css/doremon.css
的内容作出回应。但这是因为服务器将请求的资源附加到它自己的WebRoot
概念。客户如何知道这样做?作为网络服务器的作者,在您致电os.path.is_file
之前,您有责任这样做。
无论如何,这对安全很重要。您不希望客户端能够在服务器的文件系统的任意部分中进行操作。客户端无法访问WebRoot
之外的任何内容。
要实现这一点,你应该这样做:
WEBROOT = 'c:\\webroot\\'
DEFAULT_URL = os.path.join(WEBROOT, "/index.html")
处理请求时:
if resource == '/':
url = DEFAULT_URL
else:
url = os.path.join(WEBROOT, resource)
对于您的问题1,客户端可能实际上并未发送空请求。但是,您没有处理&#34;文件结尾&#34;正确。你的基本循环应该是这样的:
while True:
client_request = client_socket.recv(1024)
if client_request == '':
# Client closed connection
break
valid_http, resource = validate_http_request(client_request)
[...]
对于你的问题2,正如@Ereli所说,你应该研究HTTP keep-alive。这是一种保持连接打开的机制,但只有在双方同意的情况下才会使用。如果您希望支持,您需要在链接中所述的标题中做广告。如果您不宣传它们,则HTTP假定每个连接有一个请求。因此,关闭连接是正确的行为,直到您提供Connection: keep-alive
标头(并且仍然始终是有效行为)。并且您正在将该封闭连接(由recv
返回的空缓冲区发出信号)解释为&#34;空请求&#34;。
最后,如果/当做处理keep-alive时,您将需要更彻底地解析标头。 HTTP请求是一行(通常包含GET,但也有其他动词),后面跟着一个不确定数量的额外标题行,后跟一个空行,然后可能是附加数据(附加数据不存在于GET但可能是PUT和POST)。
现在所有这些行都可能不通过一次recv
调用传递。因此,您需要继续接收数据,直到找到表示请求结束的空白行(或者您可以 - 并且可能应该 - 施加一些任意限制,以保护自己免受试图耗尽所有内存的攻击者通过永远喂你的标题)。
此外,您应该知道可能会收到来自单个recv
的单个请求的更多。例如,一旦您通告了keep-alive,客户端就可以重新发送两个请求(称为流水线)。这些可能会通过单个接收显示在缓冲区中。所以要记住的重要一点是,对于HTTP,你需要解析行,而不是一次只处理一个完整的缓冲区。