{"id":41514177,"url":"https://github.com/rbxb/httpfilter","last_synced_at":"2026-01-23T20:14:53.897Z","repository":{"id":56413443,"uuid":"170617273","full_name":"rbxb/httpfilter","owner":"rbxb","description":"Simple http webserver","archived":false,"fork":false,"pushed_at":"2021-12-12T17:06:42.000Z","size":68,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-19T05:41:16.247Z","etag":null,"topics":["go","server","web"],"latest_commit_sha":null,"homepage":null,"language":"Go","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/rbxb.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":"2019-02-14T02:57:37.000Z","updated_at":"2024-06-19T05:41:16.248Z","dependencies_parsed_at":"2022-08-15T18:10:56.944Z","dependency_job_id":null,"html_url":"https://github.com/rbxb/httpfilter","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/rbxb/httpfilter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbxb%2Fhttpfilter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbxb%2Fhttpfilter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbxb%2Fhttpfilter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbxb%2Fhttpfilter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rbxb","download_url":"https://codeload.github.com/rbxb/httpfilter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbxb%2Fhttpfilter/sbom","scorecard":{"id":764313,"data":{"date":"2025-08-11","repo":{"name":"github.com/rbxb/httpfilter","commit":"b56ae7c6c7cc80da33087571c71c44c3df403357"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T00:20:12.015Z","repository_id":56413443,"created_at":"2025-08-23T00:20:12.015Z","updated_at":"2025-08-23T00:20:12.015Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28699375,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["go","server","web"],"created_at":"2026-01-23T20:14:53.244Z","updated_at":"2026-01-23T20:14:53.890Z","avatar_url":"https://github.com/rbxb.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# httpfilter\n\nhttpfilter is a package that allows you to bundle your website's content with the behavior that determines how the content is made visible by your web server.  \n\nOn many websites the URL path that a user goes to is different from the internal path of the actual resource that the server responds with. For example, if you go to `https://ryanburleson.dev/home`, the path visible to the user is `/home`, but the path of the HTML file that is served is actually `/home.html`.  \n\nTypically, the way to acheive this behavior is to hard code it into your web server. You may use a mapping function to define a request path and a corresponding resource path. This introduces two problems:\n1. The code that determines how a resource should be made visible is separated from the actual content.\n2. If you want to add new resources, you must modify your web server code.\n\n**httpfilter** aims to solve both of these problems. httpfilter uses simple scripts that are embeded in the same file directory as your resources in order to bundle the content and behavior and allow you to dynamically change the behavior without modifying the server code.  \nThis makes your web resources more modular, i.e. a directory containing some content also contains the code that specifies how that content should be displayed by a web server.  \n\n - [Installation](#Installation)\n - [Tutorial](#Tutorial)\n - [How it Works](#How-it-Works)\n - [Usage Example](#Usage-Example)\n - [Standard Operators](#Standard-Operators)\n - [Writing Filters](#Writing-Filters)\n - [Attaching Additional Operators](#Attaching-Additional-Operators)\n - [Writing Operator Functions](#Writing-Operator-Functions)\n - [Fixed Filter File](#Fixed-Filter-File)\n\n## Installation\n\n```shell\n$ go get github.com/rbxb/httpfilter\n$ go install github.com/rbxb/httpfilter/cmd/httpfilter\n```\n\n## Tutorial\n\nPlaying around with the pre-made sample should help you get a feel for how httpfilter works.\n\n[tutorial.md](./tutorial.md)\n\n## How it Works\n\nhttpfilter uses scripts (*called filters*) nested in the website's static files directories to invoke Go functions that modify how the request is handled. For example, if you wanted to make the request path `/index.html` redirect to `/home.html`, you could use a filter like this:\n```\n#redirect index.html home.html\n```\nThis filter file would be placed in the same directory as the supposed `index.html` file and the `home.html` file.\n\nWhen the httpfilter server recieves a request for `/index.html`, it will first look for the filter in the same directory as the requested file. The server will read the filter from the top—down, searching for an entry where the **selector** matches the requested file name. If an entry's selector matches the requested file name, the server will call the Go function that is mapped to the **operator** of that entry. Any additional **arguments** are passed to the Go function as strings.\n\n```\n        entry\n┏━━━━━━━━━┻━━━━━━━━━━━━━━━━╍┅\n#redirect index.html home.html\n ┃          ┃       ┗━━━┳━━╍┅\n operator   ┃        arguments\n            ┃\n         selector\n```\n\nThe httpfilter server passes the request and arguments to the operator function, which may write a response. The server will continue to read and execute entries from the filter until a response has been written.\n\nIf the server reaches the end of the filter and a response still hasn't been written, the server will call the `serve` operator, which will attempt to serve the file or respond with a `404 Not found` error.\n\n## Usage Example\n\nCreate an httpfilter `Server` and pass it to `http.ListenAndServe`.\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"github.com/rbxb/httpfilter\"\n)\n\nfunc main() {\n\tserver := httpfilter.NewServer(\"./root\", \"\")\n\tlog.Fatal(http.ListenAndServe(\":8080\", server))\n}\n```\n\n## Standard Operators\n\n#### `serve`\n\nThe `serve` operator attempts to serve the file named in the first argument or responds with a `404 Not found` error. E.g. this will serve the file `home.html` when the client requests `/home`:  \n```\n#serve home home.html\n```\nIf the request is not fulfilled at the end of the filter file, the httpfilter server will default to the serve operator to write a response.\n\n\n#### `ignore`\n\nThe `ignore` operator responds with a `404 Not Found` error. Use this if you want to prevent access to a specific file, e.g.\n```\n#ignore secret.txt\n```\n\n#### `redirect`\n\nThe `redirect` operator redirects a request to the URL or path in the first argument. E.g. this will redirect `/index.html` to `/home`:\n```\n#redirect index.html home\n```\n\n#### `proxy`\n\nThe `proxy` operator forwards the client's request to the end server provided in the first argument and responds with the end server's response:\n```\n#proxy page.html http://192.168.1.6:80\n```\n\n#### `request`\n\nThe `request` makes a new GET request to the server in the first argument and responds with the end server's response:\n```\n#request page.html http://192.168.1.6:80\n```\n\n## Writing Filters\n\n - Operators are always prefixed by a `#`.\n - Operators, selectors, and arguments are separated by spaces.\n - Entries are separated by line breaks.\n - The filter is read from the top—down and the server will never read upwards\n\n### Selectors using `*`\n\nYou can use a `*` in the selector to select all queries.\n - `*` will match with all queries.\n - `a.*` will match with all queries where the name is `a` regardless of the extension.\n - `*.a` will match with all queries where the extension is `.a`.\n\nFor example, this filter will `ignore` all queries where the extension is `.txt`:\n```\n#ignore *.txt\n```\n\n### Selectors using `@`\n\nUsing the `@` symbol selects a request by its subdomain.   \n\nFor example, this filter will proxy the subdomain `service` to a local server and redirect the base domain to the `service` subdomain.\n```\n#proxy @service http://192.168.1.6:80\n#redirect @ http://service.example.com\n```\n\n### Bulk Operator Syntax\n\nIf you have multiple entries that use the same operator repeatedly, e.g.\n```\n#serve home home.html\n#serve about about.html\n#serve contact contact.html\n```\nyou can write the operator once and put the entries below it:\n```\n#serve\n  home home.html\n  about about.html\n  contact contact.html\n```\n\n### Naming\n\nCurrently, filter files must be named `_filters.txt`.\nPlace the file in the directory that you want it to work in.\nThe httpfilter server will never serve a filter file to a client.\n\n## Attaching Additional Operators\n\nNon-standard operator functions can be attached to the server.\nPass them to `NewServer` as a `map[string]OpFunc`, where the map key is the operator name that should be used in the filter file to call the operator function.\n```go\nfunc NewServer(root string, filter string, ops ...map[string]OpFunc) * Server\n```\nPass in your own operator functions:\n```go\nserver := httpfilter.NewServer(\"./root\", \"\", map[string]httpfilter.OpFunc{\n\t\t\"myop\": myOpFunc,\n\t})\n```\nThe standard operators can be overwritten by passing in operators with the same key value.\n\n## Writing Operator Functions\n\nYou can write your own operator functions and attach them to your server (see [Attaching Additional Operators](#Attaching-Additional-Operators)).\n\nOperator functions follow this type:\n```go\ntype OpFunc func(w http.ResponseWriter, req *http.Request, args ...string)\n```\n\nIf the operator function calls `w.Write` or `w.WriteHeader`, the server will stop executing entries and the request/response is completed.\n\n## Fixed Filter File\n\n```go\nserver := httpfilter.NewServer(\"./root\", \"C:/_filter.txt\")\n```\n\nPutting a path into the second argument of the server constructor will force the server to use that filter file for every request. You can use a fixed filter file and the `@` selector to route subdomains to other servers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbxb%2Fhttpfilter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frbxb%2Fhttpfilter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbxb%2Fhttpfilter/lists"}