{"id":21447405,"url":"https://github.com/luisgabrielroldan/ex3mer","last_synced_at":"2025-10-04T18:02:35.739Z","repository":{"id":49126642,"uuid":"357733940","full_name":"luisgabrielroldan/ex3mer","owner":"luisgabrielroldan","description":"Ex3mer is a library for building streams from HTTP resources","archived":false,"fork":false,"pushed_at":"2021-06-27T22:57:11.000Z","size":30,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-01-23T11:34:43.697Z","etag":null,"topics":["client","elixir","http","stream"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luisgabrielroldan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-14T01:14:30.000Z","updated_at":"2021-06-27T22:57:10.000Z","dependencies_parsed_at":"2022-09-05T13:41:12.144Z","dependency_job_id":null,"html_url":"https://github.com/luisgabrielroldan/ex3mer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisgabrielroldan%2Fex3mer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisgabrielroldan%2Fex3mer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisgabrielroldan%2Fex3mer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisgabrielroldan%2Fex3mer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luisgabrielroldan","download_url":"https://codeload.github.com/luisgabrielroldan/ex3mer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243960449,"owners_count":20375101,"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":["client","elixir","http","stream"],"created_at":"2024-11-23T03:09:56.250Z","updated_at":"2025-10-04T18:02:30.661Z","avatar_url":"https://github.com/luisgabrielroldan.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ex3mer\n\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/luisgabrielroldan/ex3mer/Elixir%20Tests%20\u0026%20Dialyzer)\n[![github.com](https://img.shields.io/github/last-commit/luisgabrielroldan/ex3mer.svg)](https://github.com/luisgabrielroldan/ex3mer/commits/master)\n\nEx3mer is a library for building streams from HTTP resources\n\n\n*(THIS IS A WORK IN PROGRESS)*\n\n## Basic operation\n\n#### Example for S3\n\n```elixir\nEx3mer.S3.get_object(\"my-bucket\", \"path/to/object\")\n|\u003e Ex3mer.stream!()\n|\u003e ...\n```\n\n#### Generic download\n\n```elixir\nEx3mer.download(:get, \"http://example.com/path/to/file.zip\")\n|\u003e Ex3mer.stream!()\n|\u003e ...\n```\n\n## Examples\n\n### Events stream\n\n`Ex3mer.stream!/2` returns a stream of event tuples:\n  \n```\n  iex(1)\u003e Ex3mer.download(:get, \"http://example.com/path/to/file.zip\") \\\n  ...(1)\u003e |\u003e Ex3mer.stream!() \\\n  ...(1)\u003e |\u003e Enum.to_list()\n[\n  status: 200,\n  headers: [\n    {\"Date\", \"Sat, 17 Apr 2021 02:57:29 GMT\"},\n    {\"Last-Modified\", \"Wed, 12 Sep 2018 23:16:15 GMT\"},\n    {\"Accept-Ranges\", \"bytes\"},\n    {\"Content-Type\", \"application/zip\"},\n    {\"Content-Length\", \"1435\"},\n    {\"Server\", \"AmazonS3\"}\n  ],\n  chunk: \u003c\u003c80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 73, 189, 41, 77, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 6, 0, 28, 0, 102, 105, 108, 101, 115, 47, 85, 84, 9, 0, 3,\n    218, 175, 149, 91, 178, 156, ...\u003e\u003e,\n  chunk: \u003c\u003c32, 205, 24, 214, 136, 89, 42, 235, 113, 172, 7, 27, 1, 56, 0, 165,\n    65, 169, 75, 214, 132, 130, 19, 114, 145, 110, 143, 129, 107, 54, 0, 40,\n    204, 197, 64, 102, 206, 182, 95, 183, 30, 174, 73, 110, 126, 118, ...\u003e\u003e\n]\n```\n\nThe first two items emitted are the status and the response of headers.  The rest of the items are just chunks events.\nIn case of disconnection, a new request is made transparently but no status/header items are sent to the stream consumer.\n\n### Data stream\n\n`Ex3mer.stream_data!/2` returns a stream of chunks. If an error occurs an `%Ex3mer.Error{}` is raised.\n\n```\n  iex(1)\u003e Ex3mer.Download.from(:get, \"http://example.com/path/to/file.zip\") \\\n  ...(1)\u003e |\u003e Ex3mer.stream_data!() \\\n  ...(1)\u003e |\u003e Enum.to_list()\n[\n  \u003c\u003c80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 73, 189, 41, 77, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 6, 0, 28, 0, 102, 105, 108, 101, 115, 47, 85, 84, 9, 0, 3,\n    218, 175, 149, 91, 178, 156, ...\u003e\u003e,\n  \u003c\u003c32, 205, 24, 214, 136, 89, 42, 235, 113, 172, 7, 27, 1, 56, 0, 165,\n    65, 169, 75, 214, 132, 130, 19, 114, 145, 110, 143, 129, 107, 54, 0, 40,\n    204, 197, 64, 102, 206, 182, 95, 183, 30, 174, 73, 110, 126, 118, ...\u003e\u003e\n]\n```\n\n### Create a proxy endpoint to S3 with Phoenix\n\n```elixir\n# On router\n\n    get \"/proxy/*path\", ProxyController, :show\n\n\n# controllers/proxy_controller.ex\ndefmodule MyAppWeb.ProxyController do\n  use MyAppWeb, :controller\n\n  def show(conn, %{\"path\" =\u003e path}) do\n    path = Enum.join(path, \"/\")\n\n    \"my-bucket\"\n    |\u003e Ex3mer.S3.get_object(path)\n    |\u003e Ex3mer.stream!()\n    |\u003e Enum.reduce_while(conn, fn\n      {:status, 200}, conn -\u003e\n        conn =\n          conn\n          |\u003e put_resp_content_type(\"application/octet-stream\")\n          |\u003e send_chunked(200)\n\n        {:cont, conn}\n\n      {:status, _}, conn -\u003e\n        {:halt, send_resp(conn, 404, \"Not found\")}\n\n      {:headers, _}, conn -\u003e\n        {:cont, conn}\n\n      {:chunk, data}, conn -\u003e\n        case chunk(conn, data) do\n          {:ok, conn} -\u003e\n            {:cont, conn}\n\n          {:error, :closed} -\u003e\n            {:halt, conn}\n        end\n    end)\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisgabrielroldan%2Fex3mer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluisgabrielroldan%2Fex3mer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisgabrielroldan%2Fex3mer/lists"}