在http服务器

时间:2016-12-29 19:32:12

标签: python sockets http server

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然后请求与之相关的文件,有没有办法立即接收所有这些文件?如果没有,我如何修复所请求文件的路径?

1 个答案:

答案 0 :(得分:2)

是。通常,Web服务器具有" web root"的概念。满足所有请求的路径。但这是由Web服务器创建和实施的概念。所以,让我们调用该路径WebRoot(听起来这就是你想要的C:\webroot\)。

通常默认网址为WebRoot/index.html&#34;。如果该页面引用了其他内容(例如&#34; /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,你需要解析,而不是一次只处理一个完整的缓冲区。