{"id":15394273,"url":"https://github.com/xyproto/sheepcounter","last_synced_at":"2025-10-24T17:19:58.390Z","repository":{"id":57486036,"uuid":"151690246","full_name":"xyproto/sheepcounter","owner":"xyproto","description":":sheep: ResponseWriter that can count bytes written to the client","archived":false,"fork":false,"pushed_at":"2025-01-09T14:44:45.000Z","size":24,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-13T21:21:23.881Z","etag":null,"topics":["byte-counting","go","http-server","logging","middleware"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xyproto.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-10-05T08:13:18.000Z","updated_at":"2025-01-09T14:44:45.000Z","dependencies_parsed_at":"2025-04-15T23:53:45.989Z","dependency_job_id":"328b6267-fcdd-4c10-a4b8-c3e73bc640c8","html_url":"https://github.com/xyproto/sheepcounter","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/xyproto/sheepcounter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fsheepcounter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fsheepcounter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fsheepcounter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fsheepcounter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xyproto","download_url":"https://codeload.github.com/xyproto/sheepcounter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fsheepcounter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280834078,"owners_count":26399204,"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-10-24T02:00:06.418Z","response_time":73,"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":["byte-counting","go","http-server","logging","middleware"],"created_at":"2024-10-01T15:22:56.272Z","updated_at":"2025-10-24T17:19:58.342Z","avatar_url":"https://github.com/xyproto.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SheepCounter [![GoDoc](https://godoc.org/github.com/xyproto/sheepcounter?status.svg)](http://godoc.org/github.com/xyproto/sheepcounter) [![Report Card](https://img.shields.io/badge/go_report-A+-brightgreen.svg?style=flat)](http://goreportcard.com/report/xyproto/sheepcounter)\n\nA `http.ResponseWriter` that can count the bytes written to the client so far.\n\n# Why?\n\nIf you want to create an access log of how many bytes are sent to which clients, one method would be to write data to a buffer, count the bytes and then send the data to the client. This may be problematic for large files, since it eats up a lot of memory. It is also costly performance wise, since the data would then have to be counted while or after the data is sent to the client.\n\nA better way is to store use the number returned by the `Write` function directly. This is not straightforward with `http.ResponseWriter` without wrapping it somehow, which is what this module does. A lightweight struct wraps both a `http.ResponseWriter` and an `uint64`, for keeping track of the written bytes.\n\n# Examples\n\n## Count the bytes sent, per response\n\n~~~go\npackage main\n\nimport (\n        \"fmt\"\n        \"github.com/xyproto/sheepcounter\"\n        \"log\"\n        \"net/http\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n        sc := sheepcounter.New(w)\n        fmt.Fprintf(sc, \"Hi %s!\", r.URL.Path[1:])\n        fmt.Println(\"COUNTED:\", sc.Counter()) // Counts the bytes sent, for this response only\n}\n\nfunc main() {\n        http.HandleFunc(\"/\", handler)\n        fmt.Println(\"Serving on port 8080\")\n        log.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n~~~\n\n## Count the total amount of bytes sent\n\n~~~go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"os\"\n    \"sync/atomic\"\n\n    \"github.com/xyproto/sheepcounter\"\n)\n\nconst (\n    title = \"SheepCounter\"\n    style = `body { margin: 4em; background: wheat; color: black; font-family: terminus, \"courier new\", courier; font-size: 1.1em; } a:link { color: #403020; } a:visited { color: #403020; } a:hover { color: #605040; } a:active { color: #605040; } #counter { color: red; }`\n    page  = \"\u003c!doctype html\u003e\u003chtml\u003e\u003chead\u003e\u003cstyle\u003e%s\u003c/style\u003e\u003ctitle\u003e%s\u003c/title\u003e\u003cbody\u003e%s\u003c/body\u003e\u003c/html\u003e\"\n)\n\nvar totalBytesWritten uint64\n\nfunc helloHandler(w http.ResponseWriter, r *http.Request) {\n    sc := sheepcounter.New(w)\n    body := `\u003cp\u003eHere are the \u003ca href=\"/counter\"\u003ecounted bytes\u003c/a\u003e.\u003c/p\u003e`\n    fmt.Fprintf(sc, page, style, title, body)\n    written, err := sc.UCounter2()\n    if err != nil {\n        // Log an error and return\n        log.Printf(\"error: %s\\n\", err)\n        return\n    }\n    atomic.AddUint64(\u0026totalBytesWritten, written)\n    log.Printf(\"counted %d bytes\\n\", written)\n}\n\nfunc counterHandler(w http.ResponseWriter, r *http.Request) {\n    sc := sheepcounter.New(w)\n    body := fmt.Sprintf(`\u003cp\u003eTotal bytes sent from the server (without counting this response): \u003cspan id=\"counter\"\u003e%d\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003ca href=\"/\"\u003eBack\u003c/a\u003e\u003c/p\u003e`, atomic.LoadUint64(\u0026totalBytesWritten))\n    fmt.Fprintf(sc, page, style, title, body)\n    written, err := sc.UCounter2()\n    if err != nil {\n        // Log an error and return\n        log.Printf(\"error: %s\\n\", err)\n        return\n    }\n    atomic.AddUint64(\u0026totalBytesWritten, written)\n    log.Printf(\"counted %d bytes\\n\", written)\n}\n\nfunc main() {\n    http.HandleFunc(\"/\", helloHandler)\n    http.HandleFunc(\"/counter\", counterHandler)\n\n    httpAddr := os.Getenv(\"HTTP_ADDR\")\n    if httpAddr == \"\" {\n        httpAddr = \":4000\"\n    }\n\n    log.Println(\"Serving on \" + httpAddr)\n    log.Fatal(http.ListenAndServe(httpAddr, nil))\n}\n~~~\n\n# Requirements\n\n* Go 1.18 or later\n\n# General information\n\n* Version: 1.6.2\n* Alexander F. Rødseth \u0026lt;xyproto@archlinux.org\u0026gt;\n* License: BSD-3\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyproto%2Fsheepcounter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxyproto%2Fsheepcounter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyproto%2Fsheepcounter/lists"}