【问题标题】:When opening my web server the browser says ERR_INVALID_HTTP_RESPONSE打开我的网络服务器时,浏览器显示 ERR_INVALID_HTTP_RESPONSE
【发布时间】:2021-09-19 04:32:25
【问题描述】:

我试图在端口 4460 上创建一个 Web 服务器,但是当我在浏览器中键入“http://127.0.0.1:4460/”时 浏览器地址栏显示 ERR_INVALID_HTTP_RESONSE。(谷歌浏览器)。浏览器是最新版本。
代码没有引发任何错误,也没有发送任何 bad_gateway 请求。它没有访问 .ico 文件。
Python版本:3.8.10
我的代码:

import socket
from socket import AF_INET,SOCK_STREAM
from threading import Lock
from pprint import pprint
from threadtools import threaded
from email.utils import format_datetime as fmd
import datetime
from deflate import gzip_compress
ms = (lambda x:x/1000)
socket.setdefaulttimeout(ms(700))
ol = Lock()
plok = Lock()
ENCODINGS = "utf-8 utf-16 cp936 latin-1".split()
response_header = b"""\
200 OK
Content-Type: text/html
Content-Length: $$CTXLN$$
Content-Encoding: gzip
Connection: close
Date: $$DATE$$
Keep-Alive: timeout=2, max=2
$$CTX$$"""
bad_gateway = b"""\
502 Bad Gateway
Content-type:text/html
Content-legth:0"""
def decode(x,verbose=False):
    for enc in ENCODINGS:
        flag = False
        try:
            return x.decode(enc)
        except:
            flag = True
        finally:
            print("Decoded in:"+enc) if(not flag)and verbose else None
    return ""
def startswithany(a,lis):
    for x in lis:
        if a.startswith(x):
            return True
    return False
def is_newline(x):
    return x in ("\r\n","\n")
def load_rsrc(acpt):
    if "text/html" in acpt or "text/*" in acpt or "*/*" in acpt:
        return open("response.html","rb").read()
    elif "image/ico" in acpt or "image/*" in acpt:
        return open("response.ico","rb").read()
    else:
        return b""
def handle_connection(cnct,addr):
    global pending
    with plok:
        pending += 1
    try:
        if pending > 20:#Too many connections!!!
            cnct.send(bad_gateway)
        with ol:
            print(f"----------------\nConnection from:{addr}\n")
        has_ln = True
        data = b""
        ctx = ""
        headers = {"Unknown-Lines":[]}
        while True:
            data = b""
            while not decode(data).endswith("\n"):#\r\n ends with \n
                try:
                    data += cnct.recv(1)
                except:#timeout
                    has_ln = False
                    break
            if not has_ln:
                break
            assert len(data)
            data = decode(data).strip(" ")
            assert not data.startswith(" ")
            if is_newline(data):#empty line
                continue
            if startswithany(data,("GET","POST","PUT")):
                headers["Request-Type"] = data.strip()
            else:
                dsp = data.split(":",1)
                if len(dsp)!=2:
                    print(f"Unknown header:{data}")
                    headers["Unknown-Lines"].append(data)
                else:
                    a,b = data.split(":",1)
                    b = b.strip()
                    headers[a] = b
        with ol:
            print(f"Headers:")
            for k,v in headers.items():
                print(f"{k}:{v}")
        accept = headers.get("Accept","text/html")
        accept = accept.split(",")
        q = []
        for i,x in enumerate(accept):
            if ";q=" in x:
                a,b = x.split(";q=")
                b = float(b)
                accept[i] = a
                q.append(b)
        rt = tuple(map(str.strip,headers.get("Request-Type","GET/HTTP/1.0").split("/")))
        req = rt[0]#GET/POST/PUT
        protocol = rt[1]#HTTP;NO SECURE SERVER FOR NOW
        ver = rt[2]#version
        assert ver in ("1.0","1.1")
        now = datetime.datetime.now(datetime.timezone.utc)
        datestr = fmd(now,True).encode()
        ctx = load_rsrc(accept)
        ln = str(len(ctx)+1).encode()
        response = response_header.replace(b"$$CTXLN$$",ln)\
                                  .replace(b"$$CTX$$",ctx)\
                                  .replace(b"$$DATE$$",datestr)
        response_cmpr = gzip_compress(response)
        cnct.send(response_cmpr)
        print("Sent:")
        print(response.decode())
        if headers.get("Connection","Keep-alive") == "Keep-alive":
            import time
            time.sleep(2)
    finally:
        cnct.close()
        with plok:
            pending -= 1
skt = socket.socket(AF_INET,SOCK_STREAM)
skt.bind(("",4460))
skt.listen(3)
skt.settimeout(None)
pending = 0
while True:
    cn,ad = skt.accept()
    handle_connection(cn,ad)

【问题讨论】:

  • 为什么你使用低级接口作为 HTTP 服务器? Flask 可以用来解决你的问题吗?还是您想坚持使用 Python 标准库?看来你正在重新发明轮子......
  • 我只是想练习我的编码技巧
  • 仍然无法正常工作。如果我使用端口 80 并访问 127.0.0.1/,the 浏览器将继续加载并且程序不会打印任何内容。(我使用 Microsoft Edge 但没有运气)

标签: python python-3.x server python-3.8


【解决方案1】:

你已经接近你的目标了。稍作调整我让你的 sn-p 工作了。主要问题在于 HTTP 响应格式,它应该定义如下:

HTTP/1.1 200 OK                 <--- Missing HTTP/1.1 prefix
Content-Type: text/html
...
Keep-Alive: timeout=2, max=2
                                <--- Mind the extra newline here which is mandatory
$$CTX$$                         <--- Browser will expect HTML here

我已经调整了您提供的 MCVE,请在下面找到适用于最新 Edge 和 Firefox 浏览器的工作版本。

import socket
from socket import AF_INET,SOCK_STREAM
from threading import Lock
from pprint import pprint
#from threadtools import threaded
from email.utils import format_datetime as fmd
import datetime
#from deflate import gzip_compress

ms = (lambda x:x/1000)
socket.setdefaulttimeout(ms(700))
ol = Lock()
plok = Lock()
ENCODINGS = "utf-8 utf-16 cp936 latin-1".split()

response_header = b"""\
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: $$CTXLN$$
Connection: close
Date: $$DATE$$
Keep-Alive: timeout=2, max=2

$$CTX$$"""
# Missing HTTP/1.1 Prefix
# The extra new line is required

bad_gateway = b"""\
HTTP/1.1 502 Bad Gateway
Content-type:text/html
Content-legth:0
"""

def decode(x,verbose=False):
    for enc in ENCODINGS:
        flag = False
        try:
            return x.decode(enc)
        except:
            flag = True
        finally:
            print("Decoded in:"+enc) if(not flag)and verbose else None
    return ""

def startswithany(a,lis):
    for x in lis:
        if a.startswith(x):
            return True
    return False

def is_newline(x):
    return x in ("\r\n","\n")

def load_rsrc(acpt):
    if "text/html" in acpt or "text/*" in acpt or "*/*" in acpt:
        #return open("response.html","rb").read()
        return b"hello"
    elif "image/ico" in acpt or "image/*" in acpt:
        return b"icon"
    else:
        return b""

def handle_connection(cnct,addr):
    global pending
    with plok:
        pending += 1
    try:
        if pending > 20:#Too many connections!!!
            cnct.send(bad_gateway)
        with ol:
            print(f"----------------\nConnection from:{addr}\n")
        has_ln = True
        data = b""
        ctx = ""
        headers = {"Unknown-Lines":[]}
        while True:
            data = b""
            while not decode(data).endswith("\n"):#\r\n ends with \n
                try:
                    data += cnct.recv(1)
                except:#timeout
                    has_ln = False
                    break
            if not has_ln:
                break
            assert len(data)
            data = decode(data).strip(" ")
            assert not data.startswith(" ")
            if is_newline(data):#empty line
                continue
            if startswithany(data,("GET","POST","PUT")):
                headers["Request-Type"] = data.strip()
            else:
                dsp = data.split(":",1)
                if len(dsp)!=2:
                    print(f"Unknown header:{data}")
                    headers["Unknown-Lines"].append(data)
                else:
                    a,b = data.split(":",1)
                    b = b.strip()
                    headers[a] = b
        with ol:
            print(f"Headers:")
            for k,v in headers.items():
                print(f"{k}:{v}")
        accept = headers.get("Accept","text/html")
        accept = accept.split(",")
        q = []
        for i,x in enumerate(accept):
            if ";q=" in x:
                a,b = x.split(";q=")
                b = float(b)
                accept[i] = a
                q.append(b)
        rt = tuple(map(str.strip,headers.get("Request-Type","GET/HTTP/1.0").split("/")))
        req = rt[0]#GET/POST/PUT
        protocol = rt[1]#HTTP;NO SECURE SERVER FOR NOW
        ver = rt[2]#version
        assert ver in ("1.0","1.1")
        now = datetime.datetime.now(datetime.timezone.utc)
        datestr = fmd(now,True).encode()
        ctx = load_rsrc(accept)
        ln = str(len(ctx)+1).encode()
        response = response_header.replace(b"$$CTXLN$$",ln)\
                                  .replace(b"$$CTX$$",ctx)\
                                  .replace(b"$$DATE$$",datestr)
        #response_cmpr = gzip_compress(response)
        cnct.send(response)
        print("Sent:")
        print(response.decode())
        if headers.get("Connection","Keep-alive") == "Keep-alive":
            import time
            time.sleep(2)
    finally:
        cnct.close()
        with plok:
            pending -= 1

skt = socket.socket(AF_INET,SOCK_STREAM)
skt.bind(("",8080))
skt.listen(3)
skt.settimeout(None)
pending = 0

while True:
    cn,ad = skt.accept()
    handle_connection(cn,ad)
    

【讨论】:

  • Wait--尝试使用80端口时浏览器无法连接(连接超时)并调整为skt.bind(("",80))。此外,修改后的程序在使用 8080 端口时仍然会导致 ERR_HTTP_INVALID_RESPONSE。没有引发异常。
  • 本sn-p中端口为8080
  • 为什么我不能使用 80 端口?
  • > 另外,修改后的程序在使用 8080 端口时仍然会导致 ERR_HTTP_INVALID_RESPONSE。没有引发异常。
  • 你确定你正在运行我发送的那段代码(复制粘贴到一个新的文件中)。您的程序不正常存在,因此首先确保您已停止以前的版本并正在运行此版本。如果您仍然有错误,您将需要在您的问题上添加更多上下文,因为它在 python 3.7+ 和主要浏览器上运行。所以问题可能存在于其他地方。你可以使用任何你喜欢的端口,我的测试实例已经使用了 80,因此 8080
猜你喜欢
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 2021-01-21
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多