From 3ba9a4cf4797d220c65382dab168bec9edf3ec7d Mon Sep 17 00:00:00 2001 From: bvn13 Date: Sun, 16 Mar 2025 22:59:25 +0300 Subject: [PATCH] pygateway --- .vscode/launch.json | 25 +++ Dockerfile | 59 +++++ README.md | 3 + docker-compose.yaml | 12 ++ poetry.lock | 201 ++++++++++++++++++ pygateway/__init__.py | 0 pygateway/__main__.py | 40 ++++ .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 148 bytes .../__pycache__/__main__.cpython-313.pyc | Bin 0 -> 1231 bytes pygateway/__pycache__/proxy.cpython-313.pyc | Bin 0 -> 5854 bytes pygateway/log/__init__.py | 0 .../log/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 156 bytes .../log/__pycache__/logger.cpython-313.pyc | Bin 0 -> 856 bytes pygateway/log/logger.py | 18 ++ pygateway/proxy.py | 105 +++++++++ pyproject-original.toml | 24 +++ pyproject.toml | 24 +++ 17 files changed, 511 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yaml create mode 100644 poetry.lock create mode 100644 pygateway/__init__.py create mode 100644 pygateway/__main__.py create mode 100644 pygateway/__pycache__/__init__.cpython-313.pyc create mode 100644 pygateway/__pycache__/__main__.cpython-313.pyc create mode 100644 pygateway/__pycache__/proxy.cpython-313.pyc create mode 100644 pygateway/log/__init__.py create mode 100644 pygateway/log/__pycache__/__init__.cpython-313.pyc create mode 100644 pygateway/log/__pycache__/logger.cpython-313.pyc create mode 100644 pygateway/log/logger.py create mode 100644 pygateway/proxy.py create mode 100644 pyproject-original.toml create mode 100644 pyproject.toml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f4600b5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "111", + "type": "debugpy", + "request": "launch", + "program": "poetry run python -m pygateway -l 12345 -rh 127.0.0.1 -rp 8000", + "console": "integratedTerminal", + "justMyCode": true + }, + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/pygateway/__main__.py", + "console": "integratedTerminal", + "args": ["-l", "12345", "-rh", "127.0.0.1", "-rp", "9000"], + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..083dbbb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,59 @@ +# https://stackoverflow.com/a/57374374/2798461 + +####################################################################################################################### +FROM python:3.13-alpine AS dist +ENV PYTHONUNBUFFERED=1 + +RUN apk update +# Configure Poetry +ENV POETRY_VERSION=2.1.1 +ENV POETRY_HOME=/opt/poetry +ENV POETRY_VENV=/opt/poetry-venv +ENV POETRY_CACHE_DIR=/opt/.cache +# Install poetry separatedly from system interpreter +RUN python3 -m venv $POETRY_VENV \ + && $POETRY_VENV/bin/pip install -U pip setuptools \ + && $POETRY_VENV/bin/pip install poetry==${POETRY_VERSION} + +# Add `poetry` to PATH +ENV PATH="${PATH}:${POETRY_VENV}/bin" + + +####################################################################################################################### +FROM dist AS building + +WORKDIR /app +# Install dependencies +COPY pyproject.toml ./ +COPY README.md /app/README.md +COPY pygateway /app/pygateway +RUN cat pyproject.toml | grep -v package-mode > pyproject.toml2 +RUN rm pyproject.toml +RUN mv pyproject.toml2 pyproject.toml +RUN poetry --version +RUN poetry lock +RUN poetry install + + +####################################################################################################################### +FROM building AS runner + +# COPY pygateway /app/pygateway + +ENV PYTHONPATH=/app + +WORKDIR /app + +ARG PORT_TO_LISTEN +ARG REMOTE_HOST +ARG REMOTE_PORT +ARG ALLOWED_CLIENT_IP + +ENV PORT_TO_LISTEN=${PORT_TO_LISTEN} +ENV REMOTE_HOST=${REMOTE_HOST} +ENV REMOTE_PORT=${REMOTE_PORT} +ENV ALLOWED_CLIENT_IP=${ALLOWED_CLIENT_IP} + +RUN echo "poetry run python3 -m pygateway" > run.sh +RUN chmod u+x run.sh +ENTRYPOINT [ "sh", "run.sh" ] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dbef9e0 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PyGateway + +- simple gateway with restriction by client IP \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..4dd764d --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,12 @@ +services: + pygateway: + build: + context: ./ + restart: unless-stopped + ports: + - 8001:8080 + environment: + - PORT_TO_LISTEN=8080 + - REMOTE_HOST=${REMOTE_HOST} + - REMOTE_PORT=8080 + - ALLOWED_CLIENT_IP=192.168.0.1,192.168.0.2 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..18cb3d7 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,201 @@ +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. + +[[package]] +name = "certifi" +version = "2025.1.31" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "docopt" +version = "0.6.2" +description = "Pythonic argument parser, that will make you smile" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, +] + +[[package]] +name = "httpserver" +version = "1.1.0" +description = "Asyncio implementation of an HTTP server" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "httpserver-1.1.0-py2.py3-none-any.whl", hash = "sha256:ed8845192b1b282753e529feed054b0ccc7a7ab64a8e9632b13cc73108eeb30c"}, + {file = "httpserver-1.1.0.tar.gz", hash = "sha256:5bc3daf82512f2f0b311cca68d8ea7a3918c7520d266ce1b8660ed46c478c2e0"}, +] + +[package.dependencies] +docopt = "*" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.13" +content-hash = "d836041d0f4457d35d4a2e56655a64e3e46f78a4f0a31f3450c365545501fb43" diff --git a/pygateway/__init__.py b/pygateway/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pygateway/__main__.py b/pygateway/__main__.py new file mode 100644 index 0000000..76e55f3 --- /dev/null +++ b/pygateway/__main__.py @@ -0,0 +1,40 @@ +import os +import signal +import sys +from pygateway.proxy import Proxy + + +# parser = argparse.ArgumentParser() +# parser.add_argument("-l", "--listen", help="Port to listen", required=True) +# parser.add_argument("-rh", "--remote-host", help="Remote host", required=True) +# parser.add_argument("-rp", "--remote-port", help="Remote port", required=True) +# args = parser.parse_args() + + + +class Args: + listen: int + remote_host: str + remote_port: int + allowed_ips: str + def __init__(self): + self.listen = os.getenv('PORT_TO_LISTEN') + self.remote_host = os.getenv('REMOTE_HOST') + self.remote_port = os.getenv('REMOTE_PORT') + self.allowed_ips = os.getenv('ALLOWED_CLIENT_IP') + + +def _exit_now(signum, frame): + sys.exit(0) + + +if __name__ == '__main__': + args = Args() + proxy = Proxy( + port_to_listen=args.listen, + remote_host=args.remote_host, + remote_port=args.remote_port, + allowed_ips=args.allowed_ips + ) + signal.signal(signal.SIGTERM, _exit_now) + proxy.start_server() \ No newline at end of file diff --git a/pygateway/__pycache__/__init__.cpython-313.pyc b/pygateway/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f2a190a80a532a58a310ec6dbc518c213e1fab9 GIT binary patch literal 148 zcmey&%ge<81T%kLO9#=9K?DpiLK&Y~fQ+dO=?t2Tek&P@n1H;`AgNo%`WgATsrpG} zd4|UNDXC?tIr#cRI~hLd6ZvVO0>Uw`lSzVB<-3WsGtd&cXkFxY+dGtU{`{&j3-HLYI3dnIeE)o6C zBr^$)_-C=XT&?+%#abYlEEa4Bm1~h%6FZ7yDjuFo_z29SqD2B2J+2fm%GUTK&ey`p zBt|9ELCL&nJGG$d0>uSXV08#A>Ubz%;8ACPJgtUZt*bO`fCn^tar_iCZj^piENDL< z;hVwCj8@6pRhvMxTyQ85>_V+Ht4rC8JQ*^N&pgQ?@RC` z!Wz{bQ}^abg5I=rHlRbmG>Iq)7&vDc*hXI6MiH~W5b;~={a`j661k0Yl@cmDvrLDH z;Oa368QH8(x4X}X?6M5gQLyZm%<3xBG3YuGylQYS^~CHOC!%)m?%aerO+vO~(2A+~ zBEl~LWP5Mw)HybgZ~9M?+^ zf}yqA4u9%%D7w}EV(|Ik>kG~CsmA!!_I#!}pKs7#s8FBSh4Sk3%JllhXTwj2w-O)V Vm0c(`;Xnfpe1x%m5x}_H=Wl#l8Gir( literal 0 HcmV?d00001 diff --git a/pygateway/__pycache__/proxy.cpython-313.pyc b/pygateway/__pycache__/proxy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de503996babcc41f7e7a2cccc76c17f8904cb3cc GIT binary patch literal 5854 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 literal 0 HcmV?d00001 diff --git a/pygateway/log/__init__.py b/pygateway/log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pygateway/log/__pycache__/__init__.cpython-313.pyc b/pygateway/log/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9482453a83f0f40780d9101d87c95934de524c8d GIT binary patch literal 156 zcmey&%ge<81lumoNe9u7K?DpiLK&Y~fQ+dO=?t2Tek&P@n1H;`AgNo{`WgATsrpG} zd4|UNDXC?tIr#4|wIB}lBqf&%@V{B-^J_{_Y_lK6PNg34PQHo5sJr8%i~ WMXW%xKz0>_7$2D#85xV1fh+*~z$S74 literal 0 HcmV?d00001 diff --git a/pygateway/log/__pycache__/logger.cpython-313.pyc b/pygateway/log/__pycache__/logger.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e92240bfcf5d17c25c934b81db9a512f13ebb86 GIT binary patch literal 856 zcmZ`%&1(}u6o0d`*{`N8rHEpSF4cuBT7$HL4eF)T&ozatDT)EvCYv=iyBlV-*pc0sIg~ zpOh2O`^do#xZt8kK#nycsDeszKlPuU0#~}hI|(AVf~#483Y4^C&!FT{D5cW}y#Txe zcI66oGjrJdPwgynm5So36?Im2wezqduOOlY$ku;~W<{nui}zkQ3rh>m##1Zj(Q48N zeVbmjav}Ar)UKJYO)J;**ZgMW@yPT|ntD=$7OY%-HL4axL>SFR!fC5ku3B81=H81h&VZt)$6oy-`0wPXYl?J{Sb1b;`go_<` zei|wbW}SGapbY)nMp9WSWlqyiK^gTkOejm2!A3>Cl-i7gpb-U35B$X8*e6V*9EtqK&aTrG)*>_e;J26T)yKO44DUfrxozSTxXXg>h}Q>E9ve zxG8>1TH&IL5kiMBatQj7W^C#2^zAGA+J$zegSE}uo$UCQ^VVrEbQM6_0gUg#cxTA( kWG{A%(ch|!q;3|b>`#*)CVv4&quaPE)b7k5q0;Gp0q@4XLjV8( literal 0 HcmV?d00001 diff --git a/pygateway/log/logger.py b/pygateway/log/logger.py new file mode 100644 index 0000000..9f5534b --- /dev/null +++ b/pygateway/log/logger.py @@ -0,0 +1,18 @@ +import logging +import os + +LEVEL = os.getenv('LOG_LEVEL') +if LEVEL is None: + LEVEL = logging.INFO + +#logging.basicConfig(format="%(asctime)s | %(name)s | %(levelname)s | %(message)s") +logging.basicConfig( + format='%(asctime)s,%(msecs)d | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s', + datefmt='%Y-%m-%d:%H:%M:%S', + level=LEVEL +) + +def logger(pkg: str, level:int = logging.INFO): + lgr = logging.getLogger(pkg) + lgr.setLevel(level) + return lgr \ No newline at end of file diff --git a/pygateway/proxy.py b/pygateway/proxy.py new file mode 100644 index 0000000..fb67520 --- /dev/null +++ b/pygateway/proxy.py @@ -0,0 +1,105 @@ + +from http.server import BaseHTTPRequestHandler, HTTPServer + +import requests +from pygateway.log.logger import logger + + +log = logger(__name__) + +class Proxy: + port_to_listen:int + remote_host:str + remote_port:int + allowed_ips:list + + def __init__(self, port_to_listen:int, remote_host:str, remote_port:int, allowed_ips=None): + self.port_to_listen = int(port_to_listen) + self.remote_host = str(remote_host) + self.remote_port = int(remote_port) + if isinstance(allowed_ips, str): + self.allowed_ips = allowed_ips.split(',') + elif isinstance(allowed_ips, list): + self.allowed_ips = allowed_ips + else: + raise Exception("Allowed IPS must be string or list!") + + def start_server(self): + settings = self + class ProxyHTTPRequestHandler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.0" + + def do_GET(self): + self._handle_request("get", requests.get) + + def do_DELETE(self): + self._handle_request("delete", requests.delete) + + def do_POST(self): + self._handle_request("post", requests.post) + + def do_PUT(self): + self._handle_request("put", requests.put) + + def do_PATCH(self): + self._handle_request("patch", requests.patch) + + def _handle_request(self, method, requests_func): + # TODO: log request + url = self._resolve_url() + x_forwarded_for = self.headers['x-forwarded-for'] if 'x-forwarded-for' in self.headers else None + x_real_ip = self.headers['x_real_ip'] if 'x_real_ip' in self.headers else None + if (url is None): + log.warning(f"Unable to resolve the URL {self.path}") + self.send_response(404) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write('{"status": "Bad request"}'.encode('utf-8')) + return + if (not self.client_address[0] in settings.allowed_ips + and not x_forwarded_for in settings.allowed_ips + and not x_real_ip in settings.allowed_ips): + self.send_response(401) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write('{"status": "Not allowed"}'.encode('utf-8')) + return + + body = None + if 'content-length' in self.headers: + body = self.rfile.read(int(self.headers["content-length"])) + headers = dict(self.headers) + + # Set any custom headers that would be provided through API Management inbound policies + # headers['Api-Version'] = 'v1' + # headers['Api-Path'] = '/internal' + + resp = requests_func(url, data=body, headers=headers) + + self.send_response(resp.status_code) + for key in headers: + self.send_header(key, headers[key]) + self.end_headers() + self.wfile.write(resp.content) + + def _resolve_url(self): + url = self.path + log.info(f"Request: {url}") + is_https = url.startswith('https://') + dest_uri = url.replace('http://','').replace('https://', '') + start_pos = dest_uri.find('/') + dest_url = '' + if is_https: + dest_url = 'https://' + else: + dest_url = 'http://' + dest_url = dest_url + f'{settings.remote_host}:{settings.remote_port}' + if start_pos >= 0: + dest_url = dest_url + dest_uri[start_pos:] + return dest_url + + server_address = ('', self.port_to_listen) + self.httpd = HTTPServer(server_address, ProxyHTTPRequestHandler) + log.info(f"Start listen {self.port_to_listen}, remote host {self.remote_host}, remote port {self.remote_port}, allowed_ips={self.allowed_ips}") + self.httpd.serve_forever() + diff --git a/pyproject-original.toml b/pyproject-original.toml new file mode 100644 index 0000000..e65e9c9 --- /dev/null +++ b/pyproject-original.toml @@ -0,0 +1,24 @@ +[project] +name = "pygateway" +version = "0.1.0" +description = "" +authors = [ + {name = "bvn13",email = "from.github@bvn13.me"} +] +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "httpserver (>=1.1.0,<2.0.0)", + "requests (>=2.32.3,<3.0.0)" +] +packages = [ + { include = "pygateway" } +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +package-mode = false \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e65e9c9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "pygateway" +version = "0.1.0" +description = "" +authors = [ + {name = "bvn13",email = "from.github@bvn13.me"} +] +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "httpserver (>=1.1.0,<2.0.0)", + "requests (>=2.32.3,<3.0.0)" +] +packages = [ + { include = "pygateway" } +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +package-mode = false \ No newline at end of file