ninenines / gun

HTTP/1.1, HTTP/2, Websocket client (and more) for Erlang/OTP.
ISC License
898 stars 231 forks source link

HTTP/1 request with empty body and content-type header leaves connection in bad state #141

Closed petrohi closed 5 years ago

petrohi commented 6 years ago

On subsequent request:

{:error,
 {:function_clause,
   [{:gun_http, :request,
     [{:http_state, #PID<0.200.0>,
       {:sslsocket, {:gen_tcp, #Port<0.6542>, :tls_connection, :undefined},
        #PID<0.247.0>}, :ranch_ssl, :"HTTP/1.1", [:gun_data], :keepalive, "",
       [], :head, {0, 0}, :body_chunked,
       #Function<0.21809937/1 in :gun_http.init/4>},
      #Reference<0.291734004.3684958210.98171>, #PID<0.200.0>, "PUT",
      'api.xyz.com', 443, "/some",
      [{"content-type", "application/json"}]],
     [file: '/Users/phizalev/machine_gun/deps/gun/src/gun_http.erl',
      line: 281]},
    {:gun, :loop, 1,
     [file: '/Users/phizalev/machine_gun/deps/gun/src/gun.erl', line: 683]},
    {:gun, :proc_lib_hack, 5,
     [file: '/Users/phizalev/machine_gun/deps/gun/src/gun.erl', line: 545]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}
essen commented 6 years ago

What do you mean by empty body? How do you call it?

petrohi commented 6 years ago

Like this: :gun.request(pid, "PUT", "/some", [{"content-type", "application/json"}], "", %{})

essen commented 6 years ago

That's not a bug per se, perhaps the interface is not obvious. If you set content-type but don't set a body you are expected to use the gun:data functions to send that body.

petrohi commented 6 years ago

Would probably make sense for body be some atom in this case. But yes--makes sense.

essen commented 6 years ago

Yes I don't disagree.

essen commented 6 years ago

Let's keep it open if you don't mind, it'd be good to fix this, or at least error out earlier, at some point.

essen commented 5 years ago

https://github.com/petrohi/machine_gun/issues/1 is an example real-world issue caused by this.

Also occurs for gun:get/2.

essen commented 5 years ago

It's going to be hard to rework this in a backward compatible manner but let's give it a try.

I suggest having two functions instead of request/4,5,6 in the future:

That means that the method-functions can be converted to calling one or the other function depending on whether they expect more data afterwards.

The following can be deprecated and removed in Gun 2.0:

I think this can be done without breaking any user code, since the documented behavior will be preserved. Thoughts?

essen commented 5 years ago

Edited a proposal. Thoughts?

petrohi commented 5 years ago

Should this be?

patch/4,5, post/4,5, put/4,5: nofin, body is provided as an argument

essen commented 5 years ago

No by nofin I mean the data function can be called (the body isn't finished after the request call), while fin sends everything it gets and terminates the request.

petrohi commented 5 years ago

Understand now. request/4 with headers check is temporary for compatibility?

essen commented 5 years ago

Yes until Gun 2.0. request/4 would be removed then and you'd have to call one or the other function to be able to send body or not. So we'd still have the header issue for patch/3, post/3, put/3 and request/4 for now but request/4 would be undocumented immediately in favor of the functions headers/4,5, and it's a lesser problem for the other 3.

9mm commented 5 years ago

So how do i need to deal with this? currently I have an API returning 80K req/min with "" body and no content-length

9mm commented 5 years ago

PS i should mention I'm using machine_gun

essen commented 5 years ago

I'm currently doing some rework and tbh will probably make it Gun 2.0 and do the breaking changes we need.

That said, I'm not sure you have the right bug, if you do then all you need to do is remove the content-type or ensure you send a final empty data.

essen commented 5 years ago

Please review 630bd47 which contains the proposed solution for Gun 2.0.

essen commented 5 years ago

I'll close this meanwhile since I believe this is addressed but feel free to say otherwise or reopen. Thanks!