diff --git a/spec/std/http/client/client_spec.cr b/spec/std/http/client/client_spec.cr index a1bc5dedc19e..2c3285a61512 100644 --- a/spec/std/http/client/client_spec.cr +++ b/spec/std/http/client/client_spec.cr @@ -87,5 +87,13 @@ module HTTP client.get("/") end end + + it "accepts IO objects as body" do + body = MemoryIO.new("hello world") + + TestServer.open("localhost", 0, 0) do |server| + Client.exec("POST", "http://localhost:#{server.addr.ip_port}/", nil, body) + end + end end end diff --git a/spec/std/http/request_spec.cr b/spec/std/http/request_spec.cr index 9114ad95d181..580d107faac1 100644 --- a/spec/std/http/request_spec.cr +++ b/spec/std/http/request_spec.cr @@ -66,6 +66,14 @@ module HTTP io.to_s.should eq("POST / HTTP/1.1\r\nContent-Length: 13\r\n\r\nthisisthebody") end + it "serialize chunked POST (with IO as body)" do + body = MemoryIO.new "hello world" + request = Request.new "POST", "/", body: body + io = MemoryIO.new + request.to_io(io) + io.to_s.should eq("POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nhello world\r\n0\r\n\r\n") + end + it "parses GET" do request = Request.from_io(MemoryIO.new("GET / HTTP/1.1\r\nHost: host.example.org\r\n\r\n")).not_nil! request.method.should eq("GET") diff --git a/src/http/client/client.cr b/src/http/client/client.cr index 8df60bc0ce17..b84b4e28daf3 100644 --- a/src/http/client/client.cr +++ b/src/http/client/client.cr @@ -193,7 +193,7 @@ class HTTP::Client # response = client.{{method.id}}("/", headers: HTTP::Headers{"User-agent": "AwesomeApp"}, body: "Hello!") # response.body #=> "..." # ``` - def {{method.id}}(path, headers = nil : HTTP::Headers?, body = nil : String?) : HTTP::Client::Response + def {{method.id}}(path, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) : HTTP::Client::Response exec {{method.upcase}}, path, headers, body end @@ -206,7 +206,7 @@ class HTTP::Client # response.body_io.gets #=> "..." # end # ``` - def {{method.id}}(path, headers = nil : HTTP::Headers?, body = nil : String?) + def {{method.id}}(path, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) exec {{method.upcase}}, path, headers, body do |response| yield response end @@ -219,7 +219,7 @@ class HTTP::Client # response = HTTP::Client.{{method.id}}("/", headers: HTTP::Headers{"User-agent": "AwesomeApp"}, body: "Hello!") # response.body #=> "..." # ``` - def self.{{method.id}}(url : String | URI, headers = nil : HTTP::Headers?, body = nil : String?) : HTTP::Client::Response + def self.{{method.id}}(url : String | URI, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) : HTTP::Client::Response exec {{method.upcase}}, url, headers, body end @@ -231,7 +231,7 @@ class HTTP::Client # response.body_io.gets #=> "..." # end # ``` - def self.{{method.id}}(url : String | URI, headers = nil : HTTP::Headers?, body = nil : String?) + def self.{{method.id}}(url : String | URI, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) exec {{method.upcase}}, url, headers, body do |response| yield response end @@ -348,7 +348,7 @@ class HTTP::Client # response = client.exec "GET", "/" # response.body # => "..." # ``` - def exec(method : String, path, headers = nil : HTTP::Headers?, body = nil : String?) : HTTP::Client::Response + def exec(method : String, path, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) : HTTP::Client::Response exec new_request method, path, headers, body end @@ -361,7 +361,7 @@ class HTTP::Client # response.body_io.gets # => "..." # end # ``` - def exec(method : String, path, headers = nil : HTTP::Headers?, body = nil : String?) + def exec(method : String, path, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) exec(new_request(method, path, headers, body)) do |response| yield response end @@ -374,7 +374,7 @@ class HTTP::Client # response = HTTP::Client.exec "GET", "http://www.example.com" # response.body # => "..." # ``` - def self.exec(method, url : String | URI, headers = nil : HTTP::Headers?, body = nil : String?) : HTTP::Client::Response + def self.exec(method, url : String | URI, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) : HTTP::Client::Response exec(url) do |client, path| client.exec method, path, headers, body end @@ -388,7 +388,7 @@ class HTTP::Client # response.body_io.gets # => "..." # end # ``` - def self.exec(method, url : String | URI, headers = nil : HTTP::Headers?, body = nil : String?) + def self.exec(method, url : String | URI, headers = nil : HTTP::Headers?, body = nil : String | IO | Nil) exec(url) do |client, path| client.exec(method, path, headers, body) do |response| yield response diff --git a/src/http/request.cr b/src/http/request.cr index 9684429c65d8..9b0e50367c58 100644 --- a/src/http/request.cr +++ b/src/http/request.cr @@ -8,12 +8,14 @@ class HTTP::Request getter body getter version - def initialize(@method : String, @resource, @headers = Headers.new : Headers, @body = nil, @version = "HTTP/1.1") - if body = @body + def initialize(@method : String, @resource, @headers = Headers.new : Headers, body = nil, @version = "HTTP/1.1") + if body.is_a?(String) @headers["Content-Length"] = body.bytesize.to_s - elsif @method == "POST" || @method == "PUT" + elsif !body && (@method == "POST" || @method == "PUT") @headers["Content-Length"] = "0" end + + @body = body end # Returns a convenience wrapper around querying and setting cookie related diff --git a/src/oauth/signature.cr b/src/oauth/signature.cr index 55633c60478d..82b0b6a0894b 100644 --- a/src/oauth/signature.cr +++ b/src/oauth/signature.cr @@ -80,6 +80,10 @@ struct OAuth::Signature body = request.body content_type = request.headers["Content-type"]? if body && content_type == "application/x-www-form-urlencoded" + if body.is_a?(IO) + body = body.gets_to_end + end + params.add_query body end