wsgiref — WSGI 实用程序和参考实现 — Python 文档

来自菜鸟教程
Python/docs/3.7/library/wsgiref
跳转至:导航、​搜索

wsgiref — WSGI 实用程序和参考实现


Web 服务器网关接口 (WSGI) 是 Web 服务器软件和用 Python 编写的 Web 应用程序之间的标准接口。 拥有标准接口可以轻松使用支持 WSGI 的应用程序与许多不同的 Web 服务器。

只有 Web 服务器和编程框架的作者需要知道 WSGI 设计的每一个细节和角落案例。 您不需要为了安装 WSGI 应用程序或使用现有框架编写 Web 应用程序而了解 WSGI 的每个细节。

wsgiref 是 WSGI 规范的参考实现,可用于向 Web 服务器或框架添加 WSGI 支持。 它提供了用于操作 WSGI 环境变量和响应头的实用程序、用于实现 WSGI 服务器的基类、一个为 WSGI 应用程序提供服务的演示 HTTP 服务器,以及一个用于检查 WSGI 服务器和应用程序是否符合 WSGI 规范的验证工具([X293X ]PEP 3333)。

有关 WSGI 的更多信息以及教程和其他资源的链接,请参阅 wsgi.readthedocs.io

wsgiref.util – WSGI 环境实用程序

该模块提供了多种用于处理 WSGI 环境的实用函数。 WSGI 环境是一个包含 HTTP 请求变量的字典,如 PEP 3333 中所述。 所有采用 environ 参数的函数都期望提供符合 WSGI 的字典; 详细规格请参见 PEP 3333

wsgiref.util.guess_scheme(environ)

通过检查 environ 字典中的 HTTPS 环境变量,返回对 wsgi.url_scheme 应该是“http”还是“https”的猜测。 返回值是一个字符串。

在创建包装 CGI 或类似 CGI 的协议(如 FastCGI)的网关时,此功能很有用。 通常,提供此类协议的服务器将包含一个 HTTPS 变量,当通过 SSL 接收请求时,该变量值为“1”“是”或“on”。 因此,如果找到这样的值,此函数将返回“https”,否则返回“http”。

wsgiref.util.request_uri(environ, include_query=True)
使用 PEP 3333 的“URL 重建”部分中找到的算法,返回完整的请求 URI,可以选择包括查询字符串。 如果 include_query 为 false,则查询字符串不包含在结果 URI 中。
wsgiref.util.application_uri(environ)
类似于 request_uri(),除了 PATH_INFOQUERY_STRING 变量被忽略。 结果是请求寻址的应用程序对象的基本 URI。
wsgiref.util.shift_path_info(environ)

将单个名称从 PATH_INFO 移至 SCRIPT_NAME 并返回名称。 environ 字典是 modified 就地; 如果您需要保持原件 PATH_INFOSCRIPT_NAME 完整,请使用副本。

如果PATH_INFO中没有剩余路径段,则返回None

通常,此例程用于处理请求 URI 路径的每个部分,例如将路径视为一系列字典键。 此例程修改传入的环境,使其适合调用位于目标 URI 的另一个 WSGI 应用程序。 例如,如果/foo处有一个WSGI应用,请求URI路径为/foo/bar/baz/foo处的WSGI应用调用shift_path_info() ],它将接收字符串“bar”,并且环境将被更新以适合传递到 /foo/bar 处的 WSGI 应用程序。 即SCRIPT_NAME将从/foo变为/foo/barPATH_INFO/bar/baz变为/baz

PATH_INFO 只是一个“/”时,该例程返回一个空字符串并在 SCRIPT_NAME 后面附加一个斜杠,即使空路径段通常会被忽略,而 SCRIPT_NAME 不会'通常不会以斜线结尾。 这是有意的行为,以确保应用程序可以在使用此例程进行对象遍历时区分以 /x 结尾的 URI 和以 /x/ 结尾的 URI 之间的区别。

wsgiref.util.setup_testing_defaults(environ)

更新 environ 为测试目的使用微不足道的默认值。

该例程添加了WSGI所需的各种参数,包括HTTP_HOSTSERVER_NAMESERVER_PORTREQUEST_METHODSCRIPT_NAMEPATH_INFO ],以及所有 PEP 3333 定义的 wsgi.* 变量。 它仅提供默认值,并不替换这些变量的任何现有设置。

此例程旨在使 WSGI 服务器和应用程序的单元测试更容易设置虚拟环境。 它不应该被实际的 WSGI 服务器或应用程序使用,因为数据是假的!

用法示例:

from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server

# A relatively simple WSGI application. It's going to print out the
# environment dictionary after being updated by setup_testing_defaults
def simple_app(environ, start_response):
    setup_testing_defaults(environ)

    status = '200 OK'
    headers = [('Content-type', 'text/plain; charset=utf-8')]

    start_response(status, headers)

    ret = [("%s: %s\n" % (key, value)).encode("utf-8")
           for key, value in environ.items()]
    return ret

with make_server('', 8000, simple_app) as httpd:
    print("Serving on port 8000...")
    httpd.serve_forever()

除了上述环境功能外,wsgiref.util 模块还提供了这些杂项实用程序:

wsgiref.util.is_hop_by_hop(header_name)
如果 'header_name' 是 RFC 2616 定义的 HTTP/1.1 “Hop-by-Hop”标头,则返回 True
class wsgiref.util.FileWrapper(filelike, blksize=8192)

将类文件对象转换为 迭代器 的包装器。 生成的对象同时支持 [X35X] 和 __iter__() 迭代样式,以兼容 Python 2.1 和 Jython。 随着对象的迭代,可选的 blksize 参数将重复传递给 filelike 对象的 read() 方法,以获取要生成的字节串。 当 read() 返回空字节串时,迭代结束且不可恢复。

如果 filelikeclose() 方法,则返回的对象也会有 close() 方法,并且会调用 filelike 对象的 [ X154X] 方法调用时。

用法示例:

from io import StringIO
from wsgiref.util import FileWrapper

# We're using a StringIO-buffer for as the file-like object
filelike = StringIO("This is an example file-like object"*10)
wrapper = FileWrapper(filelike, blksize=5)

for chunk in wrapper:
    print(chunk)


wsgiref.headers – WSGI 响应头工具

该模块提供了一个单一的类,Headers,用于使用类似映射的接口方便地操作 WSGI 响应头。

class wsgiref.headers.Headers([headers])

创建一个类似映射的对象包装 headers,它必须是 PEP 3333 中描述的头名称/值元组列表。 headers 的默认值是一个空列表。

Headers 对象支持典型的映射操作,包括 __getitem__()get()__setitem__()setdefault()__delitem__()__contains__()。 对于这些方法中的每一个,键是标头名称(不区分大小写),值是与该标头名称关联的第一个值。 设置标头会删除该标头的任何现有值,然后在包装标头列表的末尾添加一个新值。 标头的现有顺序通常保持不变,新标头添加到包装列表的末尾。

与字典不同,Headers 对象在您尝试获取或删除不在包装标头列表中的键时不会引发错误。 获取一个不存在的标头只会返回 None,而删除一个不存在的标头什么也不做。

Headers 对象还支持 [X38X]、values()items() 方法。 如果存在多值标头,则 keys()items() 返回的列表可以多次包含相同的键。 Headers 对象的 len() 与它的 items() 的长度相同,也就是包裹头列表的长度。 事实上,items() 方法只是返回一个被包装的头列表的副本。

Headers 对象上调用 bytes() 会返回一个适合作为 HTTP 响应头传输的格式化字节串。 每个标题都与它的值放在一行上,用冒号和空格分隔。 每行以回车和换行结束,字节串以空行结束。

Headers 对象除了具有映射接口和格式化功能外,还具有以下方法用于查询和添加多值标头,以及添加带有 MIME 参数的标头:

get_all(name)

返回命名标头的所有值的列表。

返回的列表将按照它们出现在原始标题列表中或添加到此实例中的顺序进行排序,并且可能包含重复项。 任何删除和重新插入的字段总是附加到标题列表中。 如果不存在具有给定名称的字段,则返回一个空列表。

add_header(name, value, **_params)

添加一个(可能是多值的)标头,带有通过关键字参数指定的可选 MIME 参数。

name 是要添加的头字段。 关键字参数可用于为标头字段设置 MIME 参数。 每个参数必须是字符串或 None。 参数名称中的下划线被转换为破折号,因为破折号在 Python 标识符中是非法的,但许多 MIME 参数名称都包含破折号。 如果参数值为字符串,则以 name="value" 形式添加到头值参数中。 如果是None,则只添加参数名。 (这用于没有值的 MIME 参数。)示例用法:

h.add_header('content-disposition', 'attachment', filename='bud.gif')

上面将添加一个如下所示的标题:

Content-Disposition: attachment; filename="bud.gif"

3.5 版变更:headers 参数可选。


wsgiref.simple_server – 一个简单的 WSGI HTTP 服务器

该模块实现了一个简单的 HTTP 服务器(基于 http.server),为 WSGI 应用程序提供服务。 每个服务器实例在给定的主机和端口上为单个 WSGI 应用程序提供服务。 如果您想在单个主机和端口上为多个应用程序提供服务,您应该创建一个 WSGI 应用程序来解析 PATH_INFO 以选择要为每个请求调用哪个应用程序。 (例如,使用 wsgiref.util 中的 shift_path_info() 函数。)

wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)

创建一个新的 WSGI 服务器,监听 hostport,接受 app 的连接。 返回值是提供的 server_class 的实例,并将使用指定的 handler_class 处理请求。 app 必须是 WSGI 应用程序对象,如 PEP 3333 所定义。

用法示例:

from wsgiref.simple_server import make_server, demo_app

with make_server('', 8000, demo_app) as httpd:
    print("Serving HTTP on port 8000...")

    # Respond to requests until process is killed
    httpd.serve_forever()

    # Alternative: serve one request, then exit
    httpd.handle_request()
wsgiref.simple_server.demo_app(environ, start_response)
这个函数是一个小而完整的 WSGI 应用程序,它返回一个包含消息“Hello world!”的文本页面。 以及 environ 参数中提供的键/值对列表。 这对于验证 WSGI 服务器(例如 wsgiref.simple_server)是否能够正确运行简单的 WSGI 应用程序很有用。
class wsgiref.simple_server.WSGIServer(server_address, RequestHandlerClass)

创建一个 WSGIServer 实例。 server_address 应该是一个 (host,port) 元组,而 RequestHandlerClass 应该是将用于处理请求的 http.server.BaseHTTPRequestHandler 的子类。

您通常不需要调用此构造函数,因为 make_server() 函数可以为您处理所有细节。

WSGIServerhttp.server.HTTPServer的子类,所以它的所有方法(如serve_forever()handle_request())都可用。 WSGIServer 还提供了这些 WSGI 特定的方法:

set_app(application)

将可调用的 application 设置为将接收请求的 WSGI 应用程序。

get_app()

返回当前设置的应用程序可调用。

但是,通常情况下,您不需要使用这些附加方法,因为 set_app() 通常由 make_server() 调用,并且 get_app() 存在主要是为了请求处理程序实例的好处。

class wsgiref.simple_server.WSGIRequestHandler(request, client_address, server)

为给定的 request 创建一个 HTTP 处理程序(即 一个套接字)、client_address(一个 (host,port) 元组)和 serverWSGIServer 实例)。

您不需要直接创建此类的实例; 它们由 WSGIServer 对象根据需要自动创建。 但是,您可以将此类子类化,并将其作为 handler_class 提供给 make_server() 函数。 覆盖子类的一些可能相关的方法:

get_environ()

返回一个包含请求的 WSGI 环境的字典。 默认实现复制 WSGIServer 对象的 base_environ 字典属性的内容,然后添加从 HTTP 请求派生的各种标头。 每次调用此方法都应返回一个新字典,其中包含 PEP 3333 中指定的所有相关 CGI 环境变量。

get_stderr()

返回应该用作 wsgi.errors 流的对象。 默认实现只返回 sys.stderr

handle()

处理 HTTP 请求。 默认实现使用 wsgiref.handlers 类创建处理程序实例来实现实际的 WSGI 应用程序接口。


wsgiref.validate — WSGI 一致性检查器

在创建新的 WSGI 应用程序对象、框架、服务器或中间件时,使用 wsgiref.validate 验证新代码的一致性会很有用。 该模块提供了一个创建 WSGI 应用程序对象的函数,该对象验证 WSGI 服务器或网关与 WSGI 应用程序对象之间的通信,以检查双方的协议一致性。

请注意,此实用程序不保证完全符合 PEP 3333; 该模块没有错误并不一定意味着错误不存在。 但是,如果此模块确实产生错误,则几乎可以肯定服务器或应用程序不符合 100% c。

该模块基于 Ian Bicking 的“Python Paste”库中的 paste.lint 模块。

wsgiref.validate.validator(application)

包装 application 并返回一个新的 WSGI 应用程序对象。 返回的应用程序会将所有请求转发到原始 应用程序 ,并检查 应用程序 和调用它的服务器是否符合 WSGI 规范和 RFC 2616

任何检测到的不符合项都会导致 AssertionError 被引发; 但是请注意,这些错误的处理方式取决于服务器。 例如,wsgiref.simple_server 和其他基于 wsgiref.handlers 的服务器(不会覆盖错误处理方法来做其他事情)只会输出一条消息,错误有发生,并将回溯转储到 sys.stderr 或其他一些错误流。

此包装器还可以使用 warnings 模块生成输出,以指示有问题但实际上可能未被 PEP 3333 禁止的行为。 除非使用 Python 命令行选项或 warnings API 禁止它们,否则任何此类警告都将写入 sys.stderrnot wsgi.errors,除非它们碰巧是同一个对象)。

用法示例:

from wsgiref.validate import validator
from wsgiref.simple_server import make_server

# Our callable object which is intentionally not compliant to the
# standard, so the validator is going to break
def simple_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain')]  # HTTP Headers
    start_response(status, headers)

    # This is going to break because we need to return a list, and
    # the validator is going to inform us
    return b"Hello World"

# This is the application wrapped in a validator
validator_app = validator(simple_app)

with make_server('', 8000, validator_app) as httpd:
    print("Listening on port 8000....")
    httpd.serve_forever()


wsgiref.handlers – 服务器/网关基类

该模块提供用于实现 WSGI 服务器和网关的基本处理程序类。 这些基类处理与 WSGI 应用程序通信的大部分工作,只要它们具有类似 CGI 的环境,以及输入、输出和错误流。

class wsgiref.handlers.CGIHandler

通过 sys.stdinsys.stdoutsys.stderros.environ 基于 CGI 的调用。 当您有一个 WSGI 应用程序并希望将其作为 CGI 脚本运行时,这很有用。 只需调用 CGIHandler().run(app),其中 app 是您希望调用的 WSGI 应用程序对象。

此类是 BaseCGIHandler 的子类,它将 wsgi.run_once 设置为 true,wsgi.multithread 设置为 false,并将 wsgi.multiprocess 设置为 true,并且始终使用 sysos 获取必要的 CGI 流和环境。

class wsgiref.handlers.IISCGIHandler

一个专门的替代品 CGI处理程序 ,用于在 Microsoft 的 IIS Web 服务器上部署时使用,无需设置配置 allowPathInfo 选项 (IIS>=7) 或元数据库 allowPathInfoForScriptMappings (IIS<7)。

默认情况下,IIS 给出了一个 PATH_INFO 复制了前面的 SCRIPT_NAME,这给希望实现路由的 WSGI 应用程序带来了问题。 此处理程序删除任何此类重复的路径。

IIS 可以配置为通过正确的 PATH_INFO,但这会导致另一个错误,即 PATH_TRANSLATED 是错误的。 幸运的是,这个变量很少使用,WSGI 不保证。 但是,在 IIS<7 上,该设置只能在 vhost 级别进行,这会影响所有其他脚本映射,其中许多在暴露于PATH_TRANSLATED漏洞。 出于这个原因,IIS<7 几乎从未与修复程序一起部署。 (即使是 IIS7 也很少使用它,因为它仍然没有 UI。)

CGI 代码无法判断是否设置了该选项,因此提供了一个单独的处理程序类。 它的使用方式与 CGIHandler 相同,即通过调用 IISCGIHandler().run(app),其中 app 是您希望调用的 WSGI 应用程序对象。

3.2 版中的新功能。

class wsgiref.handlers.BaseCGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

CGIHandler 类似,但不使用 sysos 模块,而是明确指定 CGI 环境和 I/O 流。 multithreadmultiprocess 值用于为处理程序实例运行的任何应用程序设置 wsgi.multithreadwsgi.multiprocess 标志。

此类是 SimpleHandler 的子类,旨在与 HTTP“源服务器”以外的软件一起使用。 如果您正在编写使用 Status: 标头发送 HTTP 状态的网关协议实现(例如 CGI、FastCGI、SCGI 等),您可能希望将其子类化而不是 SimpleHandler ]。

class wsgiref.handlers.SimpleHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

类似于 BaseCGIHandler,但设计用于 HTTP 源服务器。 如果您正在编写 HTTP 服务器实现,您可能希望将其子类化而不是 BaseCGIHandler

此类是 BaseHandler 的子类。 它覆盖 __init__()get_stdin()get_stderr()add_cgi_vars()_write()_flush() 方法以支持显式设置环境和流通过构造函数。 提供的环境和流存储在 stdinstdoutstderrenviron 属性中。

stdoutwrite() 方法应该完整地写入每个块,例如 io.BufferedIOBase

class wsgiref.handlers.BaseHandler

这是用于运行 WSGI 应用程序的抽象基类。 每个实例将处理单个 HTTP 请求,但原则上您可以创建一个可重用于多个请求的子类。

BaseHandler 实例只有一种供外部使用的方法:

run(app)

运行指定的 WSGI 应用程序,app

所有其他 BaseHandler 方法在运行应用程序的过程中由该方法调用,因此存在主要是为了允许自定义过程。

以下方法必须在子类中被覆盖:

_write(data)

缓冲字节 data 以传输到客户端。 如果这个方法真正传输数据就可以了; BaseHandler 只是在底层系统实际有这样的区别时,将写入和刷新操作分开以提高效率。

_flush()

强制将缓冲的数据传输到客户端。 如果此方法是空操作(即,如果 _write() 实际发送数据),则可以。

get_stdin()

返回适合用作当前正在处理的请求的 wsgi.input 的输入流对象。

get_stderr()

返回适合用作当前正在处理的请求的 wsgi.errors 的输出流对象。

add_cgi_vars()

将当前请求的 CGI 变量插入到 environ 属性中。

以下是您可能希望覆盖的其他一些方法和属性。 但是,此列表只是一个摘要,并不包括可以覆盖的所有方法。 在尝试创建自定义的 BaseHandler 子类之前,您应该查阅文档字符串和源代码以获取更多信息。

自定义WSGI环境的属性和方法:

wsgi_multithread

用于 wsgi.multithread 环境变量的值。 它在 BaseHandler 中默认为 true,但在其他子类中可能有不同的默认值(或由构造函数设置)。

wsgi_multiprocess

用于 wsgi.multiprocess 环境变量的值。 它在 BaseHandler 中默认为 true,但在其他子类中可能有不同的默认值(或由构造函数设置)。

wsgi_run_once

用于 wsgi.run_once 环境变量的值。 它在 BaseHandler 中默认为 false,但 CGIHandler 默认将其设置为 true。

os_environ

要包含在每个请求的 WSGI 环境中的默认环境变量。 默认情况下,这是导入 wsgiref.handlersos.environ 的副本,但子类可以在类或实例级别创建自己的。 请注意,字典应被视为只读,因为默认值在多个类和实例之间共享。

server_software

如果设置了 origin_server 属性,则该属性的值用于设置默认的 SERVER_SOFTWARE WSGI 环境变量,以及设置 HTTP 响应中的默认 Server: 标头。 对于不是 HTTP 源服务器的处理程序(例如 BaseCGIHandlerCGIHandler),它会被忽略。

3.3 版更改: 术语“Python”被替换为特定于实现的术语,如“CPython”、“Jython”等。

get_scheme()

返回用于当前请求的 URL 方案。 默认实现使用 wsgiref.util 中的 guess_scheme() 函数,根据当前请求的 environ 变量来猜测方案应该是“http”还是“https”。

setup_environ()

environ 属性设置为完全填充的 WSGI 环境。 默认实现使用上述所有方法和属性,加上 get_stdin()get_stderr()add_cgi_vars() 方法和 wsgi_file_wrapper 属性。 只要 origin_server 属性为真值并且设置了 server_software 属性,它还会插入 SERVER_SOFTWARE 键(如果不存在)。

自定义异常处理的方法和属性:

log_exception(exc_info)

在服务器日志中记录 exc_info 元组。 exc_info 是一个 (type, value, traceback) 元组。 默认实现只是将回溯写入请求的 wsgi.errors 流并刷新它。 子类可以覆盖此方法以更改格式或重新定位输出,将回溯邮寄给管理员,或任何其他可能被认为合适的操作。

traceback_limit

默认 log_exception() 方法包含在回溯输出中的最大帧数。 如果是 None,则包括所有帧。

error_output(environ, start_response)

这个方法是一个 WSGI 应用程序,用于为用户生成一个错误页面。 只有在将标头发送到客户端之前发生错误时才会调用它。

此方法可以使用 sys.exc_info() 访问当前错误信息,并且在调用它时应将该信息传递给 start_response(如 [ 的“错误处理”部分所述) X204X]PEP 3333)。

默认实现仅使用 error_statuserror_headerserror_body 属性来生成输出页面。 子类可以覆盖它以产生更多动态错误输出。

但是请注意,从安全角度来看,不建议向任何老用户提供诊断信息; 理想情况下,您应该做一些特殊的事情来启用诊断输出,这就是默认实现不包含任何内容的原因。

error_status

用于错误响应的 HTTP 状态。 这应该是 PEP 3333 中定义的状态字符串; 它默认为 500 代码和消息。

error_headers

用于错误响应的 HTTP 标头。 这应该是 WSGI 响应头列表((name, value) 元组),如 PEP 3333 中所述。 默认列表只是将内容类型设置为 text/plain

error_body

错误响应正文。 这应该是一个 HTTP 响应正文字节串。 它默认为纯文本,“发生服务器错误。 请联系管理员。”

PEP 3333 的“可选平台特定文件处理”功能的方法和属性:

wsgi_file_wrapper

wsgi.file_wrapper 工厂,或 None。 此属性的默认值是 wsgiref.util.FileWrapper 类。

sendfile()

覆盖以实现特定于平台的文件传输。 仅当应用程序的返回值是 wsgi_file_wrapper 属性指定的类的实例时,才会调用此方法。 如果能够成功传输文件,它应该返回一个真值,这样默认的传输代码就不会被执行。 此方法的默认实现仅返回一个假值。

其他方法和属性:

origin_server

如果处理程序的 _write()_flush() 用于直接与客户端通信,而不是通过类似 CGI 的网关协议,则应将此属性设置为真值想要在特殊的 Status: 标头中显示 HTTP 状态。

该属性的默认值在 BaseHandler 中为 true,但在 BaseCGIHandlerCGIHandler 中为 false。

http_version

如果 origin_server 为 true,则此字符串属性用于设置客户端响应集的 HTTP 版本。 默认为 "1.0"

wsgiref.handlers.read_environ()

将 CGI 变量从 os.environ 转码为 PEP 3333 “bytes in unicode”字符串,返回一个新字典。 此函数由 CGIHandlerIISCGIHandler 使用,而不是直接使用 os.environ,这不一定在所有使用 Python 3 的平台和 Web 服务器上都符合 WSGI——特别是, 那些操作系统的实际环境是 Unicode (即 Windows),或者环境是字节的环境,但 Python 用来解码它的系统编码不是 ISO-8859-1(例如 Unix 系统使用 UTF-8)。

如果您正在实现自己的基于 CGI 的处理程序,您可能希望使用此例程而不是直接从 os.environ 复制值。

3.2 版中的新功能。


例子

这是一个有效的“Hello World”WSGI 应用程序:

from wsgiref.simple_server import make_server

# Every WSGI application must have an application object - a callable
# object that accepts two arguments. For that purpose, we're going to
# use a function (note that you're not limited to a function, you can
# use a class for example). The first argument passed to the function
# is a dictionary containing CGI-style environment variables and the
# second variable is the callable object (see PEP 333).
def hello_world_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain; charset=utf-8')]  # HTTP Headers
    start_response(status, headers)

    # The returned object is going to be printed
    return [b"Hello World"]

with make_server('', 8000, hello_world_app) as httpd:
    print("Serving on port 8000...")

    # Serve until process is killed
    httpd.serve_forever()