Python ZMQ中的HTTP服务器或如何使用pyzmq处理POST请求?
我正在尝试使用Python ZMQ中的HTTP服务器或如何使用pyzmq处理POST请求?,python,http,zeromq,pyzmq,Python,Http,Zeromq,Pyzmq,我正在尝试使用ZMQ\u流socket创建一个HTTP服务器 当我执行一个简单的POST请求时: POST HTTP/1.1 Host: localhost:5555 Cache-Control: no-cache Postman-Token: 67004be5-56bc-c1a9-847a-7db3195c301d Apples to Oranges! 以下是我如何使用pyzmq处理此问题: context = zmq.Context() socket = context.socket(
ZMQ\u流
socket创建一个HTTP服务器
当我执行一个简单的POST
请求时:
POST HTTP/1.1
Host: localhost:5555
Cache-Control: no-cache
Postman-Token: 67004be5-56bc-c1a9-847a-7db3195c301d
Apples to Oranges!
以下是我如何使用pyzmq处理此问题:
context = zmq.Context()
socket = context.socket(zmq.STREAM)
socket.bind("tcp://*:5555")
while True:
# Get HTTP request
parts = []
id_, msg = socket.recv_multipart() # [id, ''] or [id, http request]
parts.append(id_)
parts.append(msg)
if not msg:
# This is a new connection - this is just the identify frame (throw away id_)
# The body will come next
id_, msg = socket.recv_multipart() # [id, http request]
parts.append(id_)
parts.append(msg)
end = socket.recv_multipart() # [id*, ''] <- some kind of junk?
parts.append(end)
print("%s" % repr(parts))
因此,我明白:
“\x00\x80\x00\x00)”,
是连接的标识。这最初由ZMQ\u STREAM
socket设置。在随后的请求中,它似乎不存在\x00\x80\x00\x00)
再次是身份,这是我们从ZMQ\u流
socket的客户端后续请求中看到的['\x00\x80\x00\x00*','']
那到底代表什么
参考资料:
ord(''))=41
和ord('*')=42
,这是序列中的下一个数字
使用ZMQ_流编写HTTP服务器时,您必须小心,因为这比在建立连接后仅接收一条消息要复杂得多。
问题主要是你不能保证一个请求会完成;身体可能以块的形式到达,可能包含多条信息。您将不得不读取HTTP头并处理接收分块的正文
下面是一个处理来自curl的POST请求的示例:
from traceback import print_exc
import zmq
from tornado.httputil import HTTPHeaders
class BadRequest(Exception):
pass
class ConnectionLost(Exception):
pass
def parse_request(request):
"""Parse a request verp, path, and headers"""
first_line, header_lines = request.split(b'\r\n', 1)
verb, path, proto = first_line.decode('utf8').split()
headers = HTTPHeaders.parse(header_lines.decode('utf8', 'replace'))
return verb, path, headers
def recv_body(socket, headers, chunks, request_id):
"""Receive the body of a request"""
if headers.get('expect', '').lower() == '100-continue':
if 'Content-Length' not in headers:
# Don't support chunked transfer: http://tools.ietf.org/html/rfc2616#section-3.6.1
print("Only support specified-length requests")
socket.send_multipart([
request_id, b'HTTP/1.1 400 (Bad Request)\r\n\r\n',
request_id, b'',
])
msg = 1
while msg != b'':
# flush until new connection
_, msg = socket.recv_multipart()
raise BadRequest("Only support specified-length requests")
socket.send_multipart([request_id, b'HTTP/1.1 100 (Continue)\r\n\r\n'], zmq.SNDMORE)
content_length = int(headers['Content-Length'])
print("Waiting to receive %ikB body" )
while sum(len(chunk) for chunk in chunks) < content_length:
id_, msg = socket.recv_multipart()
if msg == b'':
raise ConnectionLost("Disconnected")
if id_ != request_id:
raise ConnectionLost("Received data from wrong ID: %s != %s" % (id_, request_id))
chunks.append(msg)
return b''.join(chunks)
print(zmq.__version__, zmq.zmq_version())
socket = zmq.Context().socket(zmq.STREAM)
socket.bind("tcp://*:5555")
while True:
# Get HTTP request
request_id, msg = socket.recv_multipart()
if msg == b'':
continue
chunks = []
try:
request, first_chunk = msg.split(b'\r\n\r\n', 1)
if first_chunk:
chunks.append(first_chunk)
verb, path, headers = parse_request(request)
print(verb, path)
print("Headers:")
for key, value in headers.items():
print(' %s: %s' % (key, value))
body = recv_body(socket, headers, chunks, request_id)
print("Body: %r" % body)
except BadRequest as e:
print("Bad Request: %s" % e)
except ConnectionLost as e:
print("Connection Lost: %s" % e)
except Exception:
print("Failed to handle request", msg)
print_exc()
socket.send_multipart([
request_id, b'HTTP/1.1 500 (OK)\r\n\r\n',
request_id, b''])
else:
socket.send_multipart([
request_id, b'HTTP/1.1 200 (OK)\r\n\r\n',
request_id, b''])
来自回溯导入打印\u exc
导入zmq
从tornado.httputil导入HTTPHeaders
类请求(异常):
通过
类ConnectionLost(异常):
通过
def解析_请求(请求):
“”“分析请求的verp、路径和标头”“”
第一行,标题行=request.split(b'\r\n',1)
动词,路径,proto=first_line.decode('utf8').split()
headers=HTTPHeaders.parse(header\u line.decode('utf8','replace'))
返回动词、路径、标题
def recv_正文(套接字、头、块、请求id):
“”“接收请求的正文”“”
if headers.get('expect','').lower()='100 continue':
如果“内容长度”不在标题中:
#不支持分块传输:http://tools.ietf.org/html/rfc2616#section-3.6.1
打印(“仅支持指定长度的请求”)
socket.send\u多部分([
请求id,b'HTTP/1.1 400(错误请求)\r\n\r\n',
请求\u id,b“”,
])
msg=1
而味精!=b':
#冲洗至新连接
_,msg=socket.recv_multipart()
raise BADDREQUEST(“仅支持指定长度的请求”)
socket.send\u multipart([request\u id,b'HTTP/1.1100(Continue)\r\n\r\n'],zmq.SNDMORE)
content_length=int(标题['content-length'])
打印(“等待接收%ikB正文”)
而sum(len(chunk)表示块中的块)
这种情况下的相关逻辑在recv_body
方法中,该方法读取头并继续recv body的块,直到完成
坦率地说,我认为使用ZMQ_流在Python中编写HTTP服务器没有多大意义。您可以将zmq套接字与现有的PythonEventLoops集成,并重用已经建立的HTTP库,因此您不必重新发明这个特殊的轮子。例如,pyzmq与tornado eventloop配合得特别好,您可以在同一个应用程序中同时使用zmq套接字和tornado http处理程序。此列表来自何处?这似乎不是文章的一部分。你能发布代码复制吗?@SlaterTyranus抱歉!发布了相关的信息。我只是将所有消息部分聚合到一个列表中。我发现了一个例子,这是在zeromq系统中集成http端点的好方法吗?是的,这正是那个例子想要说明的——使用tornado和zmq一起构建一个web应用程序。但它更复杂,不是吗?多个客户端意味着您可能会从每个交错的客户端接收部分。
from traceback import print_exc
import zmq
from tornado.httputil import HTTPHeaders
class BadRequest(Exception):
pass
class ConnectionLost(Exception):
pass
def parse_request(request):
"""Parse a request verp, path, and headers"""
first_line, header_lines = request.split(b'\r\n', 1)
verb, path, proto = first_line.decode('utf8').split()
headers = HTTPHeaders.parse(header_lines.decode('utf8', 'replace'))
return verb, path, headers
def recv_body(socket, headers, chunks, request_id):
"""Receive the body of a request"""
if headers.get('expect', '').lower() == '100-continue':
if 'Content-Length' not in headers:
# Don't support chunked transfer: http://tools.ietf.org/html/rfc2616#section-3.6.1
print("Only support specified-length requests")
socket.send_multipart([
request_id, b'HTTP/1.1 400 (Bad Request)\r\n\r\n',
request_id, b'',
])
msg = 1
while msg != b'':
# flush until new connection
_, msg = socket.recv_multipart()
raise BadRequest("Only support specified-length requests")
socket.send_multipart([request_id, b'HTTP/1.1 100 (Continue)\r\n\r\n'], zmq.SNDMORE)
content_length = int(headers['Content-Length'])
print("Waiting to receive %ikB body" )
while sum(len(chunk) for chunk in chunks) < content_length:
id_, msg = socket.recv_multipart()
if msg == b'':
raise ConnectionLost("Disconnected")
if id_ != request_id:
raise ConnectionLost("Received data from wrong ID: %s != %s" % (id_, request_id))
chunks.append(msg)
return b''.join(chunks)
print(zmq.__version__, zmq.zmq_version())
socket = zmq.Context().socket(zmq.STREAM)
socket.bind("tcp://*:5555")
while True:
# Get HTTP request
request_id, msg = socket.recv_multipart()
if msg == b'':
continue
chunks = []
try:
request, first_chunk = msg.split(b'\r\n\r\n', 1)
if first_chunk:
chunks.append(first_chunk)
verb, path, headers = parse_request(request)
print(verb, path)
print("Headers:")
for key, value in headers.items():
print(' %s: %s' % (key, value))
body = recv_body(socket, headers, chunks, request_id)
print("Body: %r" % body)
except BadRequest as e:
print("Bad Request: %s" % e)
except ConnectionLost as e:
print("Connection Lost: %s" % e)
except Exception:
print("Failed to handle request", msg)
print_exc()
socket.send_multipart([
request_id, b'HTTP/1.1 500 (OK)\r\n\r\n',
request_id, b''])
else:
socket.send_multipart([
request_id, b'HTTP/1.1 200 (OK)\r\n\r\n',
request_id, b''])