{"id":15294039,"url":"https://github.com/a3r0id/sonoma","last_synced_at":"2026-01-05T09:06:16.689Z","repository":{"id":57469323,"uuid":"338428319","full_name":"a3r0id/sonoma","owner":"a3r0id","description":" A tiny, programmable http server crafting framework that is built with security and simplicity in mind.","archived":false,"fork":false,"pushed_at":"2021-03-08T04:55:46.000Z","size":94,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-19T15:49:22.503Z","etag":null,"topics":["alternative","api","django","easy","flask","framework","http","php","python","server","simple","socket"],"latest_commit_sha":null,"homepage":"","language":"Python","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/a3r0id.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-12T20:40:21.000Z","updated_at":"2021-07-01T04:07:02.000Z","dependencies_parsed_at":"2022-09-14T17:23:30.012Z","dependency_job_id":null,"html_url":"https://github.com/a3r0id/sonoma","commit_stats":null,"previous_names":["hostinfodev/sonoma"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a3r0id%2Fsonoma","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a3r0id%2Fsonoma/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a3r0id%2Fsonoma/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a3r0id%2Fsonoma/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a3r0id","download_url":"https://codeload.github.com/a3r0id/sonoma/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245301376,"owners_count":20593166,"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":["alternative","api","django","easy","flask","framework","http","php","python","server","simple","socket"],"created_at":"2024-09-30T16:56:03.463Z","updated_at":"2026-01-05T09:06:16.677Z","avatar_url":"https://github.com/a3r0id.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sonoma\n[![PyPI version](https://badge.fury.io/py/sonoma.svg)](https://badge.fury.io/py/sonoma)\n\nA tiny, low-level, programmable http-server-crafting framework that is built with security and simplicity in mind.\n\n----\n\n![](https://imengine.prod.srp.navigacloud.com/?uuid=C31C28DA-402C-4C02-9083-6C8DACCF1556\u0026type=primary\u0026q=72\u0026width=1024)\n\n----\n\n# Setup\n```pip install sonoma```\n\n----\n\n\n# Basic Usage\n\n### Server\n```python\nfrom sonoma import httpServer\n\nserver = httpServer('127.0.0.1', 8888)\n\nserver.run()\n```\n\n### Browser\n```\n                Hello World!\nThis is the default webpage for Sonoma/1.0.x.\n```\n\n----\n\n# Basic Usage: Custom Response\n### Server\n```python\nfrom sonoma import defaults, httpServer\n\nserver = httpServer('127.0.0.1', 8888)\n\ndefaults['defaultResponse'] = \"\"\" \n    \u003c!DOCTYPE html\u003e\u003chtml\u003e\u003chead\u003e\n    \u003cstyle\u003ehtml, body{ margin: 0 auto;text-align:center; }\u003c/style\u003e\n    \u003c/head\u003e\u003cbody\u003e\n    \u003ch1 style=\\\"text-align:center;\\\"\u003eHello World!\u003c/h1\u003e\n    \u003cspan\u003eThis is a modified version of the default webpage for %s.\u003c/span\u003e\n    \u003c/body\u003e\u003c/html\u003e\n    \"\"\" % defaults['serverName'] \n\nserver.run()\n```\n\n### Browser\n```\n                        Hello World!\nThis is a modified version of the default webpage for Sonoma/1.0.12.\n```\n\n----\n\n# Advanced Usage: Custom Handler\n### Server\n```python\nfrom sonoma import httpServer, defaults, sonomaPrint\nfrom sonoma import setCookie, parseCookies\nfrom http import HTTPStatus\n\n\n# DEFINES CUSTOM HANDLER\ndef myHandler(self, REQUEST, CONNECTION):\n    \"\"\"\n    ## Supported Methods:\n    - GET\n    - HEAD\n    \"\"\"\n\n    # UNPACK \"REQUEST\" TUPLE \n    requestStatusLine, requestHeaders, requestBody = REQUEST\n\n    # UNPACK CONNECTION TUPLE\n    client_connection, client_address = CONNECTION\n\n    # LOG THE REQUEST TO STDOUT\n    sonomaPrint(\"%s -\u003e %s Request: %s\" % (str(self.vector), str(client_address), str(requestStatusLine),))\n    \n    #\n    # RESOLVE THE REQUEST METHOD AND RESPOND ACCORDINGLY:\n    #\n    \n    # SERVE GET\n    if requestStatusLine.split()[0].lower() == \"get\":\n\n        # GET OUR DEFAULT HEADERS + STATUS LINE\n        responseStatusLine, responseHeaders = self.httpHeaders(HTTPStatus.OK, contentType=\"html\")\n        \n\n        # CREATE A LIST OF STRINGS OF EACH REQUEST HEADER FOR USE IN OUR EXAMPLE\n        headerStrings = [\"%s: %s\\n\" % (header[0], header[1]) for header in requestHeaders]\n\n\n        # HERES HOW WE CAN WORK WITH COOKIES, BOTH REQUEST AND RESPONSE:\n        requestCookies = parseCookies(requestHeaders) # GET REQUEST COOKIES\n        #sonomaPrint(requestCookies)        \n        \n        setCookie(responseHeaders, \"cookieName\", \"cookieValue\", ['Secure', 'HttpOnly']) # SET A RESPONSE COOKIE\n        #sonomaPrint(responseHeaders)\n\n\n        # CREATE A CUSTOM RESPONSE\n        responseBody = (\"\"\"\n            \u003c!DOCTYPE html\u003e\u003chtml\u003e\u003chead\u003e\n            \u003cstyle\u003ehtml, body{ margin: 0 auto;text-align:center; }\u003c/style\u003e\n            \u003c/head\u003e\u003cbody\u003e\n            \u003ch1 style=\\\"text-align:center;\\\"\u003eHello World!\u003c/h1\u003e\n            \u003cspan\u003eThis is a custom response from %s.\u003c/span\u003e\n            \u003cbr/\u003e\u003cbr/\u003e\n            \u003cspan\u003eRequest Headers:\u003c/span\u003e\n            \u003cbr/\u003e\n            \u003ctextarea cols=\"100\" rows=\"100\" style=\"width: 75%%;height: 100%%;margin: 0 auto;\"\u003e%s\u003c/textarea\u003e\n            \u003c/body\u003e\u003c/html\u003e\n        \"\"\" % (defaults['serverName'], \"\".join(headerStrings))).encode() \n            \n        return (responseStatusLine, responseHeaders, responseBody)\n    \n    # RESPOND WITH 405 STATUS - METHOD NOT ALLOWED\n    else:\n        responseStatusLine, responseHeaders = self.httpHeaders(HTTPStatus.METHOD_NOT_ALLOWED, contentType=\"text\")\n        return (responseStatusLine, responseHeaders, \"\")   \n\n# INITIALIZE THE SERVER BUT SET OUR CUSTOM HANDLER BEFORE RUNNING.\nserver = httpServer('127.0.0.1', 8888)\n\nserver.set_handler(myHandler)\n\nserver.run()\n```\n\n### Browser\n![](https://cdn.discordapp.com/attachments/796917179987656774/809904244387348490/unknown.png)\n\n----\n\n# Advanced Handler: JSON API\n### Server\n```python\nfrom sonoma import httpServer, HTTPStatus, sonomaPrint, serverGmtTime\nfrom json import dumps\nfrom urllib.parse import urlparse, parse_qs\n\n# DEFINES CUSTOM HANDLER\ndef myHandler(self, REQUEST, CONNECTION):\n    \"\"\"\n    ## Supported Methods:\n    - GET\n    - HEAD\n    \"\"\"\n\n    # UNPACK \"REQUEST\" TUPLE \n    requestStatusLine, requestHeaders, requestBody = REQUEST\n\n    # UNPACK CONNECTION TUPLE\n    client_connection, client_address = CONNECTION\n\n    # LOG THE REQUEST TO STDOUT\n    sonomaPrint(\"%s -\u003e %s Request: %s\" % (str(self.vector), str(client_address), str(requestStatusLine),))\n    \n    #\n    # RESOLVE THE REQUEST METHOD AND RESPOND ACCORDINGLY:\n    #\n    \n    # SERVE GET-JSON\n    if requestStatusLine.split()[0].lower() == \"get\":\n\n        # GET OUR JSON HEADERS + STATUS LINE\n        responseStatusLine, responseHeaders = self.httpHeaders(HTTPStatus.OK, contentType=\"json\")\n        \n        # PARSE THE GET QUERY\n        try:\n            clientQuery = parse_qs(urlparse(requestStatusLine.split()[1]).query)\n        except:\n            # IF QUERY CANNOT BE PARSED THEN RESPOND WITH AN ERROR MESSAGE\n            return (responseStatusLine, responseHeaders, dumps({'error': 'Invalid url query!'}))\n\n        # GET TIME FOR ONE OF OUR SERVERSIDE PROCESSING EXAMPLES\n        tstamp = serverGmtTime()\n\n        responseBody = dumps({\n            'query': clientQuery,\n            'time_gmt': tstamp\n            }, indent=4)\n            \n        return (responseStatusLine, responseHeaders, responseBody)\n    \n    # RESPOND WITH 405 STATUS - METHOD NOT ALLOWED\n    else:\n        responseStatusLine, responseHeaders = self.httpHeaders(HTTPStatus.METHOD_NOT_ALLOWED, contentType=\"text\")\n        return (responseStatusLine, responseHeaders, \"\")   \n\n# INITIALIZE THE SERVER BUT SET OUR CUSTOM HANDLER BEFORE RUNNING.\nserver = httpServer('127.0.0.1', 8888)\n\nserver.set_handler(myHandler)\n\nserver.run()\n```\n### Browser\n\u003e http://127.0.0.1:8888/?testQuery=1\n```json\n{\n    \"query\": {\n        \"testQuery\": [\n            \"1\"\n        ]\n    },\n    \"time_gmt\": \"Sat, 20 Feb 2021 20:18:51 GMT\"\n}\n```\n# Conclusion\n\n- Adding better documentation soon!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa3r0id%2Fsonoma","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa3r0id%2Fsonoma","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa3r0id%2Fsonoma/lists"}