%%% docker_http.erl -module(docker_http). -export([request/4, stream_request/5]). %% 通过 Unix Socket 调用 Docker API -spec request(Method :: iolist(), Path :: string(), Body :: binary() | undefined, Headers :: list()) -> {ok, StatusCode :: integer(), RespHeaders :: proplists:proplist(), RespBody :: binary()} | {error, any()}. request(Method, Path, Body, Headers) when is_list(Method); is_binary(Method), is_list(Path), is_binary(Body), is_list(Headers) -> SocketPath = "/var/run/docker.sock", %% 使用 gun:open/2 + {local, Path} 方式 case gun:open_unix(SocketPath, #{}) of {ok, ConnPid} -> {ok, http} = gun:await_up(ConnPid), %% 如果 Body 是 undefined,就用 <<>> 代替 BodyBin = case Body of undefined -> <<>>; B when is_binary(B) -> B end, %% 发送 HTTP 请求 StreamRef = gun:request(ConnPid, Method, Path, Headers, BodyBin), receive_response(ConnPid, StreamRef); {error, Reason} -> {error, Reason} end. receive_response(ConnPid, StreamRef) -> receive {gun_response, ConnPid, StreamRef, nofin, Status, Headers} -> receive_body(ConnPid, StreamRef, Status, Headers, <<>>); {gun_response, ConnPid, StreamRef, fin, Status, Headers} -> {ok, Status, Headers, <<>>}; {gun_down, ConnPid, _, Reason, _} -> {error, {http_closed, Reason}} after 5000 -> {error, timeout11} end. receive_body(ConnPid, StreamRef, Status, Headers, Acc) -> receive {gun_data, ConnPid, StreamRef, fin, Data} -> {ok, Status, Headers, <>}; {gun_data, ConnPid, StreamRef, nofin, Data} -> NewAcc = <>, receive_body(ConnPid, StreamRef, Status, Headers, NewAcc) after 10000 -> {error, timeout22} end. %% 通过 Unix Socket 调用 Docker API -spec stream_request(Callback :: any(), Method :: iolist(), Path :: string(), Body :: binary() | undefined, Headers :: list()) -> ok | {error, Reason :: any()}. stream_request(Callback, Method, Path, Body, Headers) when is_list(Method); is_binary(Method), is_list(Path), is_binary(Body), is_list(Headers) -> SocketPath = "/var/run/docker.sock", case gun:open_unix(SocketPath, #{}) of {ok, ConnPid} -> {ok, http} = gun:await_up(ConnPid), %% 如果 Body 是 undefined,就用 <<>> 代替 BodyBin = case Body of undefined -> <<>>; B when is_binary(B) -> B end, %% 发送 HTTP 请求 StreamRef = gun:request(ConnPid, Method, Path, Headers, BodyBin), receive_response(Callback, ConnPid, StreamRef); {error, Reason} -> Callback({error, Reason}), {error, Reason} end. receive_response(Callback, ConnPid, StreamRef) -> receive {gun_response, ConnPid, StreamRef, nofin, _Status, _Headers} -> receive_body(Callback, ConnPid, StreamRef); {gun_down, ConnPid, _, Reason, _} -> Callback({error, Reason}), {error, Reason} after 5000 -> Callback({error, <<"处理超时"/utf8>>}), {error, timeout} end. receive_body(Callback, ConnPid, StreamRef) -> receive {gun_data, ConnPid, StreamRef, fin, Data} -> Callback({message, Data}), ok; {gun_data, ConnPid, StreamRef, nofin, Data} -> Callback({message, Data}), receive_body(Callback, ConnPid, StreamRef) end.