<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="299773" data-post-id="299773">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wojtekmach" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/120/999_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wojtekmach
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Hex Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h1><a name="p-299773-req-v04-is-outhttpsgithubcomwojtekmachreqreleasestagv040-1" class="anchor" href="#p-299773-req-v04-is-outhttpsgithubcomwojtekmachreqreleasestagv040-1" aria-label="Heading link" rel="nofollow"></a><a href="https://github.com/wojtekmach/req/releases/tag/v0.4.0" rel="noopener nofollow ugc">Req v0.4 is out!</a></h1>
<p>Req v0.4.0 changes headers to be maps, adds request &amp; response streaming, and improves steps.</p>
<h3><a name="p-299773-change-headers-to-be-maps-2" class="anchor" href="#p-299773-change-headers-to-be-maps-2" aria-label="Heading link" rel="nofollow"></a>Change Headers to be Maps</h3>
<p>Previously headers were lists of name/value tuples, e.g.:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[{"content-type", "text/html"}]
</code></pre>
<p>This is a standard across the ecosystem (with minor difference that some Erlang libraries use charlists instead of binaries.)</p>
<p>There are some problems with this particular choice though:</p>
<ul>
<li>We cannot use <code>headers[name]</code></li>
<li>We cannot use pattern matching</li>
</ul>
<p>In short, this representation isn’t very ergonomic to use.</p>
<p>Now headers are maps of string names and lists of values, e.g.:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{"content-type" =&gt; ["text/html"]}
</code></pre>
<p>This allows <code>headers[name]</code> usage:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">response.headers["content-type"]
#=&gt; ["text/html"]
</code></pre>
<p>and pattern matching:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">case Req.request!(req) do
  %{headers: %{"content-type" =&gt; ["application/json" &lt;&gt; _]}} -&gt;
    # handle JSON response
end
</code></pre>
<p>This is a major breaking change. If you cannot easily update your app or your dependencies, do:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># config/config.exs
config :req, legacy_headers_as_lists: true
</code></pre>
<p>This legacy fallback will be removed on Req 1.0.</p>
<p>There are two other changes to headers in this release.</p>
<p>Header names are now case-insensitive in functions like <code>Req.Response.get_header/2</code>.</p>
<p>Trailer headers, or more precisely trailer fields or simply trailers, are now stored in a separate <code>trailers</code> field on the <code>%Req.Response{}</code> struct as long as you use Finch 0.17+.</p>
<h3><a name="p-299773-add-request-body-streaming-3" class="anchor" href="#p-299773-add-request-body-streaming-3" aria-label="Heading link" rel="nofollow"></a>Add Request Body Streaming</h3>
<p>Req v0.4 adds official support for request body streaming by setting the request body to an <code>enumerable</code>. Here’s an example:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; stream = Stream.duplicate("foo", 3)
iex&gt; Req.post!("https://httpbin.org/post", body: stream).body["data"]
"foofoofoo"
</code></pre>
<p>The enumerable is passed through request steps and they may change it. For example, the <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#compress_body/1" rel="noopener nofollow ugc"><code>compress_body</code></a> step gzips the request body on the fly.</p>
<h3><a name="p-299773-add-response-body-streaming-4" class="anchor" href="#p-299773-add-response-body-streaming-4" aria-label="Heading link" rel="nofollow"></a>Add Response Body Streaming</h3>
<p>Req v0.4 also adds response body streaming, via the <code>:into</code> option.</p>
<p>Here’s an example where we download the first 20kb (by making a <em>range</em> request, via the <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#put_range/1" rel="noopener nofollow ugc"><code>put_range</code></a> step) of Elixir release zip. We stream the response body into a function and can handle each body chunk. The function receives a <code>{:data, data}, {req, resp}</code> and returns a <code>{:cont | :halt, {req, resp}}</code> tuple.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">resp =
  Req.get!(
    url: "https://github.com/elixir-lang/elixir/releases/download/v1.15.4/elixir-otp-26.zip",
    range: 0..20_000,
    into: fn {:data, data}, {req, resp} -&gt;
      IO.inspect(byte_size(data), label: :chunk)
      {:cont, {req, resp}}
    end
  )

# output: 17:07:38.131 [debug] redirecting to https://objects.githubusercontent.com/github-production-release-asset-2e6(...)
# output: chunk: 16384
# output: chunk: 3617

resp.status #=&gt; 206
resp.headers["content-range"] #=&gt; ["bytes 0-20000/6801977"]
resp.body #=&gt; ""
</code></pre>
<p>Notice we only stream response <em>body</em>, that is, Req automatically handles HTTP response status and headers. Once the stream is done, Req passes the response through response steps which allows following redirects, retrying on errors, etc. Response <code>body</code> is set to empty string <code>""</code> which is then ignored by <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>, <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decode_body/1" rel="noopener nofollow ugc"><code>decode_body</code></a>, and similar steps. If you need to decompress or decode incoming chunks, you need to do that in your custom <code>into: fun</code> function.</p>
<p>As the name <code>:into</code> implies, we can also stream response body into any <a href="https://hexdocs.pm/elixir/Collectable.html" rel="noopener nofollow ugc"><code>Collectable</code></a>. Here’s a similar snippet to above where we stream to a file:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">resp =
  Req.get!(
    url: "https://github.com/elixir-lang/elixir/releases/download/v1.15.4/elixir-otp-26.zip",
    range: 0..20_000,
    into: File.stream!("elixit-otp-26.zip.1")
  )

# output: 17:07:38.131 [debug] redirecting to (...)
resp.status #=&gt; 206
resp.headers["content-range"] #=&gt; ["bytes 0-20000/6801977"]
resp.body #=&gt; %File.Stream{}
</code></pre>
<h3><a name="p-299773-full-changelog-5" class="anchor" href="#p-299773-full-changelog-5" aria-label="Heading link" rel="nofollow"></a>Full CHANGELOG</h3>
<ul>
<li>
<p>Change <code>request.headers</code> and <code>response.headers</code> to be maps.</p>
</li>
<li>
<p>Ensure <code>request.headers</code> and <code>response.headers</code> are downcased.</p>
<p>Per <a href="https://www.rfc-editor.org/rfc/rfc9110.html" rel="noopener nofollow ugc">RFC 9110: HTTP Semantics</a>, HTTP headers should be case-insensitive. However, per <a href="https://datatracker.ietf.org/doc/html/rfc9113" rel="noopener nofollow ugc">RFC 9113: HTTP/2</a> headers must be sent downcased.</p>
<p>Req headers are now stored internally downcased and all accessor functions like <a href="https://hexdocs.pm/req/0.4.0/Req.Response.html#get_response/2" rel="noopener nofollow ugc"><code>Req.Response.get_header/2</code></a> are downcasing the given header name.</p>
</li>
<li>
<p>Add <code>trailers</code> field to <a href="https://hexdocs.pm/req/0.4.0/Req.Response.html" rel="noopener nofollow ugc"><code>Req.Response</code></a> struct. Trailer field is only filled in on Finch 0.17+.</p>
</li>
<li>
<p>Make <code>request.registered_options</code> internal representation private.</p>
</li>
<li>
<p>Make <code>request.options</code> internal representation private.</p>
<p>Currently <code>request.options</code> field is a map but it may change in the future. One possible future change is using keywords lists internally which would allow, for example, <code>Req.new(params: [a: 1]) |&gt; Req.update(params: [b: 2])</code> to keep duplicate <code>:params</code> in <code>request.options</code> which would then allow to decide the duplicate key semantics on a per-step basis. And so, for example, <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#put_params/1" rel="noopener nofollow ugc"><code>put_params</code></a> would <em>merge</em> params but most steps would simply use the first value.</p>
<p>To have some room for manoeuvre in the future we should stop pattern matching on <code>request.options</code>. Calling <code>request.options[key]</code>, <code>put_in(request.options[key], value)</code>, and <code>update_in(request.options[key], fun)</code> <em>is</em> allowed.</p>
</li>
<li>
<p>Fix typespecs for some functions</p>
</li>
<li>
<p>Deprecate <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#output/1" rel="noopener nofollow ugc"><code>output</code></a> step in favour of <code>into: File.stream!(path)</code>.</p>
</li>
<li>
<p>Rename <code>follow_redirects</code> step to <a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#redirect/1" rel="noopener nofollow ugc"><code>redirect</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#redirect/1" rel="noopener nofollow ugc"><code>redirect</code></a>: Rename <code>:follow_redirects</code> option to <code>:redirect</code>.</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#redirect/1" rel="noopener nofollow ugc"><code>redirect</code></a>: Rename <code>:location_trusted</code> option to <code>:redirect_trusted</code>.</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#redirect/1" rel="noopener nofollow ugc"><code>redirect</code></a>: Change HTTP request method to GET only on POST requests that result in 301..303.</p>
<p>Previously we were changing the method to GET for all 3xx except 307 and 308.</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>: Remove support for <code>deflate</code> compression (which was broken)</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>: Don’t crash on unknown codec</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>: Fix handling HEAD requests</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>: Re-calculate <code>content-length</code> header after decompresion</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decompress_body/1" rel="noopener nofollow ugc"><code>decompress_body</code></a>: Remove <code>content-encoding</code> header after decompression</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#decode_body/1" rel="noopener nofollow ugc"><code>decode_body</code></a>: Do not decode response with <code>content-encoding</code> header</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#run_finch/1" rel="noopener nofollow ugc"><code>run_finch</code></a>: Add <code>:inet6</code> option</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Support <code>retry: :safe_transient</code> which retries HTTP 408/429/500/502/503/504 or exceptions with <code>reason</code> field set to <code>:timeout</code>/<code>:econnrefused</code>.</p>
<p><code>:safe_transient</code> is the new default retry mode. (Previously we retried on 408/429/5xx and <em>any</em> exception.)</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Support <code>retry: :transient</code> which is the same as <code>:safe_transient</code> except it retries on all HTTP methods</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Use <code>retry-after</code> header value on HTTP 503 Service Unavailable. Previously only HTTP 429 Too Many Requests was using this header value.</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Support <code>retry: &amp;fun/2</code>. The function receives <code>request, response_or_exception</code> and returns either:</p>
<ul>
<li>
<p><code>true</code> - retry with the default delay</p>
</li>
<li>
<p><code>{:delay, milliseconds}</code> - retry with the given delay</p>
</li>
<li>
<p><code>false/nil</code> - don’t retry</p>
</li>
</ul>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Deprecate <code>retry: :safe</code> in favour of <code>retry: :safe_transient</code></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Steps.html#retry/1" rel="noopener nofollow ugc"><code>retry</code></a>: Deprecate <code>retry: :never</code> in favour of <code>retry: false</code></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.html#request/2" rel="noopener nofollow ugc"><code>Req.request/2</code></a>: Improve error message on invalid arguments</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.html#update/2" rel="noopener nofollow ugc"><code>Req.update/2</code></a>: Do not duplicate headers</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.html#update/2" rel="noopener nofollow ugc"><code>Req.update/2</code></a>: Merge <code>:params</code></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Request.html" rel="noopener nofollow ugc"><code>Req.Request</code></a>: Fix displaying redacted basic authentication</p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Request.html" rel="noopener nofollow ugc"><code>Req.Request</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Request.html#get_option/3" rel="noopener nofollow ugc"><code>Req.Request.get_option/3</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Request.html" rel="noopener nofollow ugc"><code>Req.Request</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Request.html#fetch_option/2" rel="noopener nofollow ugc"><code>Req.Request.fetch_option/2</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Request.html" rel="noopener nofollow ugc"><code>Req.Request</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Request.html#fetch_option!/2" rel="noopener nofollow ugc"><code>Req.Request.fetch_option!/2</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Request.html" rel="noopener nofollow ugc"><code>Req.Request</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Request.html#delete_option/2" rel="noopener nofollow ugc"><code>Req.Request.delete_option/2</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Response.html" rel="noopener nofollow ugc"><code>Req.Response</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Response.html#delete_header/2" rel="noopener nofollow ugc"><code>Req.Response.delete_header/2</code></a></p>
</li>
<li>
<p><a href="https://hexdocs.pm/req/0.4.0/Req.Response.html" rel="noopener nofollow ugc"><code>Req.Response</code></a>: Add <a href="https://hexdocs.pm/req/0.4.0/Req.Response.html#update_private/4" rel="noopener nofollow ugc"><code>Req.Response.update_private/4</code></a></p>
</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="299773" data-batch-url="/posts/batch_likers">
                        29
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-299773" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="299773"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-most-liked cat-most-liked" title="One of the top 3 liked posts in this thread!"></div>
  </section>
</div>
    <div class="postbit" id="299887" data-post-id="299887">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zambal" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zambal/120/1549_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zambal
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Congrats with the release. I currently use httpoison for most of my http client needs, but will definitely consider Req the next time I need a http client.</p>
<p>Now that Req uses maps to store headers, I was wondering how Req handles multiple response headers with the same key. Fold them to a single entry with a list of values?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="299887" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-299887" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="299887"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="299888" data-post-id="299888">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The map still has a list per key.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="299888" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-299888" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="299888"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="318163" data-post-id="318163">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MatijaL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MatijaL
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi, when using <code>Req.get()</code> with invalid URL, the process crashes and I get an error<br>
<code>scheme is required for url:</code> and then there are some Finch functions mentioned. I can’t even pattern match on the error and return an error message back to the user.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[error] GenServer #PID&lt;0.939.0&gt; terminating
** (ArgumentError) scheme is required for url: ddsdsdf.com
    (finch 0.17.0) lib/finch/request.ex:135: Finch.Request.parse_url/1
    (finch 0.17.0) lib/finch/request.ex:103: Finch.Request.build/5
    (req 0.4.9) lib/req/steps.ex:753: Req.Steps.run_finch/1
    (req 0.4.9) lib/req/request.ex:993: Req.Request.run_request/1
    (req 0.4.9) lib/req/request.ex:938: Req.Request.run/1
</code></pre>
<p>Is there a way to solve this?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318163" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-318163" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318163"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="318165" data-post-id="318165">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MatijaL" data-post="25" data-topic="48494">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/258eb7/48.png" class="avatar"> MatijaL:</div>
<blockquote>
<p>Is there a way to solve this?</p>
</blockquote>
</aside>
<p>Don’t depend on <code>Req</code> doing the uri validation, but do it on your own. <code>Req</code> takes <code>URI</code> structs and that module has APIs for you to validate the input.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318165" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-318165" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318165"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="318172" data-post-id="318172">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="stevensonmt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/120/20503_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  stevensonmt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I wonder if this should be recommended as a change in behavior for Finch. Currently Finch does this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">@doc false
  def parse_url(url) when is_binary(url) do
    url |&gt; URI.parse() |&gt; parse_url()
  end

  def parse_url(%URI{} = parsed_uri) do
     ...
</code></pre>
<p>but the <code>URI.parse/1</code> docs point out</p>
<blockquote>
<p>this function expects both absolute and relative URIs to be well-formed and does not perform any validation. See the “Examples” section below. Use <a href="https://hexdocs.pm/elixir/URI.html#new/1" rel="noopener nofollow ugc"><code>new/1</code></a> if you want to validate the URI fields after parsing.</p>
</blockquote>
<p>and</p>
<blockquote>
<p>In contrast to <a href="https://hexdocs.pm/elixir/URI.html#new/1" rel="noopener nofollow ugc"><code>URI.new/1</code></a>, this function will parse poorly-formed URIs</p>
</blockquote>
<p>Perhaps Finch should use <code>URI.new/1</code> to parse the URL binary. I also think the guard for <code>Finch.Request.parse_url/1</code> is not needed since both <code>URI.new/1</code> and <code>URI.parse/1</code> would handle both binaries and URI structs.</p>
<p>I also think the URI module has made an odd choice in having the <code>parse</code> function return uncommented success even when a string input cannot be a valid URI.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318172" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-318172" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318172"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="318174" data-post-id="318174">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Afaik that would require increasing the minimum elixir version, which might be undesireable.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318174" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-318174" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318174"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="318175" data-post-id="318175">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wojtekmach" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/120/999_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wojtekmach
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Hex Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="stevensonmt" data-post="27" data-topic="48494">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/48/20503_2.png" class="avatar"> stevensonmt:</div>
<blockquote>
<p>Perhaps Finch should use <code>URI.new/1</code> to parse the URL binary</p>
</blockquote>
</aside>
<p>This wouldn’t solve the problem above though:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; URI.new("ddsdsdf.com")
{:ok,
 %URI{
   scheme: nil,
   userinfo: nil,
   host: nil,
   port: nil,
   path: "ddsdsdf.com",
   query: nil,
   fragment: nil
 }}
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318175" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-318175" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318175"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="318178" data-post-id="318178">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="stevensonmt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/120/20503_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  stevensonmt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Weird that it considers that to be a “valid” URI. In that case maybe Finch could pattern match <code>URI.parse</code> against <code>%URI{scheme: nil}</code> to handle this situation?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318178" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-318178" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318178"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="318180" data-post-id="318180">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wojtekmach" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/120/999_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wojtekmach
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Hex Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>That’s basically what it does already!</p>
<aside class="onebox githubblob" data-onebox-src="https://github.com/sneako/finch/blob/v0.18.0/lib/finch/request.ex#L134:L135">
  <header class="source">

      <a href="https://github.com/sneako/finch/blob/v0.18.0/lib/finch/request.ex#L134:L135" target="_blank" rel="noopener nofollow ugc">github.com/sneako/finch</a>
  </header>

  <article class="onebox-body">
    <h4><a href="https://github.com/sneako/finch/blob/v0.18.0/lib/finch/request.ex#L134:L135" target="_blank" rel="noopener nofollow ugc">lib/finch/request.ex</a></h4>

<div class="git-blob-info">
  <a href="https://github.com/sneako/finch/blob/v0.18.0/lib/finch/request.ex#L134:L135" rel="noopener nofollow ugc"><code>v0.18.0</code></a>
</div>



    <pre class="onebox"><code class="lang-ex">
      <ol class="start lines" start="124" style="counter-reset: li-counter 123 ;">
          <li>  normalized_path = parsed_uri.path || "/"</li>
          <li></li>
          <li>  scheme =</li>
          <li>    case parsed_uri.scheme do</li>
          <li>      "https" -&gt;</li>
          <li>        :https</li>
          <li></li>
          <li>      "http" -&gt;</li>
          <li>        :http</li>
          <li></li>
          <li class="selected">      nil -&gt;</li>
          <li>        raise ArgumentError, "scheme is required for url: #{URI.to_string(parsed_uri)}"</li>
          <li></li>
          <li>      scheme -&gt;</li>
          <li>        raise ArgumentError,</li>
          <li>              "invalid scheme \"#{scheme}\" for url: #{URI.to_string(parsed_uri)}"</li>
          <li>    end</li>
          <li></li>
          <li>  {scheme, parsed_uri.host, parsed_uri.port, normalized_path, parsed_uri.query}</li>
          <li>end</li>
          <li></li>
      </ol>
    </code></pre>



  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>
 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="318180" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/req-a-batteries-included-http-client-for-elixir/48494/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-318180" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="318180"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/48494/load_more?page=4">Load more posts</a>
</div></template></turbo-stream>