{"id":20722467,"url":"https://github.com/toomanybees/gemini_server","last_synced_at":"2025-04-23T15:48:40.093Z","repository":{"id":44932721,"uuid":"313194669","full_name":"TooManyBees/gemini_server","owner":"TooManyBees","description":"A Gemini protocol server","archived":false,"fork":false,"pushed_at":"2022-01-18T16:55:06.000Z","size":26,"stargazers_count":7,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-10T19:20:21.957Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","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/TooManyBees.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}},"created_at":"2020-11-16T04:52:46.000Z","updated_at":"2024-08-09T03:41:18.000Z","dependencies_parsed_at":"2022-08-29T03:01:48.751Z","dependency_job_id":null,"html_url":"https://github.com/TooManyBees/gemini_server","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TooManyBees%2Fgemini_server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TooManyBees%2Fgemini_server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TooManyBees%2Fgemini_server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TooManyBees%2Fgemini_server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TooManyBees","download_url":"https://codeload.github.com/TooManyBees/gemini_server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224996518,"owners_count":17404486,"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-11-17T03:35:58.743Z","updated_at":"2024-11-17T03:35:59.261Z","avatar_url":"https://github.com/TooManyBees.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GeminiServer\n\nA simple server for the Gemini protocol, with an API inspired by Sinatra.\n\n## Usage\n\nUse the built-in executable to serve the current directory.\n\n```\n$ gem install gemini_server\nSuccessfully installed gemini_server-0.1.0\n1 gem installed\n$ gemini_server -h\nUsage: gemini_server [options]\n    -p, --port PORT                  Port to listen on\n        --cert-path PATH             Path to cert file\n        --key-path PATH              Path to key file\n        --charset CHARSET            Charset of text/* files\n        --lang LANG                  Language of text/* files\n```\n\nOr require the library to declare custom routes in Ruby.\n\n```ruby\nrequire \"gemini_server\"\n\nserver = GeminiServer.new\n\nserver.route(\"/greeting/:friend\") do\n  if params[\"friend\"] == \"Gary\"\n    gone \"Gary and I aren't on speaking terms, sorry.\"\n  else\n    success \"Hi there, #{params[\"friend\"]}!\"\n  end\nend\n\nserver.route(\"/farewell\") do\n  lang \"pig-latin\"\n  success \"arewellfay!\"\nend\n\nserver.listen(\"0.0.0.0\", 1965)\n```\n\n### Initialization options\n\n\u003cdl\u003e\n  \u003cdt\u003e\u003ccode\u003ecert\u003c/code\u003e*\u003c/dt\u003e\n  \u003cdd\u003eA SSL certificate. Either a \u003ccode\u003eOpenSSL::X509::Certificate\u003c/code\u003e object, or a string.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003ecert_path\u003c/code\u003e*\u003c/dt\u003e\n  \u003cdd\u003ePath to a SSL certificate file. Defaults to the value of the env variable \u003ccode\u003eGEMINI_CERT_PATH\u003c/code\u003e. Ignored if \u003ccode\u003ecert\u003c/code\u003e option is supplied.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003ekey\u003c/code\u003e*\u003c/dt\u003e\n  \u003cdd\u003eA SSL key. Either a \u003ccode\u003eOpenSSL::PKey\u003c/code\u003e object, or a string.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003ekey_path\u003c/code\u003e*\u003c/dt\u003e\n  \u003cdd\u003ePath to a private key file. Defaults to the value of the env variable \u003ccode\u003eGEMINI_KEY_PATH\u003c/code\u003e. Ignored if \u003ccode\u003ekey\u003c/code\u003e option is supplied.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003emime_type\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eSets the default MIME type for successful responses. Defaults to \u003ccode\u003etext/gemini\u003c/code\u003e, or inferred by the name of the file being served.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003echarset\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eIf set, includes the charset in the response's MIME type.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003elang\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eIf set, includes the language in the response's MIME type, if the MIME type is \u003ccode\u003etext/gemini\u003c/code\u003e. Per the Gemini spec, \u003cem\u003e\"Valid values for the \"lang\" parameter are comma-separated lists of one or more language tags as defined in RFC4646.\"\u003c/em\u003e\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003epublic_folder\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003ePath to a location from which the server will serve static files. If not set, the server will not serve any static files.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003eviews_folder\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003ePath to the location of ERB templates. If not set, defaults to current directory.\u003c/dd\u003e\n\u003c/dl\u003e\n\n*\\* The option pairs `cert` and `cert_path`, and likewise `key` and `key_path`, are mutually exclusive, so they are technically optional. But per the Gemini spec, connections must use TLS, so it is a runtime error if neither option, nor either of the fallback env variables, are used.*\n\n### Route handlers\n\nTo define a route handler, use `GeminiServer#route`:\n\n```ruby\nserver = GeminiServer.new\nserver.route(\"/path/to/route/:variable\") do\n  # route logic\nend\n```\n\nThe route method takes a [Mustermann](https://github.com/sinatra/mustermann) matcher string and a block.\n\nWithin the block, code has access to these methods:\n\n\u003cdl\u003e\n  \u003cdt\u003e\u003ccode\u003eparams\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eReturns a hash of params parsed from the request path.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003euri\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eReturns the full URI of the request.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003emime_type(type)\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eSets the MIME type of the response.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003echarset(ch)\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eSets the charset of the response, overriding the server's default charset.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003elang(l)\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eSets the lang of the response, overriding the server's default lang.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003eerb(filename, locals: {})\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eRenders an ERB template located at \u003ccode\u003efilename\u003c/code\u003e, then sets status to success. MIME type is inferred by the template extension. The template will have access to any instance variables defined in the handler block, as well as any local variables passed in via the \u003ccode\u003elocals\u003c/code\u003e keyword param.\u003c/dd\u003e\n  \u003cdt\u003e\u003ccode\u003erespond(code, meta, body=nil)\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eSets the response code, meta, and optional body. It's probably easier to use \u003ccode\u003eerb\u003c/code\u003e method, or any of the convenience status methods in the next section.\u003c/dd\u003e\n\u003c/dl\u003e\n\n### ERB templates\n\nUsing an ERB template automatically sets the status to `20` (success) because a success is the only type of response that can contain a body. It also tries to infer the MIME type from the template extension (excluding any `.erb`).\n\nERB rendering can define local variables, like in Sinatra:\n\n```ruby\nserver.route(\"/hithere/:friend\") do\n  erb \"hithere.gmi\", locals: { friend: params[\"friend\"] }\nend\n```\n\n```markdown\n\u003c!-- hithere.gmi.erb --\u003e\n# Hi there!\n\nHi there, \u003c%= friend %\u003e.\n```\n\nERB templates have the `params` hash available as a local var:\n\n```ruby\nserver.route(\"/hithere/:friend\") do\n  erb \"hithere.gmi\"\nend\n```\n\n```markdown\n\u003c!-- hithere.gmi.erb --\u003e\n# Hi there!\n\nHi there, \u003c%= params[\"friend\"] %\u003e.\n```\n\n### Status methods\n\nEach of these methods are available within a route handler block. Forgetting to use a status method defaults to a temporary failure. See [Gemini Specification](https://gemini.circumlunar.space/docs/specification.html) for an explanation of each response status.\n\n* `input(prompt)`\n* `sensitive_input(prompt)`\n* `success(body, mime_type=nil)`\n* `redirect_temporary(url)`\n* `redirect_permanent(url)`\n* `temporary_failure(explanation = \"Temporary failure\")`\n* `server_unavailable(explanation = \"Server unavailable\")`\n* `cgi_error(explanation = \"CGI error\")`\n* `proxy_error(explanation = \"Proxy error\")`\n* `slow_down(delay)`\n* `permanent_failure(explanation = \"Permanent failure\")`\n* `not_found(explanation = \"Not found\")`\n* `gone(explanation = \"Gone\")`\n* `proxy_request_refused(explanation = \"Proxy request refused\")`\n* `bad_request(explanation = \"Bad request\")`\n* `client_certificate_required(explanation = \"Client certificate required\")`\n* `certificate_not_authorized(explanation = \"Certificate not authorized\")`\n* `certificate_not_valid(explanation = \"Certificate not valid\")`\n\n### Static file serving\n\nTo serve static files, set the initialization option `public_folder` to the location of your static files. If no route handlers match a request, the server will look for a static file to serve in that location instead. If the `public_folder` option is unset, no static files will be served.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoomanybees%2Fgemini_server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoomanybees%2Fgemini_server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoomanybees%2Fgemini_server/lists"}