diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eb1507b8..f6aba7d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -87,11 +87,11 @@ jobs: if: ${{ matrix.lint }} - name: Run tests - run: mix test --trace --include proxy + run: mix test --trace --include integration --include proxy if: ${{ !matrix.coverage }} - name: Run tests with coverage - run: mix coveralls.github --include proxy + run: mix coveralls.github --include integration --include proxy if: ${{ matrix.coverage }} - name: Run Dialyzer diff --git a/README.md b/README.md index 7f59482d..b18bad27 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Mint 🌱 -[![Build status badge](https://travis-ci.org/elixir-mint/mint.svg?branch=master)](https://travis-ci.org/elixir-mint/mint) +[![CI badge](https://github.com/elixir-mint/mint/actions/workflows/main.yml/badge.svg)](https://github.com/elixir-mint/mint/actions/workflows/main.yml) [![Documentation badge](https://img.shields.io/badge/Documentation-ff69b4)][documentation] [![Hex.pm badge](https://img.shields.io/badge/Package%20on%20hex.pm-informational)](https://hex.pm/packages/mint) [![Coverage status badge](https://coveralls.io/repos/github/elixir-mint/mint/badge.svg?branch=main)](https://coveralls.io/github/elixir-mint/mint?branch=main) @@ -19,11 +19,15 @@ defp deps do end ``` -Then, run `$ mix deps.get`. +Then, run: + +```shell +mix deps.get +``` ## Usage -Mint is different from most Erlang and Elixir HTTP clients because it provides a *process-less architecture*. Instead, Mint is based on a functional and immutable data structure that represents an HTTP connection. This data structure wraps a TCP or SSL socket. This allows for more fine-tailored architectures where the developer is responsible for wrapping the connection struct, such as having one process handle multiple connections or having different kinds of processes handle connections. You can think of Mint as [`:gen_tcp`](https://erlang.org/doc/man/gen_tcp.html) and [`:ssl`](https://www.erlang.org/doc/man/ssl.html), but with an understanding of the HTTP/1.1 and HTTP/2 protocols. +Mint is different from most Erlang and Elixir HTTP clients because it provides a _process-less architecture_. Instead, Mint is based on a functional and immutable data structure that represents an HTTP connection. This data structure wraps a TCP or SSL socket. This allows for more fine-tailored architectures where the developer is responsible for wrapping the connection struct, such as having one process handle multiple connections or having different kinds of processes handle connections. You can think of Mint as [`:gen_tcp`](https://erlang.org/doc/man/gen_tcp.html) and [`:ssl`](https://www.erlang.org/doc/man/ssl.html), but with an understanding of the HTTP/1.1 and HTTP/2 protocols. Let's see an example of a basic interaction with Mint. First, we start a connection through `Mint.HTTP.connect/3`: @@ -37,7 +41,7 @@ This transparently chooses between HTTP/1 and HTTP/2. Then, we can send requests iex> {:ok, conn, request_ref} = Mint.HTTP.request(conn, "GET", "/", [], "") ``` -The connection socket runs in [*active mode*](http://erlang.org/doc/man/inet.html#setopts-2) (with `active: :once`), which means that the user of the library needs to handle [TCP messages](http://erlang.org/doc/man/gen_tcp.html#connect-4) and [SSL messages](http://erlang.org/doc/man/ssl.html#id66002): +The connection socket runs in [_active mode_](http://erlang.org/doc/man/inet.html#setopts-2) (with `active: :once`), which means that the user of the library needs to handle [TCP messages](http://erlang.org/doc/man/gen_tcp.html#connect-4) and [SSL messages](http://erlang.org/doc/man/ssl.html#id66002): ```elixir iex> flush() @@ -47,7 +51,6 @@ iex> flush() Users are not supposed to examine these messages. Instead, Mint provides a `stream/2` function that turns messages into HTTP responses. Mint streams responses back to the user in parts through response parts such as `:status`, `:headers`, `:data`, and `:done`. - ```elixir iex> {:ok, conn} = Mint.HTTP.connect(:https, "httpbin.org", 443) iex> {:ok, conn, request_ref} = Mint.HTTP.request(conn, "GET", "/", [], "") @@ -66,7 +69,7 @@ iex> receive do In the example above, we get all the responses as a single SSL message, but that might not always be the case. This means that `Mint.HTTP.stream/2` might not always return responses. -The connection API is *stateless*, which means that you need to make sure to always save the connection that functions return: +The connection API is _stateless_, which means that you need to make sure to always save the connection that functions return: ```elixir # Wrong ❌ @@ -106,29 +109,47 @@ Mint is a low-level client. If you need higher-level features such as connection If you wish to contribute, check out the [issue list][issues] and let us know what you want to work on, so that we can discuss it and reduce duplicate work. -Tests are organized with tags. Integration tests that hit real websites over the internet are tagged with `:requires_internet_connection`. Proxy tests are tagged with `:proxy` and require that you run `DOCKER_USER="$UID:$GID" docker-compose up` from the Mint root directory in order to run (they are excluded by default when you run `$ mix test`). A few examples of running tests: +Tests are organized with tags. Both the `:integration` and `:proxy` tagged tests are disabled by default, but can be enabled selectively. - * `$ mix test` to run the test suite without caring about Docker and `docker-compose up`. +If you encounter `{:error, %Mint.TransportError{reason: :nxdomain}}` error during the integration test, this suggests your DNS-based adblocker (self-hosted or VPN) might be blocking social media sites used in the integration test cases. - * `$ mix test --exclude integration` to only run local tests (for example, you don't have an internet connection available). +Here are a few examples of running tests. + +Running all but `:integration` and `:proxy` tests: + +```shell +mix test +``` - * `$ mix test --include proxy` to run all tests, including proxy tests. +Local tests (for example, you don't have an Internet connection available). + +```shell +mix test --exclude integration +``` + +Normal tests, including proxy or integration related test. + +```shell +DOCKER_USER="$UID:$GID" docker-compose up --detach # or podman-compose up --detach +mix test --include proxy +mix test --include integration --include proxy +``` ## License Copyright 2018 Eric Meadows-Jönsson and Andrea Leopardi - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +> https://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. [castore]: https://github.com/elixir-mint/castore [documentation]: https://hexdocs.pm/mint diff --git a/docker-compose.yml b/docker-compose.yml index bf44f70a..ae9b4520 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,12 +12,12 @@ services: - "8889:8888" httpbin: - image: kennethreitz/httpbin:latest + image: docker.io/kennethreitz/httpbin:latest ports: - "8080:80" caddyhttpbin: - image: caddy:2.6.4-alpine + image: docker.io/caddy:2.6.4-alpine # In GitHub Actions we want to access the files created # by Caddy. However, in Linux these end up being owned by root # because the container by default runs using the root user diff --git a/test/docker/tinyproxy-auth/Dockerfile b/test/docker/tinyproxy-auth/Dockerfile index 322ffdc1..3d11db59 100644 --- a/test/docker/tinyproxy-auth/Dockerfile +++ b/test/docker/tinyproxy-auth/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17.2 +FROM alpine:3.20.1 RUN apk add --no-cache \ tinyproxy diff --git a/test/docker/tinyproxy/Dockerfile b/test/docker/tinyproxy/Dockerfile index 322ffdc1..3d11db59 100644 --- a/test/docker/tinyproxy/Dockerfile +++ b/test/docker/tinyproxy/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17.2 +FROM alpine:3.20.1 RUN apk add --no-cache \ tinyproxy diff --git a/test/mint/http1/integration_test.exs b/test/mint/http1/integration_test.exs index 14dcfa9b..abb884d3 100644 --- a/test/mint/http1/integration_test.exs +++ b/test/mint/http1/integration_test.exs @@ -5,6 +5,8 @@ defmodule Mint.HTTP1.IntegrationTest do alias Mint.{TransportError, HTTP1, HttpBin} + @moduletag :integration + describe "local httpbin" do test "200 response" do assert {:ok, conn} = HTTP1.connect(:http, "localhost", 8080) @@ -113,6 +115,8 @@ defmodule Mint.HTTP1.IntegrationTest do end describe "httpbin.org" do + @describetag :integration + test "keep alive" do assert {:ok, conn} = HTTP1.connect(:https, HttpBin.host(), HttpBin.https_port(), @@ -196,8 +200,6 @@ defmodule Mint.HTTP1.IntegrationTest do end describe "badssl.com" do - @describetag :requires_internet_connection - @tag :capture_log test "SSL with bad certificate" do assert {:error, %TransportError{reason: reason}} = diff --git a/test/mint/http2/integration_test.exs b/test/mint/http2/integration_test.exs index d7790346..ca45a563 100644 --- a/test/mint/http2/integration_test.exs +++ b/test/mint/http2/integration_test.exs @@ -6,7 +6,7 @@ defmodule HTTP2.IntegrationTest do alias Mint.HTTP2 alias Mint.HttpBin - @moduletag :requires_internet_connection + @moduletag :integration setup context do transport_opts = diff --git a/test/mint/integration_test.exs b/test/mint/integration_test.exs index 5e1fdf98..cda8f2c0 100644 --- a/test/mint/integration_test.exs +++ b/test/mint/integration_test.exs @@ -5,7 +5,7 @@ defmodule Mint.IntegrationTest do alias Mint.{TransportError, HTTP, HttpBin} - @moduletag :requires_internet_connection + @moduletag :integration describe "nghttp2.org" do test "SSL - select HTTP1" do diff --git a/test/mint/tunnel_proxy_test.exs b/test/mint/tunnel_proxy_test.exs index a80bb15a..bcc04aed 100644 --- a/test/mint/tunnel_proxy_test.exs +++ b/test/mint/tunnel_proxy_test.exs @@ -7,7 +7,7 @@ defmodule Mint.TunnelProxyTest do alias Mint.HttpBin @moduletag :proxy - @moduletag :requires_internet_connection + @moduletag :integration test "200 response - http://httpbin.org" do # Ensure we only match relevant messages diff --git a/test/mint/unsafe_proxy_test.exs b/test/mint/unsafe_proxy_test.exs index 9faf1c68..667d38e7 100644 --- a/test/mint/unsafe_proxy_test.exs +++ b/test/mint/unsafe_proxy_test.exs @@ -6,7 +6,7 @@ defmodule Mint.UnsafeProxyTest do alias Mint.HttpBin @moduletag :proxy - @moduletag :requires_internet_connection + @moduletag :integration test "200 response - http://httpbin.org" do assert {:ok, conn} = diff --git a/test/test_helper.exs b/test/test_helper.exs index 52ecea04..b231f47b 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,4 @@ -ExUnit.start(exclude: :proxy) +ExUnit.start(exclude: [:integration, :proxy]) Application.ensure_all_started(:ssl) Logger.configure(level: :info)