diff --git a/grpc/CHANGELOG.md b/grpc/CHANGELOG.md index 495cd22e..c1bcdfae 100644 --- a/grpc/CHANGELOG.md +++ b/grpc/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fixes + * The Mint adapter now returns `{:error, %GRPC.RPCError{}}` instead of raising when a response's HTTP status is not 200, like the Gun adapter. * The Gun adapter no longer shares a named channel's connection process across Erlang nodes. It was registered in `:global`, so a node connecting with a `:name` already used on another node adopted the remote connection process; `connect/2` returned `{:ok, channel}`, but every RPC on it then raised `ArgumentError` because `GRPC.Stub.call/5` calls `Process.alive?/1` on the connection pid and that raises for remote pids. Connection processes are now registered in the node-local `GRPC.Client.Registry`, so reuse is per node. ## v1.0.4 (2026-0-15) diff --git a/grpc/lib/grpc/client/adapters/mint/connection_process/connection_process.ex b/grpc/lib/grpc/client/adapters/mint/connection_process/connection_process.ex index 0e865d65..3119f9b1 100644 --- a/grpc/lib/grpc/client/adapters/mint/connection_process/connection_process.ex +++ b/grpc/lib/grpc/client/adapters/mint/connection_process/connection_process.ex @@ -249,8 +249,18 @@ if Code.ensure_loaded?(Mint.HTTP) do state end + defp process_response({:status, request_ref, 200}, state) do + State.update_response_status(state, request_ref, 200) + end + defp process_response({:status, request_ref, status}, state) do - State.update_response_status(state, request_ref, status) + state + |> State.update_response_status(request_ref, status) + |> consume_or_cancel_stream( + request_ref, + :error, + GRPC.RPCError.exception(GRPC.Status.internal(), "status got is #{status} instead of 200") + ) end defp process_response({:headers, request_ref, headers}, state) do diff --git a/grpc/test/grpc/adapters/mint/connection_process_test.exs b/grpc/test/grpc/adapters/mint/connection_process_test.exs index d097d561..7c3c6616 100644 --- a/grpc/test/grpc/adapters/mint/connection_process_test.exs +++ b/grpc/test/grpc/adapters/mint/connection_process_test.exs @@ -625,6 +625,34 @@ defmodule GRPC.Client.Adapters.Mint.ConnectionProcessTest do end end + describe "handle_info - non-200 responses" do + setup :quiet_connection + setup :valid_stream_request + setup :valid_stream_response + + test "reports the HTTP status to the stream response process", + %{stream_response_pid: response_pid, state: state} do + # A proxy answering with a plain HTTP error: a HEADERS frame whose only + # header is `:status` (static table index 8) with the value "502". + stream_id = state.conn.next_stream_id - 2 + headers_frame = <<5::24, 0x01, 0x04, stream_id::32, 0x08, 0x03, "502">> + message = {:tcp, state.conn.socket, headers_frame} + + assert {:noreply, new_state} = ConnectionProcess.handle_info(message, state) + + # the connection is untouched, only this request failed + assert Mint.HTTP.open?(new_state.conn) + + # the caller-facing stream response process got the status as an error + response_state = :sys.get_state(response_pid) + + assert [ + {:error, %GRPC.RPCError{status: 13, message: "status got is 502 instead of 200"}} + | _headers + ] = :queue.to_list(response_state.responses) + end + end + describe "retry_timeout/1" do test "returns exponentially increasing timeouts" do t1 = ConnectionProcess.retry_timeout(1)