{"id":20715369,"url":"https://github.com/p3k/json3k","last_synced_at":"2025-04-23T10:44:15.475Z","repository":{"id":3333894,"uuid":"4377807","full_name":"p3k/json3k","owner":"p3k","description":"p3k.org JSONP Web Services","archived":false,"fork":false,"pushed_at":"2024-12-08T23:30:17.000Z","size":83,"stargazers_count":2,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T23:22:46.454Z","etag":null,"topics":["http-proxy","json","jsonp","mod-wsgi","mod-wsgi-py3","proxy","pupdb","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","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/p3k.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-05-19T11:39:16.000Z","updated_at":"2024-12-08T23:29:40.000Z","dependencies_parsed_at":"2024-11-22T11:35:26.647Z","dependency_job_id":null,"html_url":"https://github.com/p3k/json3k","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/p3k%2Fjson3k","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p3k%2Fjson3k/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p3k%2Fjson3k/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p3k%2Fjson3k/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/p3k","download_url":"https://codeload.github.com/p3k/json3k/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250419387,"owners_count":21427592,"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":["http-proxy","json","jsonp","mod-wsgi","mod-wsgi-py3","proxy","pupdb","python","python3"],"created_at":"2024-11-17T02:38:11.812Z","updated_at":"2025-04-23T10:44:15.458Z","avatar_url":"https://github.com/p3k.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# p3k.org’s JSON/P Services\n\nFor Python3 / [mod_wsgi](https://modwsgi.readthedocs.io).\n\n```shell\n# A virtual Python environment is automatically created in the .venv directory\n$ make \u0026\u0026 make server\n# — or —\n$ make wgsi \u0026\u0026 make wsgi-server\n```\n\n\u003e 💡 [Integration with Google AppEngine](https://github.com/p3k/json3k/tree/gae) is no longer supported.\n\n## Roxy\n\nRoxy is a simple HTTP proxy returning the response of an HTTP request as JSON data:\n\n```shell\ncurl -G --data-urlencode 'url=https://postman-echo.com/time/now' \\\n   'http://localhost:8000/roxy'\n```\n\n```json\n{\n  \"content\": \"Mon, 06 Jan 2020 07:26:58 GMT\",\n  \"headers\": {\n    \"Content-Encoding\": \"gzip\",\n     \"Content-Type\": \"text/html; charset=utf-8\",\n     \"Date\": \"Mon, 06 Jan 2020 07:26:58 GMT\",\n     \"ETag\": \"W/\\\"1d\\\"\",\n     \"Server\": \"nginx\",\n     \"set-cookie\": \"sails.sid=s%3AS2fABSVzWnUuBKmQoq5LTwFIf7_QN_NG.xmjFxEuq5w2mVp9DLrknr6tNryVW4JnGO4u5N%2F8dk58; Path=/; HttpOnly\",\n     \"Vary\": \"Accept-Encoding\",\n     \"Content-Length\": \"49\",\n     \"Connection\": \"Close\",\n     \"X-Roxy-Url\": \"https://postman-echo.com/time/now\",\n     \"X-Roxy-Status\": 200\n  }\n}\n```\n\nThe additional header `X-Roxy-Url` contains either the final URL, in case the request has been redirected, or the original URL otherwise; `X-Roxy-Status` contains the original HTTP status code which might differ from the one returned by a HTTP server caching Roxy responses (which is recommended).\n\n```shell\ncurl -Gi --data-urlencode 'url=https://postman-echo.com/status/404' \\\n  'http://localhost:8000/roxy'\n```\n\n```plain\nHTTP/1.1 404 NOT FOUND\nDate: Sun, 15 Nov 2020 21:08:27 GMT\nServer: Apache\nContent-Length: 79\nAccess-Control-Allow-Origin: *\nX-Roxy-Status: 404\nX-Roxy-Error: Not Found\nExpires: Sun, 15 Nov 2020 21:09:27 GMT\nConnection: close\nContent-Type: application/json\n\n{\"content\": \"\", \"headers\": {\"X-Roxy-Status\": 404, \"X-Roxy-Error\": \"Not Found\"}}\n```\n\nIn the HTTP headers sent by Roxy (not to be confused with those in the JSON payload) the additional `X-Roxy-*` headers mentioned above are included, too.\n\nFinally, in case of an error `X-Roxy-Error` contains a more or less descriptive error message, depending on the cause (HTTP status code, application issue etc.)\n\n```shell\ncurl -G --data-urlencode 'url=https://unknown.domain' \\\n  'http://localhost:8000/roxy'\n```\n\n```json\n{\n  \"content\": \"\",\n  \"headers\": {\n    \"X-Roxy-Status\": 500,\n    \"X-Roxy-Error\": \"\u003curlopen error [Errno -2] Name or service not known\u003e\"\n  }\n}\n```\n\n### JSONP\n\n```shell\ncurl -G --data-urlencode 'url=https://postman-echo.com/time/now' \\\n  'http://localhost:8000/roxy?callback=evaluate'\n```\n\n```js\nevaluate({\"content\": \"Mon, 06 Jan 2020 07:30:53 GMT\", \"headers\": {\"Content-Encoding\": \"gzip\", \"Content-Type\": \"text/html; charset=utf-8\", \"Date\": \"Mon, 06 Jan 2020 07:30:53 GMT\", \"ETag\": \"W/\\\"1d\\\"\", \"Server\": \"nginx\", \"set-cookie\": \"sails.sid=s%3AsPZWnJe5WvmBOFj4iIydYgPGVcx-zccy.VKP6VA7uRXxkYqk%2FuwCCR9aUnMnb2BfmppSs5sC92es; Path=/; HttpOnly\", \"Vary\": \"Accept-Encoding\", \"Content-Length\": \"49\", \"Connection\": \"Close\", \"X-Roxy-Url\": \"https://postman-echo.com/time/now\", \"X-Roxy-Status\": 200}})\n```\n\n---\n\n## Ferris\n\nFerris is a simple referrer counter incrementing the hits for each registered URL. Each referrer is assigned to a group which eventually can be requested to provide the list of total hits per referrer in descending order.\n\n```shell\ncurl -Gi --data-urlencode 'url=http://host.dom' 'http://localhost:8000/ferris?group=foo'\n\nHTTP/1.0 201 CREATED\nContent-Type: text/html; charset=utf-8\nContent-Length: 1\nServer: Werkzeug/0.16.0 Python/3.6.9\nDate: Sat, 21 Dec 2019 17:20:52 GMT\n\n1\n```\n\nThe response body contains the current hit counter of the referrer URL.\n\n```shell\ncurl -G --data-urlencode 'url=http://other.server' 'http://localhost:8000/ferris?group=foo'\n1\n\n!! # repeat last command\n2\n\n!!\n3\n```\n\nSending a request without a URL, only with a group (which is required), Ferris returns the list of referrers recorded so far:\n\n```shell\ncurl 'http://localhost:8000/ferris?group=foo'\n```\n\n```json\n[\n  {\n    \"url\": \"http://other.server\",\n    \"hits\": 3,\n    \"date\": 1576949054453598,\n    \"metadata\": {}\n  },\n  {\n    \"url\": \"http://host.dom\",\n    \"hits\": 1,\n    \"date\": 1576948808457560,\n    \"metadata\": {}\n  }\n]\n```\n\nIt is possible to add metadata to a referrer simply by appending it JSON-encoded to the ping URL:\n\n```shell\ncurl -G --data-urlencode 'metadata={\"foo\":[\"bar\",\"baz\"]}' --data-urlencode 'url=https://host.dom' 'http://localhost:8000/ferris?group=meta'\n1\n```\n\n```shell\ncurl 'http://localhost:8000/ferris?group=meta'\n```\n\n```json\n[\n  {\n    \"url\": \"http://host.dom\",\n    \"hits\": 1,\n    \"date\": 1578296164821.103,\n    \"metadata\": {\n      \"foo\": [\"bar\", \"baz\"]\n    }\n  }\n]\n```\n\n### JSONP\n\n```shell\ncurl 'http://localhost:8000/ferris?group=foo\u0026callback=evaluate'\n```\n\n```js\nevaluate([{\"url\": \"http://other.server\", \"hits\": 3, \"date\": 1576949054453598}, {\"url\": \"http://host.dom\", \"hits\": 1, \"date\": 1576948808457560}])\n```\n\n### Cleanup\n\nThere is a task URL defined to delete all records of a group to reduce the necessary amount of data storage. This is only allowed from localhost and should be called from a cronjob:\n\n```shell\ncurl 'http://localhost:8000/tasks/ferris?group=foo'\nTrue\n```\n\n## Deployment\n\nRun `make config` to output the corresponding Apache configuration lines:\n\n```shell\n$ make config\nmod_wsgi-express module-config\nLoadModule wsgi_module \"/path/to/.venv/json3k/lib/python3.10/site-packages/mod_wsgi/server/mod_wsgi-py310.cpython-310-x86_64-linux-gnu.so\"\nWSGIPythonHome \"/path/to/.venv/json3k\"\n```\n\nIn current Apache installations, the `LoadModule` line goes into `/etc/apache2/mods-enabled/wsgi.load`, and the other one into `/etc/apache2/mods-enabled/wsgi.conf`.\n\nYou might also need to modify the `WSGISocketPrefix` setting, so Apache does not complain about [insufficient permission to create the socket](https://modwsgi.readthedocs.io/en/develop/user-guides/configuration-issues.html#location-of-unix-sockets):\n\n```apache2\nWSGISocketPrefix /var/run/apache2/wsgi\n```\n\nIn case of multiple applications are being run, [WSIGʼs “daemon” mode](https://modwsgi.readthedocs.io/en/develop/user-guides/configuration-guidelines.html#defining-process-groups) needs to be used:\n\n```apache2\nWSGIDaemonProcess json3k python-home=/path/to/.venv/json3k home=/path/to/json3k\nWSGIScriptAlias /json3k /path/to/json3k/wsgi.py process-group=json3k\n```\n\n---\n\n## License\n\nJSONP Services by Tobi Schäfer are licensed under a Creative Commons Attribution-ShareAlike 3.0 Austria License. Based on a work at \u003chttps://github.com/p3k/json3k\u003e.\n\n\u003chttp://creativecommons.org/licenses/by-sa/3.0/at/deed.en_US\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp3k%2Fjson3k","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fp3k%2Fjson3k","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp3k%2Fjson3k/lists"}