读取请求正文而不删除它
Read request body without deleting it
所以我有一个 MyHandler,它必须知道请求正文中的内容:
class MyHandler
include HTTP::Handler
def call(context)
p "MyHandler got body: " + context.request.body.not_nil!.gets_to_end
call_next(context)
end
end
server = HTTP::Server.new(42, [MyHandler.new]) do |context|
p "Server got body: " + context.request.body.not_nil!.gets_to_end
end
正如预期的那样,在 MyHandler
读取后,服务器收到一个空主体。如何在不修改原始上下文的情况下复制正文?
Crystal 支持流式请求体,这意味着一旦你在请求中流式传输,IO 就是 EOF,第二个处理程序无法读取任何数据。
解决这个问题的一个简单方法是使用 body_string = context.request.body.try(&.gets_to_end)
检索整个内容,然后使用 context.request.body = body_string
将请求正文设置为返回的字符串。这会将整个主体缓冲到内存中,然后将主体设置为存储在内存中的缓冲区。这种方法的缺点是攻击者可以发送无限大小的请求正文并吃掉服务器上的所有内存,从而导致 DOS 攻击。另一个缺点是,如果您正在处理二进制数据,则需要使用 #to_slice
将字符串转换为切片才能使用它。
解决 DOS 攻击问题的一种方法 - 如果您考虑了最大主体大小 - 如果主体太大则请求失败:
if body = context.request.body
body_io = IO::Memory.new
bytes_read = IO.copy(body, body_io, limit: 1_048_576) # 1GiB limit
body_io.rewind
if bytes_read == 1_048_576
# Fail request
end
# use body_io
body_io.rewind # Reset body_io to start
context.request.body = body_io
end
如果您需要接受一个无限大小的主体,而不是将其缓冲到内存中,您应该创建一个自定义 IO
实现,它包装现有的主体 IO
并在 IO
中运行所需的转换 IO#read(Bytes)
。这个方法比较复杂,前面的方法几乎涵盖了所有情况,所以我就不提供这个选项的代码示例了。
所以我有一个 MyHandler,它必须知道请求正文中的内容:
class MyHandler
include HTTP::Handler
def call(context)
p "MyHandler got body: " + context.request.body.not_nil!.gets_to_end
call_next(context)
end
end
server = HTTP::Server.new(42, [MyHandler.new]) do |context|
p "Server got body: " + context.request.body.not_nil!.gets_to_end
end
正如预期的那样,在 MyHandler
读取后,服务器收到一个空主体。如何在不修改原始上下文的情况下复制正文?
Crystal 支持流式请求体,这意味着一旦你在请求中流式传输,IO 就是 EOF,第二个处理程序无法读取任何数据。
解决这个问题的一个简单方法是使用 body_string = context.request.body.try(&.gets_to_end)
检索整个内容,然后使用 context.request.body = body_string
将请求正文设置为返回的字符串。这会将整个主体缓冲到内存中,然后将主体设置为存储在内存中的缓冲区。这种方法的缺点是攻击者可以发送无限大小的请求正文并吃掉服务器上的所有内存,从而导致 DOS 攻击。另一个缺点是,如果您正在处理二进制数据,则需要使用 #to_slice
将字符串转换为切片才能使用它。
解决 DOS 攻击问题的一种方法 - 如果您考虑了最大主体大小 - 如果主体太大则请求失败:
if body = context.request.body
body_io = IO::Memory.new
bytes_read = IO.copy(body, body_io, limit: 1_048_576) # 1GiB limit
body_io.rewind
if bytes_read == 1_048_576
# Fail request
end
# use body_io
body_io.rewind # Reset body_io to start
context.request.body = body_io
end
如果您需要接受一个无限大小的主体,而不是将其缓冲到内存中,您应该创建一个自定义 IO
实现,它包装现有的主体 IO
并在 IO
中运行所需的转换 IO#read(Bytes)
。这个方法比较复杂,前面的方法几乎涵盖了所有情况,所以我就不提供这个选项的代码示例了。