{"id":16906627,"url":"https://github.com/rsms/ghp","last_synced_at":"2025-03-22T10:31:18.415Z","repository":{"id":45715815,"uuid":"155119981","full_name":"rsms/ghp","owner":"rsms","description":"Go Hypertext Preprocessor","archived":false,"fork":false,"pushed_at":"2019-02-06T00:19:44.000Z","size":134,"stargazers_count":38,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-15T21:17:04.744Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":false,"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/rsms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-28T22:01:32.000Z","updated_at":"2024-01-03T14:16:02.000Z","dependencies_parsed_at":"2022-08-04T23:00:15.471Z","dependency_job_id":null,"html_url":"https://github.com/rsms/ghp","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/rsms%2Fghp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fghp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fghp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fghp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsms","download_url":"https://codeload.github.com/rsms/ghp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244943745,"owners_count":20536290,"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":[],"created_at":"2024-10-13T18:43:39.018Z","updated_at":"2025-03-22T10:31:18.035Z","avatar_url":"https://github.com/rsms.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go Hypertext Preprocessor\n\nServe stuff over the interwebs with Go in a PHP-like fashion.\n\n- Simply create and edit .go files in a straight-forward directory structure\n- A directory with a `servlet.go` file is considered an endpoint. `func ServeHTTP(r ghp.Request, w ghp.Response)` will be called to handle HTTP requests.\n- Hot-reloading at runtime without the need to restart a server.\n- Source graph optionally computed live for perfect dependency knowledge — change a source file in a far-away dependency and have appropriate GHP endpoints be recompiled and reloaded.\n- Dead-simple Zero-Downtime Restarts out of the box\n\n\n### GHP page example:\n\n`layout.ghp`:\n\n```html\n\u003chtml\u003e\n  \u003cbody\u003e\n    \u003ch1\u003e{.URL}\u003c/h1\u003e\n    {.Content}\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n`page.ghp`:\n\n```html\n---\nparent: parent.ghp\n---\n\u003cp\u003eTime: {timestamp}\u003c/p\u003e\n```\n\n```\n$ curl -i http://localhost:8001/page.ghp\nHTTP/1.1 200 OK\nDate: Sun, 04 Nov 2018 23:27:01 GMT\nContent-Length: 84\nContent-Type: text/html; charset=utf-8\n\n\u003chtml\u003e\n  \u003cbody\u003e\n    \u003ch1\u003e/page.ghp\u003c/h1\u003e\n    \u003cp\u003eTime: 1541374021\u003c/p\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Servlet example\n\n`bar/servlet.go`:\n\n```go\npackage main\nimport \"ghp\"\n\nfunc ServeHTTP(r ghp.Request, w ghp.Response) {\n  w.WriteString(\"Hello world\")\n}\n```\n\n```\n$ curl -i http://localhost:8001/bar/\nHTTP/1.1 200 OK\nDate: Sun, 04 Nov 2018 23:23:53 GMT\nContent-Length: 11\nContent-Type: text/plain; charset=utf-8\n\nHello world\n```\n\nServlets can additionally provide the optional\n`StartServlet` and `StopServlet` functions, called when a servlet instance\nhas been started and is stopping, respectively.\n\n`StopServlet` is called just after a servlet has been disconnected from\nreceiving any new requests. This might happen when a new instance (version)\nof the same servlet has been started, or when GHP is shutting down.\n\nDuring a graceful shutdown of GHP,\nfor instance from SIGHUP or [ZDR](#zero-downtime-restarts),\nor when a servlet instance is replaced with a newer version, `StopServlet`\nmay block while completing any ongoing work, like shutting down a websocket\nor writing data to disk.\n\n`StartServlet` can be useful for setting up shared resources, or for picking\nup shared state from a past servlet instance.\n\n\n## Zero-Downtime Restarts\n\nGHP supports seamless restarts where the server never stops listening for\nconnections. ZDR is enabled by default and doesn't require you to launch\n`ghp` processes in any special way.\n\n- Coordination is per directory served. i.e. a GHP process serving a certain\n  directory will coordinate with any other GHP process that is launched to\n  serve the same directory.\n- Works by transferring ownership of listener file descriptors via a Unix\n  socket, thus ZDR works on any POSIX system where Unix sockets are enabled.\n- Gracefully shuts down an older process, completeing in-flight requests while\n  at the same the newer process starts serving new requests concurrently.\n- Servlets can hook into this system by simply providing the optional\n  `StopServlet` and `StartServlet` functions.\n- Coordination can be customized using a config file by setting `zdr.group` to\n  a unique string that is unique to the host machine.\n\nTry it with the example app:\n\n```\n# Terminal 1                            Terminal 1\n$ cd ghp/example \u0026\u0026 ../bin/ghp\nlistening on http://[::1]:8002\nlistening on http://localhost:8002\nlistening on https://localhost:8443     $ cd ghp/example \u0026\u0026 ../bin/ghp\ngraceful shutdown initiated             listening on http://[::1]:8002\ngraceful shutdown completed             listening on http://localhost:8002\n$                                       listening on https://localhost:8443\n$ ../bin/ghp                            graceful shutdown initiated\nlistening on http://[::1]:8002          graceful shutdown completed\nlistening on http://localhost:8002      $\nlistening on https://localhost:8443     $ pkill -HUP ghp\ngraceful shutdown initiated\ngraceful shutdown completed\n$\n```\n\nWhen switching processes, try requesting\n`http://localhost:8002/servlet-sleeper/` which responds very slowly piece by\npiece. You'll notice that even when you start another GHP process that takes\nover, an ongoing requests is patiently served til completion.\n\n\n## Usage\n\n```sh\n./build.sh\n(cd example \u0026\u0026 ../bin/ghp -dev)\n```\n\nOpen `http://localhost:8002/`\n\nEdit go files in `example/pub` and reload your web browser.\n\n\n### Dev setup\n\n- Terminal 1: `autorun -r=500 ghp/*.go -- ./build.sh -noget`\n- Terminal 2: `(cd example \u0026\u0026 autorun ../bin/ghp -- ../bin/ghp -dev)`\n\nNow just edit source files and GHP will be automatically rebuilt and restarted.\n\nGet [autorun here](https://github.com/rsms/autorun)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsms%2Fghp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsms%2Fghp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsms%2Fghp/lists"}