{"id":13508311,"url":"https://github.com/KazuCocoa/http_proxy","last_synced_at":"2025-03-30T11:31:40.813Z","repository":{"id":2131409,"uuid":"45341166","full_name":"KazuCocoa/http_proxy","owner":"KazuCocoa","description":"http proxy with Elixir. wait request with multi port and forward to each URIs","archived":false,"fork":false,"pushed_at":"2024-06-03T20:23:41.000Z","size":380,"stargazers_count":59,"open_issues_count":5,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-09T08:08:47.292Z","etag":null,"topics":["elixir","http-proxy"],"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/KazuCocoa.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":"2015-11-01T13:21:26.000Z","updated_at":"2024-06-29T23:08:12.000Z","dependencies_parsed_at":"2024-06-20T15:40:54.460Z","dependency_job_id":null,"html_url":"https://github.com/KazuCocoa/http_proxy","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazuCocoa%2Fhttp_proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazuCocoa%2Fhttp_proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazuCocoa%2Fhttp_proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazuCocoa%2Fhttp_proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KazuCocoa","download_url":"https://codeload.github.com/KazuCocoa/http_proxy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246314011,"owners_count":20757450,"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","http-proxy"],"created_at":"2024-08-01T02:00:51.234Z","updated_at":"2025-03-30T11:31:40.343Z","avatar_url":"https://github.com/KazuCocoa.png","language":"Elixir","funding_links":[],"categories":["HTTP"],"sub_categories":[],"readme":"# HttpProxy\n\n![Elixir CI](https://github.com/KazuCocoa/http_proxy/workflows/Elixir%20CI/badge.svg?branch=main)\n[![](https://img.shields.io/hexpm/v/http_proxy.svg?style=flat)](https://hex.pm/packages/http_proxy)\n\n[![codecov](https://codecov.io/gh/KazuCocoa/http_proxy/branch/main/graph/badge.svg)](https://codecov.io/gh/KazuCocoa/http_proxy)\n\nSimple multi HTTP Proxy using Plug. And support record/play requests.\n\n# MY GOAL\n- Record/Play proxied requests\n    - http_proxy support multi port and multi urls on one execution command `mix proxy`.\n- Support VCR\n\n# architecture\n\n```\n           http_proxy\nClient  (server  client)  proxied_server\n  |            |            |\n  | 1.request  |            |\n  |  ------\u003e   | 2.request  |\n  |            |  ------\u003e   |\n  |            |            |\n  |            | 3.response |\n  | 4.response |  \u003c------   |\n  |  \u003c------   |            |\n  |            |            |\n```\n\n1. The client sends a request to http_proxy, then the http_proxy works as a proxy server.\n2. When the http_proxy receives the request from the client, then the http_proxy sends the request to a proxied server, e.g. http://google.com, as a client.\n3. The http_proxy receives responses from the proxied_server, then the http_proxy sets the response into its response to the client.\n4. The Client receives responses from the http_proxy.\n\n# Quick use as http proxy\n## requirement\n- Elixir over 1.7\n\n## set application and deps\n\n- `mix.exs`\n    - `:logger` is option.\n    - `:http_proxy` is not need if you run http_proxy with `HttpProxy.start/0` or `HttpProxy.stop/0` manually.\n\n```elixir\ndef application do\n  [applications: [:logger, :http_proxy]]\nend\n\n...\n\ndefp deps do\n  [\n    {:http_proxy, \"~\u003e 1.4.0\"}\n  ]\nend\n```\n\n## set configuration\n\n- `config/config.exs`\n\n```\nuse Mix.Config\n\nconfig :http_proxy,\n  proxies: [\n             %{port: 8080,\n               to:   \"http://google.com\"},\n             %{port: 8081,\n               to:   \"http://yahoo.com\"}\n            ]\n```\n\n- To manage logger, you should define logger settings like the following.\n\n```\nconfig :logger, :console,\n  level: :info\n```\n\n## solve deps and run a server\n\n```\n$ mix deps.get\n$ mix clean\n$ mix run --no-halt # start proxy server\n```\n\nIf you would like to start production mode, you should run with `MIX_ENV=prod` like the following command.\n\n```\n$ MIX_ENV=prod mix run --no-halt\n```\n\n## launch browser\n\nLaunch browser and open `http://localhost:8080` or `http://localhost:8081`.\nThen, `http://localhost:8080` redirect to `http://google.com` and `http://localhost:8081` do to `http://yahoo.com`.\n\n# Development\n- Copy `pre-commit` hook\n    - `cp hooks/pre-commit ./git/hooks/pre-commit`\n\n# Configuration\n## Customize proxy port\n\n- You can customize a proxy port. For example, if you change a waiting port from `8080` to `4000`, then you can access to `http://google.com` via `http://localhost:4000`.\n\n```\nuse Mix.Config\n\nconfig :http_proxy,\n  proxies: [\n             %{port: 4000,\n               to:   \"http://google.com\"},\n             %{port: 8081,\n               to:   \"http://yahoo.com\"}\n            ]\n```\n\n## Add proxy\n\n- You can add a waiting ports in configuration file. For example, the following setting allow you to access to `http://apple.com` via `http://localhost:8082` in addition.\n\n```\nuse Mix.Config\n\nconfig :http_proxy,\n  proxies: [\n             %{port: 8080,\n               to:   \"http://google.com\"},\n             %{port: 8081,\n               to:   \"http://yahoo.com\"},\n             %{port: 8082,\n               to:   \"http://apple.com\"}\n            ]\n```\n\n## Play and Record mode\n\n- When `:record` and `:play` are `false`, then the http_proxy works just multi port proxy.\n- When `:record` is `true`, then the http_proxy works to record request which is proxied.\n- When `:play` is `true`, then the http_proxy works to play request between this the http_proxy and clients.\n    - You should set JSON files under `mappings` in `play_path`.\n    - `config.proxies.to` must be available URL to succeed generating http client.\n        - https://github.com/KazuCocoa/http_proxy/blob/main/lib/http_proxy/handle.ex#L49\n\n```elixir\nuse Mix.Config\n\nconfig :http_proxy,\n  proxies: [                   # MUST\n             %{port: 8080,     # proxy all request even play or record\n               to:   \"http://google.com\"},\n             %{port: 8081,\n               to:   \"http://yahoo.com\"}\n            ]\n  timeout: 20_000,             # Option, ms to wait http request.\n  record: false,               # Option, true: record requests. false: don't record.\n  play: false,                 # Option, true: play stored requests. false: don't play.\n  export_path: \"test/example\", # Option, path to export recorded files.\n  play_path: \"test/data\"       # Option, path to read json files as response to.\n```\n\n## Example\n### Record request as the following\n\n```json\n{\n  \"request\": {\n    \"headers\": [],\n    \"method\": \"GET\",\n    \"options\": {\n      \"aspect\": \"query_params\"\n    },\n    \"remote\": \"127.0.0.1\",\n    \"request_body\": \"\",\n    \"url\": \"http://localhost:8080/hoge/inu?email=neko\u0026pass=123\"\n  },\n  \"response\": {\n    \"body_file\": \"path/to/body_file.json\",\n    \"cookies\": {},\n    \"headers\": {\n      \"Cache-Control\": \"public, max-age=2592000\",\n      \"Content-Length\": \"251\",\n      \"Content-Type\": \"text/html; charset=UTF-8\",\n      \"Date\": \"Sat, 21 Nov 2015 00:37:38 GMT\",\n      \"Expires\": \"Mon, 21 Dec 2015 00:37:38 GMT\",\n      \"Location\": \"http://www.google.com/hoge/inu?email=neko\u0026pass=123\",\n      \"Server\": \"sffe\",\n      \"X-Content-Type-Options\": \"nosniff\",\n      \"X-XSS-Protection\": \"1; mode=block\"\n    },\n    \"status_code\": 301\n  }\n}\n```\nResponse body will save in \"path/to/body_file.json\".\n\n### Play request with the following JSON data\n\n- Example is https://github.com/KazuCocoa/http_proxy/tree/master/test/data/mappings\n- You can set `path` or `path_pattern` as attribute under `request`.\n    - If `path`, the http_proxy check requests are matched completely.\n    - If `path_pattern`, the http_proxy check requests are matched with Regex.\n- You can set `body` or `body_file` as attribute under `response`.\n    - If `body`, the http_proxy send the body string.\n    - If `body_file`, the http_proxy send the body_file binary as response.\n\n#### `path` and `body` case\n\n```json\n{\n  \"request\": {\n    \"path\": \"/request/path\",\n    \"port\": 8080,\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"body\": \"\u003chtml\u003ehello world\u003c/html\u003e\",\n    \"cookies\": {},\n    \"headers\": {\n      \"Content-Type\": \"text/html; charset=UTF-8\",\n      \"Server\": \"GFE/2.0\"\n    },\n    \"status_code\": 200\n  }\n}\n```\n\n#### `path_pattern` and `body_file` case\n\n- Pattern match with `Regex.match?(Regex.compile!(\"\\A/request.*neko\\z\"), request_path)`\n- `File.read/2` via `file/to/path.json` and respond the binary\n\n```json\n{\n  \"request\": {\n    \"path_pattern\": \"\\A/request.*neko\\z\",\n    \"port\": 8080,\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"body_file\": \"file/to/path.json\",\n    \"cookies\": {},\n    \"headers\": {\n      \"Content-Type\": \"text/html; charset=UTF-8\",\n      \"Server\": \"GFE/2.0\"\n    },\n    \"status_code\": 200\n  }\n}\n```\n\n## dependencies\n\n```\n$ mix xref graph\nlib/http_proxy.ex\n└── lib/http_proxy/supervisor.ex\n    ├── lib/http_proxy/agent.ex\n    │   ├── lib/http_proxy/play/data.ex\n    │   │   ├── lib/http_proxy/agent.ex\n    │   │   └── lib/http_proxy/play/response.ex\n    │   │       ├── lib/http_proxy/play/data.ex\n    │   │       └── lib/http_proxy/utils/file.ex\n    │   └── lib/http_proxy/play/paths.ex\n    │       ├── lib/http_proxy/agent.ex\n    │       └── lib/http_proxy/play/response.ex\n    └── lib/http_proxy/handle.ex\n        ├── lib/http_proxy/play/body.ex\n        ├── lib/http_proxy/play/data.ex\n        ├── lib/http_proxy/play/paths.ex\n        ├── lib/http_proxy/play/response.ex\n        └── lib/http_proxy/record/response.ex\n            ├── lib/http_proxy/format.ex\n            │   └── lib/http_proxy/data.ex (compile)\n            └── lib/http_proxy/utils/file.ex\n```\n\n# TODO\n- [x] record request\n- [x] play request\n- [x] refactor\n- [x] support Regex request path.\n- [x] start/stop http_proxy manually\n- [ ] ~~use vcr~~\n    - integrate https://github.com/parroty/exvcr\n\n# styleguide\n\nhttp://elixir.community/styleguide\n\n# LICENSE\nMIT. Please read LICENSE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKazuCocoa%2Fhttp_proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKazuCocoa%2Fhttp_proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKazuCocoa%2Fhttp_proxy/lists"}