Reputation: 2984
I am writing a mock server in Ruby to test our internal API. I'd like to include a body of POST request in server response. I use Net::HTTP::Server and Net::HTTP class. Right now mock server looks like this:
require 'net/http/server'
require 'pp'
require 'json'
Net::HTTP::Server.run(:port => 8080) do |request,stream|
pp request
[200, {'Content-Type' => 'text/html'}, [request[:body]]]
end
Script that sends POST request with body is this:
require 'net/http'
require 'uri'
url = URI.parse(http://localhost:8080/)
x = {"country" => "Ukraine", "city" => 'Kiev'}
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url.path)
request.set_form_data(x)
response = http.request(request)
puts "Request body: #{request.body}"
puts "Response Body: #{response.body}"
However server log indicates that POST request did not contain body:
#Output from server
{:method=>"POST"@0,
:uri=>{:path=>"/"},
:version=>"1.1"@12,
:headers=>
{"Accept"@17=>"*/*"@25,
"User-Agent"@30=>"Ruby"@42,
"Content-Type"@48=>"application/x-www-form-urlencoded"@62,
"Connection"@97=>"close"@109,
"Host"@116=>"localhost:8080"@122,
"Content-Length"@138=>"25"@154}}
#Output from script file:
Request body: country=Ukraine&city=Kiev
Response body:
What did I do wrong?
Related: Parse body of POST reqest in self-made server in Ruby
Upvotes: 1
Views: 1826
Reputation: 171
Post requests are not supported. They are not included in the spec. Calling stream.body will block if the request body content-length is not a multiple of 4096.
Because https://github.com/postmodern/net-http-server/blob/master/lib/net/http/server/stream.rb calls
IO.read(4096, '')
You can get around by reading the socket in a non-blocking way if you don't know the size in advance. For example following handler will try to read the Request body and then simply return it in the Response body. If Content-Length is provided in the header, it will block waiting for the stream to send enough data till specified amount of bytes.
class PostHandler
BUF_SIZE = 4096
def call(request, stream)
case request[:method].to_s
when "POST"
body = nil
if request[:headers]["Content-Type"] == "application/x-www-form-urlencoded"
body = readBlock(stream.socket, request[:headers]["Content-Length"].to_i)
else
body = readNonblock(stream.socket)
end
unless body.nil?
return [200, {}, [body] ]
end
end
[404, {}, [""]]
end
private
# blocking way to read the stream
def readBlock(socket, length)
socket.read(length)
end
# non-blocking alternative
# stop reading as soon as it would block
def readNonblock(socket)
buffer = ""
loop {
begin
buffer << socket.read_nonblock(BUF_SIZE)
rescue Errno::EAGAIN
# Resource temporarily unavailable - read would block
break
end
}
buffer
end
end
Upvotes: 2
Reputation: 1294
I'm working on a similar thing at the moment. For "quick and dirty" testing I would suggest curl. For example:
GET:
curl http://localhost:3000/locations"
POST:
curl http://localhost:3000/locations -d "location[country]=Ukraine&location[city]=Kiev"
PUT:
curl http://localhost:3000/locations/1 -X PUT -d "location[country]=Ukraine"
DELETE:
curl http://localhost:3000/locations/1 -X DELETE"
Having said that, for more robust testing, assuming you are using Rails/Rack, I'm using Cucumber for integration tests for the API. where you can simply use the rais/rack helpers, for example, in use case, for POST:
url = "http://localhost:3000/locations/"
hash = { "location" => { "country => "Ukraine", "city" => "Kiev" }
post url, hash
Upvotes: 1
Reputation: 39650
I assume it's a problem with net-http-server.
Try with WEBrick as your server:
require 'webrick'
include WEBrick
def start_webrick(config = {})
config.update(:Port => 8080)
server = HTTPServer.new(config)
yield server if block_given?
['INT', 'TERM'].each {|signal|
trap(signal) {server.shutdown}
}
server.start
end
start_webrick {|server|
server.mount_proc('/') {|req, resp|
resp.body = req.body
}
}
This works for me, output is:
country=Ukraine&city=Kiev
Upvotes: 1