2016-12-29 2 views
1
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-сервер для назначения, сервер должен запускать локальные файлы с моего компьютера в браузере. Прямо сейчас я пытаюсь запустить url по умолчанию.Обработка запросов клиентов на http-сервере

Сначала я получаю «/» как хороший запрос, но затем получаю пустой запрос, который является недопустимым запросом, который закрывает соединение. После того, как сервер создает новое соединение, он получает «/css/doremon.css» в качестве запроса. doreomn.css - это файл веб-сайта, который я пытаюсь запустить. Это создаст ошибку в get_file_data, потому что путь должен быть: «C: \ webroot \ css \ doremon.css».

Это вызывает два вопроса: 1. Почему клиент отправляет пустые запросы на сервер? Как я могу предотвратить прерывание соединения? 2. С третьего запроса кажется, что клиент сначала отправляет запрошенный URL-адрес, а затем запрашивает файлы, связанные с ним, есть ли способ получить их все сразу? Если нет, то как я могу исправить путь для запрошенных файлов?

+0

Кажется, что ваши вопросы касаются действий клиента, но вы только демонстрируете код сервера - если клиент отправляет запросы, которые вы не знаете, как обращаться с ними, почему вы не корректируете свой код сервера для их обработки? – Jmills

+0

Если ваш клиент является веб-браузером (а не 'curl'), вы можете получить http-команду' OPTIONS'. как вы справляетесь с этим? – Ereli

+0

также стоит прочитать о [keep-alive] (https://en.wikipedia.org/wiki/HTTP_persistent_connection) и о том, как HTTP-соединения повторно используются. здесь также http://stackoverflow.com/a/20799796/1265980 – Ereli

ответ

2

Да. Обычно веб-серверы имеют понятие «веб-корневой» путь, по которому выполняются все запросы. Но это понятие, которое создается и применяется веб-сервером. Итак, давайте назовем этот путь WebRoot (похоже, это то, что вам нужно C:\webroot\).

Обычно URL по умолчанию, то WebRoot/index.html». И если есть что-то еще называют этой страницы (например,„/css/doremon.css“), клиент запрашивает этот ресурс (GET /css/doremon.css ...), и сервер отвечает содержимым 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, то вероятность того, что клиент фактически не посылает пустые запросы. Однако вы не обрабатываете «конец файла» правильно. Ваша основная петля должна быть что-то вроде:

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-сохранить в живых. Это механизм, позволяющий сохранить соединение открытым, но он будет использоваться только в том случае, если обе стороны согласятся сделать это. Если вы хотите поддержать это, вам необходимо рекламировать его в заголовках, которые вы возвращаете, как описано в ссылке. Если вы делаете не, рекламируйте их, тогда HTTP принимает один запрос на соединение. Следовательно, закрытое соединение - это правильное поведение здесь, пока вы не предоставите заголовок Connection: keep-alive (и все равно всегда является допустимым поведением). И вы интерпретируете это закрытое соединение (которое сигнализируется пустым буфером, возвращаемым с recv) как «пустой запрос».

И наконец, если вы/do справитесь с сохранением жизни, вам нужно будет выполнить более тщательную работу по разбору заголовков. HTTP-запрос - это одна строка (обычно содержит GET, но есть и другие глаголы), за которой следует неопределенное количество дополнительных строк заголовка, за которым следует пустая строка, а затем, возможно, дополнительные данные (дополнительные данные отсутствуют для GET, но может быть для PUT и POST).

Теперь все эти линии могут не быть поставлены с одним вызовом recv. Следовательно, вам нужно продолжать получать данные, пока не найдете пустую строку, сигнализирующую о конце запроса (или вы могли бы - и, вероятно, должны - наложить какой-то произвольный лимит, чтобы защитить себя от злоумышленника, пытающегося использовать всю вашу память подавая вам заголовки навсегда).

Кроме того, вы должны знать, что вы могли бы получить еще, чем один запрос от одного recv. Например, как только вы рекламируете keep-alive, клиент может отправить два запроса назад (так называемая конвейерная обработка). Они могут отображаться в вашем буфере через один прием. Поэтому важно помнить, что для HTTP вы должны анализировать строки, а не просто обрабатывать полный буфер за раз.

+1

Отличный ответ. Может быть интересно добавить проверку на запросы типа '/../ mySecretFile' (эти точки также могут быть закодированы). –