{"id":29151197,"url":"https://github.com/karpeleslab/webutil","last_synced_at":"2026-02-26T01:10:49.328Z","repository":{"id":57507872,"uuid":"234592618","full_name":"KarpelesLab/webutil","owner":"KarpelesLab","description":"Various methods useful for HTTP servers","archived":false,"fork":false,"pushed_at":"2025-08-05T02:27:26.000Z","size":46,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-15T02:21:37.327Z","etag":null,"topics":[],"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/KarpelesLab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2020-01-17T16:45:58.000Z","updated_at":"2025-08-05T02:27:21.000Z","dependencies_parsed_at":"2024-05-11T07:29:50.860Z","dependency_job_id":"634da64d-54b9-406d-bab8-e7eb23537636","html_url":"https://github.com/KarpelesLab/webutil","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/KarpelesLab/webutil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fwebutil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fwebutil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fwebutil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fwebutil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KarpelesLab","download_url":"https://codeload.github.com/KarpelesLab/webutil/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fwebutil/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273837323,"owners_count":25177115,"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","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-07-01T00:08:53.315Z","updated_at":"2026-02-26T01:10:49.324Z","avatar_url":"https://github.com/KarpelesLab.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://godoc.org/github.com/KarpelesLab/webutil?status.svg)](https://godoc.org/github.com/KarpelesLab/webutil)\n\n# Web Utilities for Go\n\nA collection of utilities for HTTP servers and web applications in Go.\n\n## Installation\n\n```bash\ngo get github.com/KarpelesLab/webutil\n```\n\n## Features\n\n- **HTTP Error Handling**: Type `HTTPError` implements both `error` and `http.Handler` interfaces\n- **Error-Returning Handlers**: Extended handler interface that allows returning errors\n- **HTTP Redirects**: Represent redirects as errors for flexible control flow\n- **Resumable Downloads**: Auto-resuming HTTP downloads using Range headers\n- **Data URI Parsing**: Parse and decode RFC 2397 `data:` URI schemes\n- **PHP Query String Parsing**: Parse and encode PHP-style query strings with array notation\n- **URL Path Manipulation**: Add or remove prefixes from request paths\n- **IP:Port Parsing**: Parse IP addresses with optional ports (IPv4 and IPv6)\n\n## Usage Examples\n\n### HTTP Error Handling\n\nThe `HTTPError` type can be used both as an error and as an HTTP handler:\n\n```go\n// Return HTTP errors from handlers\nfunc handler(w http.ResponseWriter, req *http.Request) error {\n    if !authenticated {\n        return webutil.StatusUnauthorized\n    }\n    if resource == nil {\n        return webutil.StatusNotFound\n    }\n    return nil\n}\n\n// Serve any error as HTTP response\nif err != nil {\n    webutil.ServeError(w, req, err)\n    return\n}\n\n// Extract HTTP status from any error (including wrapped errors)\nstatus := webutil.HTTPStatus(err)\n```\n\n### Error-Returning Handlers\n\nUse `Handler` and `WrapFunc` for handlers that can return errors:\n\n```go\n// Function-based handler that returns errors\nhttp.Handle(\"/\", webutil.WrapFunc(func(w http.ResponseWriter, req *http.Request) error {\n    data, err := fetchData()\n    if err != nil {\n        return webutil.StatusInternalServerError\n    }\n    json.NewEncoder(w).Encode(data)\n    return nil\n}))\n\n// Interface-based handler\ntype myHandler struct{}\n\nfunc (h *myHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) error {\n    // Handle request, return error if needed\n    return nil\n}\n\nhttp.Handle(\"/api\", webutil.Wrap(\u0026myHandler{}))\n```\n\n### HTTP Redirects as Errors\n\n```go\nfunc handler(w http.ResponseWriter, req *http.Request) error {\n    if needsRedirect {\n        target, _ := url.Parse(\"https://example.com/new-location\")\n        return webutil.RedirectError(target)           // 302 Found\n        // or: return webutil.RedirectErrorCode(target, http.StatusMovedPermanently)\n    }\n    return nil\n}\n```\n\n### Resumable Downloads\n\n```go\n// Get with auto-resume on connection failures\nreader, err := webutil.Get(\"https://example.com/large-file.zip\")\nif err != nil {\n    log.Fatal(err)\n}\ndefer reader.Close()\n\n// Reading will automatically resume if the connection drops\nio.Copy(dst, reader)\n```\n\n### Data URI Parsing\n\n```go\n// Parse base64-encoded data URI\ndata, mimeType, err := webutil.ParseDataURI(\"data:text/plain;base64,SGVsbG8gV29ybGQ=\")\n// data: []byte(\"Hello World\")\n// mimeType: \"text/plain\"\n\n// Parse URL-encoded data URI\ndata, mimeType, err := webutil.ParseDataURI(\"data:text/html,%3Ch1%3EHello%3C%2Fh1%3E\")\n\n// Get also supports data URIs\nreader, err := webutil.Get(\"data:text/plain,Hello\")\n```\n\n### PHP-Style Query String Parsing\n\n```go\n// Parse nested objects: \"user[name]=John\u0026user[age]=30\"\nresult := webutil.ParsePhpQuery(\"user[name]=John\u0026user[age]=30\")\n// result: map[string]any{\"user\": map[string]any{\"name\": \"John\", \"age\": \"30\"}}\n\n// Parse arrays: \"tags[]=go\u0026tags[]=web\"\nresult := webutil.ParsePhpQuery(\"tags[]=go\u0026tags[]=web\")\n// result: map[string]any{\"tags\": []any{\"go\", \"web\"}}\n\n// Convert from url.Values\nvalues := url.Values{\"items[]\": {\"a\", \"b\"}}\nresult := webutil.ConvertPhpQuery(values)\n\n// Encode back to query string\nquery := webutil.EncodePhpQuery(result)\n```\n\n### URL Path Manipulation\n\n```go\n// Strip prefix from requests before routing\nhttp.Handle(\"/api/\", \u0026webutil.SkipPrefix{\n    Prefix:  \"/api\",\n    Handler: apiRouter,\n})\n\n// Add prefix to requests\nhttp.Handle(\"/\", \u0026webutil.AddPrefix{\n    Prefix:  \"/v1\",\n    Handler: versionedHandler,\n})\n```\n\n### IP:Port Parsing\n\n```go\n// Parse various IP:port formats\naddr := webutil.ParseIPPort(\"127.0.0.1:8080\")     // IPv4 with port\naddr := webutil.ParseIPPort(\"192.168.1.1\")        // IPv4 only\naddr := webutil.ParseIPPort(\"[::1]:8080\")         // IPv6 with port\naddr := webutil.ParseIPPort(\"::1\")                // IPv6 only\naddr := webutil.ParseIPPort(\":8080\")              // Port only\n\nif addr != nil {\n    fmt.Printf(\"IP: %v, Port: %d\\n\", addr.IP, addr.Port)\n}\n```\n\n## Pre-defined HTTP Status Errors\n\nThe package provides constants for all standard HTTP error status codes:\n\n**4xx Client Errors:**\n`StatusBadRequest`, `StatusUnauthorized`, `StatusPaymentRequired`, `StatusForbidden`, `StatusNotFound`, `StatusMethodNotAllowed`, `StatusNotAcceptable`, `StatusProxyAuthRequired`, `StatusRequestTimeout`, `StatusConflict`, `StatusGone`, `StatusLengthRequired`, `StatusPreconditionFailed`, `StatusRequestEntityTooLarge`, `StatusRequestURITooLong`, `StatusUnsupportedMediaType`, `StatusRequestedRangeNotSatisfiable`, `StatusExpectationFailed`, `StatusTeapot`, `StatusMisdirectedRequest`, `StatusUnprocessableEntity`, `StatusLocked`, `StatusFailedDependency`, `StatusTooEarly`, `StatusUpgradeRequired`, `StatusPreconditionRequired`, `StatusTooManyRequests`, `StatusRequestHeaderFieldsTooLarge`, `StatusUnavailableForLegalReasons`\n\n**5xx Server Errors:**\n`StatusInternalServerError`, `StatusNotImplemented`, `StatusBadGateway`, `StatusServiceUnavailable`, `StatusGatewayTimeout`, `StatusHTTPVersionNotSupported`, `StatusVariantAlsoNegotiates`, `StatusInsufficientStorage`, `StatusLoopDetected`, `StatusNotExtended`, `StatusNetworkAuthenticationRequired`\n\n## Documentation\n\nSee [GoDoc](https://godoc.org/github.com/KarpelesLab/webutil) for complete API documentation.\n\n## License\n\nSee LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpeleslab%2Fwebutil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarpeleslab%2Fwebutil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpeleslab%2Fwebutil/lists"}