Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending back cookies #155

Closed
teamon opened this issue Jan 24, 2018 · 11 comments
Closed

Sending back cookies #155

teamon opened this issue Jan 24, 2018 · 11 comments
Labels
Milestone

Comments

@teamon
Copy link
Member

teamon commented Jan 24, 2018

In #37 (comment) @florinpatrascu wrote

hi there - sorry for piggybacking on this older thread. It is about cookies, and I just started using Tesla. Are there any examples of how to handle the cookies, with Tesla. Right now I just want to be able to send back the cookies I am getting from the server i.e JSESSIONID - Thank you

There isn't anything built-in specific to cookies, but you can do it yourself with something alongs these lines:

env = post("/login")
cookie = env.headers["set-cookie"]
get("/stuff", headers: %{"cookie" => cookie})

WARNING: Not tested!

@florinpatrascu
Copy link

thank you @teamon, but if there are multiple cookies incl. the JSESSIONID? The "set-cookie" is a key in a map, and wouldn't be overwritten, in the case I need to set multiple "Set-Cookie" header entries? With the middleware workaround discussed in #37 , I can indeed have access to the original headers, excerpt:

{'set-cookie','JSESSIONID=C110FE6....'},
{'set-cookie','NSC....4216cb;path=/;secure;httponly'},
{'set-cookie', 'SSLB=0; path=/; domain=.....'}

but I am not sure how to send them back, since the "Set-Cookie" (normalized) is unique (in a map).

@teamon
Copy link
Member Author

teamon commented Jan 24, 2018

Ahh right... This isn't possible right now.

You can hack around it with:

# create a custom adapter
defmodule MyClient do
  use Tesla

  adapter :custom_adapter

  def custom_adapter(env, opts) do
    headers = env.opts[:raw_headers]
    env = %{env | headers: headers}
    Tesla.Adapter.Hackney.call(env)
  end
end

# set :raw_headers opt 
get("/...", opts: [raw_headers: [{"set-cookie", "aaa"}, {"set-cookie", "bbb"}, ...]])

This should work with hackney and ibrowse adapters, it will break with httpc (because it does env.headers["content-type"]).

I'm adding this issue to the list of breaking changes for 1.0 (#129)

@florinpatrascu
Copy link

Ahaaa :) Cool! I'll write my own adapter then, as you mentioned, thank you! Going forward, are the headers becoming a Keyword list, rather than a Map, for Tesla 1.0?

@teamon teamon mentioned this issue Jan 24, 2018
9 tasks
@florinpatrascu
Copy link

👍

@florinpatrascu
Copy link

@teamon - in the absence of a cookie jar, what is the recommended method, in the current version (not future 1.x) for passing some of the Response headers, back to a built client env, so that these (Response) headers can be reused by get/post/etc, w/o explicitly setting them as get/post/etc opts?

I am using this issue (#37) as a reference:

defmodule Client do
  use Tesla

  def new do
    Tesla.build_client [], [
      :keep_original_headers
    ]
  end

  def keep_original_headers(env, next) do
    env = Tesla.run(env, next)
    Tesla.put_opt(env, :original_headers, env.headers)
  end
end

I can see the :original_headers in the Response, but I'd like to have access to them and set/add them to the request opts, if possible?!

@teamon
Copy link
Member Author

teamon commented Jan 25, 2018

Since everything in elixir is immutable the only way to use headers from once response in another reqeust is to pass it when performing the request.

This should work, again, not tested.

defmodule MyClient do
  use Tesla

  plug :keep_original_headers
  adapter :run_with_original_headers
  
  def new(prev_env) do
    # add middleware with dynamic opts argument
    Tesla.build_client [], [
      {:apply_original_headers, prev_env.opts[:original_headers]}
    ]
  end

  def apply_original_headers(env, next, original_headers) do
    # get that dynamic opts value and set it as :original_headers
    env
    |> Tesla.put_opt(:original_headers, original_headers)
    |> Tesla.run(next)
  end

  def run_with_original_headers(env, opts) do
    # override request headers with original_headers from opts
    headers = env.opts[:original_headers]
    env = %{env | headers: headers}
    Tesla.Adapter.Hackney.call(env)
  end

  def keep_original_headers(env, next) do
    # after the request put original_headers in opts
    env = Tesla.run(env, next)
    Tesla.put_opt(env, :original_headers, env.headers)
  end
end


env1 = MyClient.new() |> MyClient.post("/login")
env2 = MyClient.new(env1) |> MyClient.get("/data")

@florinpatrascu
Copy link

thank you @teamon!

@teamon teamon added this to the 1.0 milestone Feb 1, 2018
@teamon
Copy link
Member Author

teamon commented Feb 1, 2018

@florinpatrascu Please try tesla from 1.0 branch:

# mix.exs
{:tesla, github: "teamon/tesla", branch: "1.0"}

There is a Migration guide to help you convert from current version.

@florinpatrascu
Copy link

Many thanks, @teamon! I’ll start testing as soon as possible!

@florinpatrascu
Copy link

Much better, and lots of improvements. Now I’ll have to refactor my previous hack, and eventually build a cookie-jar :) Thank you, for all this work, @teamon !

@teamon teamon closed this as completed Feb 16, 2018
@adamz-prescribefit
Copy link

This is just for people looking how to do it with a later version of tesla. To make it work with 1.4:

use Tesla, only: [:post]
plug Tesla.Middleware.BaseUrl, "http://my_url"
plug Tesla.Middleware.FormUrlencoded

def post_it() do
    request_body = %{username: "username", password: "password"}
    {:ok, response} = post("/autentication_url", request_body)
    cookie = Tesla.get_header(response, "set-cookie")
    
    post!(
      "/my_other_url",
      %{message: "my_message},
      headers: [{"cookie", cookie}]
    )
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants