Python отправляет http ответ - PullRequest
       10

Python отправляет http ответ

4 голосов
/ 07 апреля 2011

Я пытаюсь написать очень простой HTTP-сервер, который отправляет потоковое видео на сервер. Когда клиент подключается, программа get_video (вымышленная) запускается в другом процессе, и его стандартный вывод направляется к нам (в предположении, что get_video отправляет / передает потоковое видео на стандартный вывод). Я использую subprocess.Popen() для этого.

import subprocess, socket

def send_video(sock, programme_id):
    p = subprocess.Popen(["get_video","--pid",programme_id], stdout=subprocess.PIPE)
    sock.send("HTTP/1.1 200 OK\nContent-type: application/octet-stream\n\n")
    while True:
        chunk = p.stdout.read(1024)
        if chunk:
            try:
                sock.send(chunk)
            except Exception:
                pass
        else:
            break

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('',8080))
    s.listen(5)

    while True:
        client, address = s.accept()
        data = client.recv(1024)
        send_video(client, "123456")
        client.close()

if __name__ == "__main__":
    main()

Это едва работает. Если я отправляю HTTP-запрос, используя wget <a href="http://localhost:8080/blah.mp4" rel="nofollow">http://localhost:8080/blah.mp4</a>, все происходит как положено - видео передается в сокет клиента и добавляется в новый файл, blah.mp4.

Однако, если я создаю фиктивную HTML-страницу с <a href="http://localhost:8080/blah/mp4">download</a> и затем пытаюсь «сохранить цель / ссылку как ...» по этой ссылке, программа get_video вызывается дважды. Второй раз, когда видео действительно отправляется на сокет клиента. Должна быть какая-то буферизация.

Обратите внимание на блок try/except в send_video(). Раньше я получал сообщение об ошибке «сломанный канал» (с использованием фиктивного метода HTML-страницы), указывающее на то, что сокет клиента не предназначен для записи. Я положил try/except туда, чтобы попытаться игнорировать его.

Я довольно смущен. HTTP-запросы выглядят одинаково, я не уверен, что мой браузер (Firefox) делает по-другому, чтобы вызвать это.

Есть идеи?

Edit:

Заголовок от wget:

GET /blah.mp4 HTTP/1.0
User-Agent: Wget/1.12 (linux-gnu)
Accept: */*
Host: 192.168.1.2:8080
Connection: Keep-Alive

Заголовок с HTML-страницы-заглушки:

GET /blah.mp4 HTTP/1.1
Host: 192.168.1.2:8080
User-Agent: Mozilla/5.0 (blah) Gecko/blah Firefox/3.6.16
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Сервер python сообщил только по одному о каждом, но Wireshark сообщил о 2 запросах GET для метода фиктивной ссылки html. Я не вижу здесь ничего очевидного ...

1 Ответ

0 голосов
/ 08 апреля 2011

До меня дошло, что, поскольку браузер отправляет GET с последующим FIN, ACK после нажатия на ссылку «сохранить цель как ...» по ссылке, тогда это будет причиной ошибки сломанной трубы. , Когда вы выбираете, где вы хотите сохранить файл и нажимаете кнопку «ОК» в браузере, выдается еще один GET. Происходило то, что я все еще застрял в этой петле while True, когда обнаружил сломанную трубу. Поэтому я продолжал читать из объекта Popen и не мог каждый раз отправлять его клиенту. Как только это закончилось, следующий GET смог продолжить, и тогда передача прошла успешно. Уф.

Короче, рабочий код:

import subprocess, socket

def send_video(sock, programme_id):
    p = subprocess.Popen(["get_video","--pid",programme_id], stdout=subprocess.PIPE)
    sock.send("HTTP/1.1 200 OK\nContent-type: application/octet-stream\n\n")
    while True:
        chunk = p.stdout.read(1024)
        if chunk:
            try:
                sock.send(chunk)
            except socket.error, e:
                sock.close()
                break
        else:
            break

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('',8080))
    s.listen(5)

    while True:
        client, address = s.accept()
        data = client.recv(1024)
        send_video(client, "123456")
        client.close()

if __name__ == "__main__":
    main()

Спасибо.

...