Reputation: 550
So I have a MyHandler which has to know what's inside the request body:
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
As expected, after MyHandler
has read, server receives a empty body. How can copy the body without modifying original context?
Upvotes: 3
Views: 689
Reputation: 3175
Crystal supports streaming request bodies, which means that once you stream in the request, the IO is EOF and the second handler can't read any data.
A simple way to solve this would be to retrieve the entire content using body_string = context.request.body.try(&.gets_to_end)
, then set the request body to the returned string using context.request.body = body_string
. This buffers the entire body to memory then sets the body to the buffer stored in memory. The downside to this approach is that an attacker can send an infinitely sized request body and eat all the memory on your server, causing a DOS attack. Another disadvantage is that if you're working with binary data, you would then need to convert the string into a slice using #to_slice
to work with it.
One way to solve the DOS attack problem - if you have a maximum body size in mind - is to fail the request if the body is too large:
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
If you need to accept an infinitely sized body, and not buffer it to memory, you should create a custom IO
implementation which wraps the existing body IO
and runs the required transform inside IO#read(Bytes)
. This method is quite complex, and the previous method covers almost all situations, so I won't provide a code sample for this option.
Upvotes: 9