{"id":21657957,"url":"https://github.com/Azolo/websockex","last_synced_at":"2025-07-17T20:32:30.581Z","repository":{"id":20022318,"uuid":"82728029","full_name":"Azolo/websockex","owner":"Azolo","description":"An Elixir Websocket Client","archived":false,"fork":false,"pushed_at":"2023-06-26T18:32:39.000Z","size":357,"stargazers_count":495,"open_issues_count":40,"forks_count":93,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-04-04T08:45:56.375Z","etag":null,"topics":["elixir","websocket","websocket-client"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Azolo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-21T21:17:44.000Z","updated_at":"2024-06-18T12:28:22.198Z","dependencies_parsed_at":"2024-06-18T12:28:20.970Z","dependency_job_id":"c86e9cd7-d4ac-4fbc-9dfc-e41b548ca0d1","html_url":"https://github.com/Azolo/websockex","commit_stats":{"total_commits":288,"total_committers":19,"mean_commits":"15.157894736842104","dds":0.09027777777777779,"last_synced_commit":"4a94f6870528f45d64cdd47bd4374faf52528466"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azolo%2Fwebsockex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azolo%2Fwebsockex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azolo%2Fwebsockex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azolo%2Fwebsockex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Azolo","download_url":"https://codeload.github.com/Azolo/websockex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226304206,"owners_count":17603532,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","websocket","websocket-client"],"created_at":"2024-11-25T09:28:24.607Z","updated_at":"2024-11-25T09:28:27.857Z","avatar_url":"https://github.com/Azolo.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# WebSockex\n\n[![Build Status](https://travis-ci.org/Azolo/websockex.svg?branch=master)](https://travis-ci.org/Azolo/websockex)\n\nAn Elixir Websocket Client.\n\nA simple implementation could be\n\n```elixir\ndefmodule WebSocketExample do\n  use WebSockex\n\n  def start_link(url, state) do\n    WebSockex.start_link(url, __MODULE__, state)\n  end\n\n  def handle_frame({type, msg}, state) do\n    IO.puts \"Received Message - Type: #{inspect type} -- Message: #{inspect msg}\"\n    {:ok, state}\n  end\n\n  def handle_cast({:send, {type, msg} = frame}, state) do\n    IO.puts \"Sending #{type} frame with payload: #{msg}\"\n    {:reply, frame, state}\n  end\nend\n```\n\nSee the `examples/` directory for other examples or take a look at the [documentation][docs].\n\n## Installation\n\nAdd `websockex` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [{:websockex, \"~\u003e 0.4.3\"}]\nend\n```\n\n### With Elixir releases prior to version  1.4\n\nEnsure `websockex` is started before your application:\n\n```elixir\ndef application do\n  [applications: [:websockex]]\nend\n```\n\n## Why WebSockex?\n\nWebSockex was conceived after trying other libraries and realizing that I needed something tested, that actually\nimplemented the spec, provided information about the connection, and could fit into a supervision tree. There was\nnothing that really fit into all those categories, so WebSockex was created.\n\nThere are other libraries out there can fit into some of the categories, but I consider WebSockex the best option if\nyou want a callback inspired approach where most of the protocol workings are abstracted out of your way.\n\nIf you are afraid that WebSockex isn't stable enough or have some other problem with it that you don't feel like\ntelling me about, then I would suggest the excellent [`gun` library][gun_hex]. It's a bit harder to use, and requires\nsome knowledge of the spec. However it is an excellent library.\n\n[gun_hex]: https://hex.pm/packages/gun\n\n## Supervision and Linking\n\nA WebSockex based process is a easily able to fit into any supervision tree. It supports all the necessary capabilites\nto do so. In addition, it supports the `Supervisor` children format introduced in Elixir 1.5. So in any version of\nElixir after 1.5, you can simply do:\n\n```elixir\ndefmodule MyApp.Client do\n  use WebSockex\n\n  def start_link(state) do\n    WebSockex.start_link(\"ws://myapp.ninja\", __MODULE__, state)\n  end\nend\n\ndefmodule MyApp.Application do\n  use Application\n\n  def start(_type, _args) do\n    children = [\n      {MyApp.Client, [\"WebSockex is Great\"]}\n    ]\n\n    Supervisor.start_link(children, strategy: :one_for_one)\n  end\nend\n```\n\nSee the Supervision section in the `WebSockex` module documentation for more details on how this works.\n\nWebSockex also supports both linked(`start_link/3`) and unlinked(`start/3`) processes.\n\n```elixir\niex\u003e {:ok, pid} = WebSockex.start_link(url, __MODULE__, state)\niex\u003e {:ok, pid} = WebSockex.start(url, __MODULE__, state)\n```\n\nHowever, the recommendation is to always use `start_link/3` and if necessary trap exits.\n\nThis is because `start/3` creates a detached process and has the capability to produce zombie processes outside of any\napplication tree. This is generally a good piece of advice for any process, however since a module using WebSockex\nbevhaviour can be written as a self-sustaining tcp connection. I feel like it is even more important to express this\nparticular piece of advice here.\n\n## Telemetry\n\nWebsockex clients emit the following telemetry events:\n\n* `[:websockex, :connected]`\n* `[:websockex, :disconnected]`\n* `[:websockex, :frame, :received]`\n* `[:websockex, :frame, :sent]`\n* `[:websockex, :terminate]`\n\nFor all these events, the measurements is `%{time: System.system_time()}` and they all share common metadata as a map containing the `:conn` and the `:module` keys. For frame events, the metadata also contains the `:frame` key. For disconnections and terminations, it will contain the `:reason` key. \n\n\n## Tips\n### Terminating with :normal after an Exceptional Close or Error\n\nUsually you'll want to negotiate and handle any abnormal close event or error leading to it, as per WS Spec, but there might be cases where you simply want the socket to exit as if it was a normal event, even if it was abruptly closed or another exception was raised. In those cases you can define the terminate callback and return `exit(:normal)` from it.\n```elixir\ndef terminate(reason, state) do\n    IO.puts(\\nSocket Terminating:\\n#{inspect reason}\\n\\n#{inspect state}\\n\")\n    exit(:normal)\nend\n```\n\n## Debugging\n\nWebSockex supports the debugging mechanism for [OTP Special Processes][special_process] provided through the `:sys` module.\n\nSince WebSockex rolls its own Special Process implementation, it's able to provide a lot more information than a regular\n`GenServer`.\n\nIf, for example, I enable tracing with `EchoClient` from the examples (with `Logger` off), I would get this:\n\n```elixir\niex\u003e {:ok, pid} = EchoClient.start_link(debug: [:trace])\n*DBG* #PID\u003c0.371.0\u003e attempting to connect\n*DBG* #PID\u003c0.371.0\u003e sucessfully connected\n{:ok, #PID\u003c0.371.0\u003e}\niex\u003e EchoClient.echo(pid, \"Hello\")\n*DBG* #PID\u003c0.371.0\u003e sending frame: {:text, \"Hello\"}\n:ok\n*DBG* #PID\u003c0.371.0\u003e received frame: {:text, \"Hello\"}\n*DBG* #PID\u003c0.371.0\u003e received frame: :ping\n*DBG* #PID\u003c0.371.0\u003e replying from :handle_ping with :pong\niex\u003e EchoClient.echo(pid, \"Close the things!\")\n*DBG* #PID\u003c0.371.0\u003e sending frame: {:text, \"Close the things!\"}\n:ok\n*DBG* #PID\u003c0.371.0\u003e received frame: {:text, \"Close the things!\"}\n*DBG* #PID\u003c0.371.0\u003e closing with local reason: {:local, :normal}\n*DBG* #PID\u003c0.371.0\u003e sending close frame: {:local, :normal}\n*DBG* #PID\u003c0.371.0\u003e forcefully closed the connection because the server was taking too long close\n```\n\nYou could also enable tracing after a process has started like this:\n\n```elixir\niex\u003e {:ok, pid} = EchoClient.start_link()\niex\u003e :sys.trace(pid, true)\n:ok\niex\u003e EchoClient.echo(pid, \"Hi\")\n*DBG* #PID\u003c0.379.0\u003e sending frame: {:text, \"Hi\"}\n:ok\n*DBG* #PID\u003c0.379.0\u003e received frame: {:text, \"Hi\"}\n```\n\n[special_process]: http://erlang.org/doc/design_principles/spec_proc.html\n[docs]: https://hexdocs.pm/websockex\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAzolo%2Fwebsockex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAzolo%2Fwebsockex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAzolo%2Fwebsockex/lists"}