├── .circleci └── config.yml ├── .gitignore ├── CONTRIBUTING.md ├── Fetch.png ├── GitPush.png ├── LICENSE ├── Makefile ├── PullReuqestArrow.png ├── README.md ├── SECURITY.md ├── benchmarks └── README.md ├── cert.pem ├── checkout-main.png ├── coverage.html ├── ctx.go ├── ctx_example_test.go ├── ctx_godoc_test.go ├── ctx_test.go ├── example ├── LICENSE ├── Logo.png ├── README.md ├── form.post │ └── main.go ├── get.ctx │ └── main.go ├── group │ ├── README.md │ └── group.go ├── logo_lado.png ├── middleware │ ├── README.md │ ├── basicauth │ │ ├── README.md │ │ ├── middl_v1 │ │ │ └── basicauth.go │ │ ├── middl_v2 │ │ │ └── basicauth.go │ │ ├── middl_v3 │ │ │ └── basicauth.go │ │ └── middl_v4 │ │ │ └── basicauth.go │ ├── cache │ │ ├── basic │ │ │ └── main.go │ │ ├── custom-key │ │ │ └── main.go │ │ ├── redis.mock │ │ │ └── main.go │ │ └── redis │ │ │ └── main.go │ ├── compress │ │ ├── README.md │ │ ├── compress.gzip.HandlerFunc │ │ │ └── main.go │ │ ├── compress.gzip.net.http │ │ │ └── main.go │ │ └── compress.gzip │ │ │ └── compress_gzip.go │ ├── cors │ │ ├── README.md │ │ ├── cors.nativo.net_http │ │ │ └── main.go │ │ ├── cors.nativo.net_http_v2 │ │ │ └── main.go │ │ ├── cors.nativo.net_http_v3 │ │ │ └── main.go │ │ ├── cors.nativo.quick │ │ │ └── main.go │ │ ├── cors.quick.cors.confg │ │ │ └── main.go │ │ ├── cors.quick.group │ │ │ └── main.go │ │ ├── cors.with.middleware │ │ │ └── main.go │ │ └── server.html │ │ │ ├── main.go │ │ │ └── static │ │ │ └── index.html │ ├── dynamic │ │ ├── README.md │ │ └── main.go │ ├── etag │ │ └── README.md │ ├── healthcheck │ │ ├── README.md │ │ ├── custom │ │ │ └── main.go │ │ └── simple │ │ │ └── main.go │ ├── helmet │ │ ├── README.md │ │ ├── custom │ │ │ └── main.go │ │ └── simple │ │ │ └── main.go │ ├── limiter │ │ ├── README.md │ │ ├── basic │ │ │ └── main.go │ │ └── group │ │ │ └── main.go │ ├── logger │ │ ├── README.md │ │ ├── net.http │ │ │ └── main.go │ │ ├── simple │ │ │ └── main.go │ │ ├── with.format │ │ │ └── main.go │ │ ├── with.json │ │ │ └── main.go │ │ └── with.slog │ │ │ └── main.go │ ├── maxbody │ │ ├── README.md │ │ ├── any.config │ │ │ └── maxbody.any.config.go │ │ ├── default.config │ │ │ └── maxbody.default.config.go │ │ ├── length.too.large │ │ │ └── maxbody.entity.large.config.go │ │ └── max.bytes.reader │ │ │ └── main.go │ ├── msgid │ │ ├── README.md │ │ └── main.go │ ├── msguuid │ │ ├── README.md │ │ ├── any.config │ │ │ └── msguuid.any.config.go │ │ └── default.config │ │ │ └── msguuid.default.config.go │ ├── pprof │ │ ├── README.md │ │ ├── custom-pprof │ │ │ └── custom-pprof.go │ │ ├── custom │ │ │ └── main.go │ │ ├── pprof.jpg │ │ └── simple │ │ │ └── main.go │ ├── proxy │ │ └── README.md │ ├── recover │ │ ├── README.md │ │ ├── custom │ │ │ └── main.go │ │ └── simple │ │ │ └── main.go │ ├── requestid │ │ └── README.md │ ├── skip │ │ └── README.md │ └── timeout │ │ └── README.md ├── page │ ├── LICENSE │ ├── Logo.png │ ├── README.md │ ├── logo_lado.png │ └── quick.png ├── pkg │ ├── gcolor │ │ └── simple │ │ │ └── main.go │ ├── glog │ │ ├── api │ │ │ └── main.go │ │ └── simple │ │ │ └── main.go │ ├── rand │ │ ├── algodefault │ │ │ └── main.go │ │ ├── randomInt │ │ │ └── main.go │ │ └── traceId │ │ │ └── main.go │ └── uuid │ │ ├── from.bytes │ │ └── main.go │ │ ├── must.parse │ │ └── main.go │ │ ├── parse │ │ └── main.go │ │ └── string.urn │ │ └── main.go ├── quick.delete │ ├── README │ └── delete.go ├── quick.get │ ├── README.md │ └── main.go ├── quick.head │ └── main.go ├── quick.http.client │ ├── README.md │ ├── cookiejar.client │ │ └── main.go │ ├── custom.http.client │ │ └── main.go │ ├── delete │ │ └── delete.go │ ├── full.methods.retry │ │ └── main.go │ ├── full.retray │ │ └── main.go │ ├── get │ │ └── get.go │ ├── http.client.config │ │ └── main.go │ ├── post │ │ └── post.go │ ├── postform │ │ └── main.go │ ├── put │ │ └── put.go │ ├── quick.delete │ │ └── main.go │ ├── quick.get │ │ └── main.go │ ├── quick.post │ │ └── main.go │ ├── quick.put │ │ └── main.go │ ├── retry.get │ │ └── retry.get.go │ ├── retry.new │ │ └── main.go │ ├── retry.post │ │ └── retry.post.go │ ├── servers │ │ ├── server1 │ │ │ └── main.go │ │ └── server2 │ │ │ └── main.go │ ├── transport.client.failover │ │ └── main.go │ ├── transport.in.failover │ │ └── main.go │ ├── with.retry.failover │ │ └── main.go │ └── with.retry.transport │ │ └── main.go ├── quick.http2 │ └── multiplex │ │ ├── cert.pem │ │ ├── k6.script.get.http.js │ │ ├── k6.script.get.http2.js │ │ ├── key.pem │ │ └── main.go ├── quick.listen.tls │ ├── cert.pem │ ├── key.pem │ └── main.go ├── quick.new │ ├── full │ │ └── main.go │ └── simple │ │ └── main.go ├── quick.newerror │ └── main.go ├── quick.png ├── quick.post │ ├── README.md │ └── post.go ├── quick.put │ ├── README.md │ └── put.go ├── quick.regex │ ├── README.md │ ├── lower │ │ └── main.go │ ├── numeric │ │ └── main.go │ └── version │ │ └── main.go ├── quick.start │ ├── README.md │ └── main.go ├── quick.use │ └── main.go ├── quick_logo.png ├── site │ └── docs │ │ ├── app │ │ └── app.go │ │ ├── ctx │ │ └── ctx.go │ │ ├── middleware │ │ ├── CORS.go │ │ ├── Healthcheck.go │ │ ├── basicauth.go │ │ ├── compress.go │ │ ├── helmet.go │ │ └── limiter.go │ │ └── package-lock.json ├── static │ ├── README.md │ ├── echo │ │ └── main.go │ ├── fiber │ │ └── main.go │ ├── gin │ │ └── main.go │ ├── net.http.embed │ │ ├── main.go │ │ └── static │ │ │ └── index.html │ ├── net.http │ │ └── main.go │ ├── quick.embed │ │ ├── embed │ │ ├── main.go │ │ └── static │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ └── quick │ │ ├── main.go │ │ └── static │ │ ├── index.html │ │ ├── script.js │ │ └── style.css ├── template │ ├── README.md │ ├── custom │ │ ├── main.go │ │ └── views │ │ │ ├── index.html │ │ │ └── layouts │ │ │ ├── base.html │ │ │ └── main.html │ └── embed │ │ ├── main.go │ │ └── views │ │ ├── index.html │ │ └── layouts │ │ ├── base.html │ │ └── main.html └── upload.multipart │ ├── README.md │ ├── echo │ └── main.go │ ├── fiber │ └── main.go │ ├── gin │ └── main.go │ ├── net.http │ └── main.go │ ├── quick.png │ └── quick │ ├── file.copy │ └── main.go │ ├── file │ └── main.go │ └── files │ └── main.go ├── fork.png ├── gcolor ├── README.md ├── gcolor.go ├── gcolor_example_test.go ├── gcolor_test.go └── quick.png ├── glog ├── README.md ├── bench.png ├── glob_ctx_example_test.go ├── glog.go ├── glog_ctx.go ├── glog_ctx_test.go ├── glog_example_test.go ├── glog_test.go └── quick.png ├── go.mod ├── group.go ├── group_delete_test.go ├── group_example_test.go ├── group_get_test.go ├── group_godoc_test.go ├── group_post_test.go ├── group_put_test.go ├── http.status.go ├── http.status_test.go ├── http ├── README.md └── client │ ├── Makefile │ ├── README.md │ ├── client.go │ ├── client_example_test.go │ ├── client_test.go │ ├── coverage.sh │ └── quick.png ├── http_status_example_test.go ├── internal ├── concat │ └── concat.go ├── log │ └── log.go ├── print │ └── println.go └── qos │ └── qos.go ├── key.pem ├── middleware ├── Makefile ├── basicauth │ ├── README.md │ ├── basicauth.go │ ├── basicauth_example_test.go │ ├── basicauth_fuzz_test.go │ ├── basicauth_table_driven_test.go │ └── basicauth_traditional_test.go ├── cache │ ├── README.md │ ├── benchmark_cache_test.go │ ├── cache.go │ ├── cache_example_test.go │ ├── cache_test.go │ ├── config.go │ ├── coverage.html │ ├── redis.go │ └── storage.go ├── compress │ ├── Makefile │ ├── README.md │ ├── compress.go │ ├── compress_example_test.go │ ├── compress_test.go │ ├── coverage.sh │ └── mock.go ├── cors │ ├── Makefile │ ├── README.md │ ├── cors.go │ ├── cors_example_test.go │ ├── cors_test.go │ ├── coverage.sh │ └── mock.go ├── coverage.sh ├── healthcheck │ ├── README.md │ ├── healthcheck.go │ ├── healthcheck_example_test.go │ └── healthcheck_test.go ├── helmet │ ├── README.md │ ├── helmet.go │ ├── helmet_example_test.go │ └── helmet_test.go ├── limiter │ ├── limiter.go │ ├── limiter_example_test.go │ └── limiter_test.go ├── logger │ ├── README.md │ ├── logger.go │ ├── logger_example_test.go │ └── logger_test.go ├── maxbody │ ├── Makefile │ ├── README.md │ ├── coverage.sh │ ├── maxbody.go │ ├── maxbody_example_test.go │ ├── maxbody_test.go │ └── mock.go ├── msgid │ ├── Makefile │ ├── README.md │ ├── coverage.sh │ ├── mock.go │ ├── msgid.go │ ├── msgid_example_test.go │ └── msgid_test.go ├── msguuid │ ├── Makefile │ ├── README.md │ ├── coverage.sh │ ├── mock.go │ ├── msguuid.go │ ├── msguuid_example_test.go │ └── msguuid_test.go ├── pprof │ ├── README.md │ ├── pprof.go │ ├── pprof.jpg │ ├── pprof_example_test.go │ └── pprof_test.go └── recover │ ├── README.md │ ├── recover.go │ ├── recover_example_test.go │ └── recover_test.go ├── pullrequest.png ├── pullrequesttext.png ├── quick.go ├── quick.png ├── quickTest └── README.md ├── quick_ctx_mock.go ├── quick_ctx_mock_test.go ├── quick_default_config_test.go ├── quick_delete_test.go ├── quick_display.go ├── quick_example_test.go ├── quick_fileserver_test.go ├── quick_get_test.go ├── quick_godoc_test.go ├── quick_head_test.go ├── quick_logo.png ├── quick_pool.go ├── quick_post_test.go ├── quick_put_test.go ├── quick_server.gif ├── quick_test.go ├── quick_upload_example_test.go ├── quick_upload_file.go ├── quick_upload_test.go ├── quicktest.go ├── quicktest_example_v2_test.go ├── quicktest_v2.go ├── quicktest_v2_test.go ├── rand ├── README.md ├── rand.go ├── rand_example_test.go └── rand_test.go ├── readmeLogs ├── log.format.json.png ├── log.format.slog.png ├── log.format.text.png ├── log.simple.png ├── msguuid.png └── quick.png ├── scripts ├── coverage.sh └── test.sh ├── static └── index.html ├── template ├── README.md ├── html │ ├── html.go │ ├── html_benchmark_test.go │ ├── html_example_test.go │ ├── html_test.go │ └── static │ │ └── views │ │ ├── index.html │ │ └── layouts │ │ ├── base.html │ │ └── main.html ├── quick.png └── template.go ├── test_uploads ├── file1.txt ├── file2.txt └── quick.txt ├── uploads └── quick.txt └── uuid ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── dce.go ├── dce_example_test.go ├── doc.go ├── hash.go ├── hash_example_test.go ├── json_test.go ├── marshal.go ├── marshal_example_test.go ├── node.go ├── node_js.go ├── node_net.go ├── null.go ├── null_test.go ├── seq_test.go ├── sql.go ├── sql_test.go ├── time.go ├── time_test.go ├── util.go ├── uuid.go ├── uuid_example_test.go ├── uuid_test.go ├── version1.go ├── version1_example_test.go ├── version4.go ├── version4_example_test.go ├── version6.go ├── version6_example_test.go ├── version6_test.go ├── version7.go └── version7_example_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | go-executor: 5 | docker: 6 | - image: cimg/go:1.24 7 | 8 | jobs: 9 | build: 10 | executor: go-executor 11 | 12 | steps: 13 | - checkout 14 | 15 | - run: 16 | name: "Verify Go version" 17 | command: go version 18 | 19 | - run: 20 | name: "Install dependencies" 21 | command: go mod tidy 22 | 23 | - run: 24 | name: "Run Tests with Coverage" 25 | command: | 26 | go test -v -covermode=count -coverprofile=coverage.out -tags exclude_test ./... 27 | 28 | - store_artifacts: 29 | path: coverage.out 30 | 31 | - store_test_results: 32 | path: tests 33 | 34 | - run: 35 | name: "Upload Coverage to Coveralls" 36 | command: | 37 | go install github.com/mattn/goveralls@latest 38 | if [ -z "$COVERALLS_TOKEN" ]; then 39 | echo "COVERALLS_TOKEN is missing! Skipping upload."; 40 | else 41 | goveralls -coverprofile=coverage.out -service=circleci -repotoken $COVERALLS_TOKEN 42 | fi 43 | 44 | workflows: 45 | version: 2.1 46 | build_and_test: 47 | jobs: 48 | - build: 49 | filters: 50 | branches: 51 | only: main -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Output of the go coverage tool, specifically when used with LiteIDE 2 | *.out 3 | testdata/ 4 | **/testdata/ 5 | **/results.bin 6 | 7 | # allow only on root 8 | **/go.mod 9 | **/go.sum 10 | !go.mod 11 | !go.sum 12 | 13 | # IDE files 14 | .vscode 15 | .DS_Store 16 | .idea 17 | 18 | # Misc 19 | *.quick.gz 20 | pocs/* 21 | pocs.example/* 22 | *.pprof 23 | *.workspace 24 | .pocs/ 25 | 26 | # Dependencies 27 | /vendor/ 28 | vendor/ 29 | vendor 30 | /Godeps/ 31 | 32 | 33 | # Test binary, built with `go test -c` 34 | *.test 35 | *.tmp 36 | draft/* 37 | 38 | # neovim ctags 39 | tags 40 | 41 | # coverage htmls 42 | ./middleware/*.html 43 | ./middleware/compress/*.html 44 | ./middleware/cors/*.html 45 | ./middleware/logger/*.html 46 | ./middleware/maxbody/*.html 47 | ./middleware/msgid/*.html 48 | ./middleware/msguuid/*.html 49 | ./httpclient/*.html 50 | -------------------------------------------------------------------------------- /Fetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/Fetch.png -------------------------------------------------------------------------------- /GitPush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/GitPush.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jefferson Otoni Lima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | .EXPORT_ALL_VARIABLES: 3 | 4 | GO111MODULE=on 5 | GOPROXY=direct 6 | GOSUMDB=off 7 | GOPRIVATE=github.com/jeffotoni/quick 8 | 9 | update: 10 | @echo "########## Compilando nossa API ... " 11 | @rm -f go.* 12 | go mod init github.com/jeffotoni/quick 13 | go mod tidy 14 | @echo "fim" 15 | 16 | test: 17 | @bash ./scripts/test.sh; 18 | 19 | cover: 20 | @bash ./scripts/coverage.sh; 21 | @cd http/client && make cover 22 | #@rm -f ./coverage.out; 23 | #@rm -f ./cover.out; 24 | 25 | #### install 26 | ### go install github.com/securego/gosec/v2/cmd/gosec@latest 27 | gosec: 28 | gosec ./... 29 | 30 | #### install 31 | #### go install github.com/gordonklaus/ineffassign@latest 32 | ineffassign: 33 | ineffassign ./... 34 | 35 | 36 | #### install 37 | #### go install honnef.co/go/tools/cmd/staticcheck@latest 38 | staticcheck: 39 | staticcheck ./... 40 | -------------------------------------------------------------------------------- /PullReuqestArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/PullReuqestArrow.png -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Quick Benchmarking 2 | 3 | ## Introduction 4 | Welcome to our benchmarking project! Here, we are conducting performance tests to evaluate different HTTP frameworks in Go. The goal is to better understand how each framework performs under heavy loads and identify which one offers the best performance for different scenarios. 5 | 6 | ## Tools Used 7 | To ensure accurate and reliable benchmarking, we use two powerful load-testing tools: 8 | 9 | - **[k6](./k6/README.md)**: An open-source tool designed for performance and load testing, known for its ease of use and advanced metric analysis. 10 | - **[Vegeta](./vegeta/README.md)**: A highly efficient and flexible load tester that allows custom attack configurations to measure server capacity. 11 | 12 | ## What Are We Measuring? 13 | Our benchmarking evaluates essential metrics, including: 14 | - **Total HTTP Requests**: The total number of processed requests. 15 | - **Requests per Second**: The number of requests handled each second. 16 | - **Average Response Time**: How long the server takes to respond to each request. 17 | - **Data Received and Sent**: The total volume of data transmitted. 18 | - **Error Rate**: The percentage of failed requests. 19 | 20 | ## Project Structure 21 | The project is organized into directories, each containing its own tests and configurations: 22 | 23 | 📁 **k6/** - Load tests using k6. See the [k6 README](./k6/README.md) for details. 24 | 25 | 📁 **vegeta/** - Load tests using Vegeta. See the [Vegeta README](./vegeta/README.md) for details. 26 | 27 | ## How to Run the Tests? 28 | Each directory contains detailed instructions on how to execute the tests. Simply navigate to the corresponding directory and follow the guidelines. 29 | 30 | ## Contribution 31 | If you want to contribute with new tests or improvements, feel free to open a PR or share your ideas! 🚀 32 | 33 | --- 34 | Let's discover together which framework delivers the best performance! 🎯 35 | 36 | -------------------------------------------------------------------------------- /cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID7zCCAtegAwIBAgIUDKcclLVnkJOgvfPfMokMCyxyVvgwDQYJKoZIhvcNAQEL 3 | BQAwgYYxCzAJBgNVBAYTAkJSMRUwEwYDVQQIDAxNSU5BUyBHRVJBSVMxETAPBgNV 4 | BAcMCENPTlRBR0VNMQ8wDQYDVQQKDAZHT09HTEUxCzAJBgNVBAsMAlRJMRIwEAYD 5 | VQQDDAlsb2NhbGhvc3QxGzAZBgkqhkiG9w0BCQEWDG15QGdtYWlsLmNvbTAeFw0y 6 | NTAzMDcxOTA5MzNaFw0yNjAzMDcxOTA5MzNaMIGGMQswCQYDVQQGEwJCUjEVMBMG 7 | A1UECAwMTUlOQVMgR0VSQUlTMREwDwYDVQQHDAhDT05UQUdFTTEPMA0GA1UECgwG 8 | R09PR0xFMQswCQYDVQQLDAJUSTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZI 9 | hvcNAQkBFgxteUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 10 | AoIBAQC9h55aW21BjQNStK563Jid+AMeCC9d8du0twYKTUaSzAcAb82VHnSxlV9D 11 | MZpQSRa59+YXIvJZ7KS5/LfHSV9nwPyIZ6bvpvX6Js1kXPcDn1fkpVC9TVSSV7ec 12 | tIVnfnig67cC05OgVlK4lEkuDPPqcerpE5nflMI3PZRV8FlSPonDjG4QSIoScklp 13 | Z8xvqnPxz42hyCGCRH04YjHyQ6HU7WYAjAtXmrOJnbkYgfz37L2D1o+mk+hM0izO 14 | KOY5bmxTMR0zWy99dXLhFjp/ibw7jvTU8dWFtpwdLBAwQ6itoM2ADZ+5eouNPieb 15 | kP33XWtUK59RSD9eK1MZB18vX4efAgMBAAGjUzBRMB0GA1UdDgQWBBRXKQNPg1pk 16 | y6s90tC2YWterTtxlDAfBgNVHSMEGDAWgBRXKQNPg1pky6s90tC2YWterTtxlDAP 17 | BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJN7XKA+py3knwnFDQ 18 | WpwHx4QbIlZ+ggV3r+Lz/IF710PiGUIOFFg6tYpCmIdJTv5MekqJlvX42dy1MsUS 19 | YhU1HiKIRcBXEoxAqEMMUD7yWwQJeBg8aW8GH+37v8VUSuSG31c7MHr0OY2bhq6K 20 | 9joVagTG2GhwBatC0soUYO7ZbF+jo/ExhFBfPZByY0Gt2rQYgkkHMq90l5P3ZZRK 21 | sK9R4hi6MJ5j7PR0SVXYxuhm+Crf2aI2pZJyilq8Vy8/b2VIAhkI7NB22rHZjViH 22 | sSPZM5ShSiN29JT8vxj3IGPQzkXtmMeluLbZcWkIxlDANiDeKyOvL84EPPNuzb5e 23 | YXSg 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /checkout-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/checkout-main.png -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 jeffotoni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/Logo.png -------------------------------------------------------------------------------- /example/form.post/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/jeffotoni/quick" 8 | ) 9 | 10 | func main() { 11 | q := quick.New() 12 | 13 | q.Post("/postform", func(c *quick.Ctx) error { 14 | 15 | // Get individual values ​​from the form 16 | name := c.FormValue("name") 17 | email := c.FormValue("email") 18 | 19 | // Get all values ​​from the form 20 | allValues := c.FormValues() 21 | 22 | // Respond with the received data 23 | return c.Status(200).JSON(map[string]any{ 24 | "message": "Form received", 25 | "name": name, 26 | "email": email, 27 | "data": allValues, 28 | }) 29 | }) 30 | 31 | fmt.Println("Server running on :3000") 32 | log.Fatal(q.Listen("0.0.0.0:3000")) 33 | } 34 | 35 | // $ curl -X POST http://localhost:3000/postform \ 36 | // -d "name=quick" \ 37 | // -d "email=quick@example.com" 38 | -------------------------------------------------------------------------------- /example/get.ctx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/jeffotoni/quick" 4 | 5 | func main() { 6 | 7 | q := quick.New() 8 | 9 | q.Get("/v1/user", func(c *quick.Ctx) error { 10 | userAgent := c.GetHeader("User-Agent") 11 | ip := c.RemoteIP() 12 | method := c.Method() 13 | path := c.Path() 14 | queryValue := c.QueryParam("search") 15 | 16 | return c.Status(200).JSON(map[string]string{ 17 | "user_agent": userAgent, 18 | "ip": ip, 19 | "method": method, 20 | "path": path, 21 | "search": queryValue, 22 | }) 23 | }) 24 | 25 | q.Listen(":8080") 26 | } 27 | -------------------------------------------------------------------------------- /example/group/group.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/jeffotoni/quick" 4 | 5 | func main() { 6 | q := quick.New(quick.Config{ 7 | MaxBodySize: 5 * 1024 * 1024, 8 | }) 9 | 10 | group := q.Group("/v1") 11 | group.Get("/user", func(c *quick.Ctx) error { 12 | return c.Status(200).SendString("[GET] [GROUP] /v1/user ok!!!") 13 | }) 14 | 15 | group.Post("/user", func(c *quick.Ctx) error { 16 | return c.Status(200).SendString("[POST] [GROUP] /v1/user ok!!!") 17 | }) 18 | 19 | group2 := q.Group("/v2") 20 | 21 | group2.Get("/user", func(c *quick.Ctx) error { 22 | c.Set("Content-Type", "application/json") 23 | return c.Status(200).SendString("Quick in action com [GET] /v2/user!") 24 | }) 25 | 26 | group2.Post("/user", func(c *quick.Ctx) error { 27 | c.Set("Content-Type", "application/json") 28 | return c.Status(200).SendString("Quick in action com [POST] /v2/user!") 29 | }) 30 | 31 | q.Listen("0.0.0.0:8080") 32 | } 33 | -------------------------------------------------------------------------------- /example/logo_lado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/logo_lado.png -------------------------------------------------------------------------------- /example/middleware/basicauth/middl_v1/basicauth.go: -------------------------------------------------------------------------------- 1 | // basic auth 2 | // 3 | // $ curl -u admin:1234 http://localhost:8080/protected 4 | // $ curl -H "Authorization: Basic YWRtaW46MTIzNA==" http://localhost:8080/protected 5 | package main 6 | 7 | import ( 8 | "encoding/base64" 9 | "log" 10 | "net/http" 11 | "strings" 12 | 13 | "github.com/jeffotoni/quick" 14 | ) 15 | 16 | func main() { 17 | 18 | q := quick.New() 19 | 20 | // implementing middleware directly in Use 21 | q.Use(func(next http.Handler) http.Handler { 22 | // credentials 23 | username := "admin" 24 | password := "1234" 25 | 26 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 27 | authHeader := r.Header.Get("Authorization") 28 | if authHeader == "" { 29 | http.Error(w, "Unauthorized", http.StatusUnauthorized) 30 | return 31 | } 32 | 33 | // Check if it starts with "Basic" 34 | if !strings.HasPrefix(authHeader, "Basic ") { 35 | http.Error(w, "Unauthorized", http.StatusUnauthorized) 36 | return 37 | } 38 | 39 | // Decode credentials 40 | payload, err := base64.StdEncoding.DecodeString(authHeader[len("Basic "):]) 41 | if err != nil { 42 | http.Error(w, "Unauthorized", http.StatusUnauthorized) 43 | return 44 | } 45 | 46 | creds := strings.SplitN(string(payload), ":", 2) 47 | if len(creds) != 2 || creds[0] != username || creds[1] != password { 48 | http.Error(w, "Unauthorized", http.StatusUnauthorized) 49 | return 50 | } 51 | next.ServeHTTP(w, r) 52 | }) 53 | }) 54 | 55 | q.Get("/protected", func(c *quick.Ctx) error { 56 | c.Set("Content-Type", "application/json") 57 | return c.SendString("You have accessed a protected route!") 58 | }) 59 | 60 | // Start server 61 | log.Fatal(q.Listen("0.0.0.0:8080")) 62 | 63 | } 64 | -------------------------------------------------------------------------------- /example/middleware/basicauth/middl_v2/basicauth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/jeffotoni/quick" 8 | middleware "github.com/jeffotoni/quick/middleware/basicauth" 9 | ) 10 | 11 | // export USER=admin 12 | // export PASSWORD=1234 13 | 14 | var ( 15 | User = os.Getenv("USER") 16 | Password = os.Getenv("PASSORD") 17 | ) 18 | 19 | // curl -u admin:1234 http://localhost:8080/protected 20 | func main() { 21 | 22 | q := quick.New() 23 | 24 | q.Use(middleware.BasicAuth(User, Password)) 25 | 26 | q.Get("/protected", func(c *quick.Ctx) error { 27 | c.Set("Content-Type", "application/json") 28 | return c.SendString("You have accessed a protected route!") 29 | }) 30 | 31 | // Start server 32 | log.Fatal(q.Listen("0.0.0.0:8080")) 33 | } 34 | -------------------------------------------------------------------------------- /example/middleware/basicauth/middl_v3/basicauth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jeffotoni/quick" 7 | middleware "github.com/jeffotoni/quick/middleware/basicauth" 8 | ) 9 | 10 | // curl -u admin:1234 http://localhost:8080/protected 11 | // or 12 | // curl -H "Authorization: Basic YWRtaW46MTIzNA==" http://localhost:8080/protected 13 | func main() { 14 | 15 | //starting Quick 16 | q := quick.New() 17 | 18 | // calling middleware 19 | q.Use(middleware.BasicAuth("admin", "1234")) 20 | 21 | // everything below Use will apply the middleware 22 | q.Get("/protected", func(c *quick.Ctx) error { 23 | c.Set("Content-Type", "application/json") 24 | return c.SendString("You have accessed a protected route!") 25 | }) 26 | 27 | // Start server 28 | log.Fatal(q.Listen("0.0.0.0:8080")) 29 | } 30 | -------------------------------------------------------------------------------- /example/middleware/basicauth/middl_v4/basicauth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jeffotoni/quick" 7 | middleware "github.com/jeffotoni/quick/middleware/basicauth" 8 | ) 9 | 10 | // curl -u admin:1234 http://localhost:8080/protected 11 | // or 12 | // curl -H "Authorization: Basic YWRtaW46MTIzNA==" http://localhost:8080/protected 13 | func main() { 14 | 15 | q := quick.New() 16 | 17 | // using group to isolate routes and middlewares 18 | gr := q.Group("/") 19 | 20 | // middleware BasicAuth 21 | gr.Use(middleware.BasicAuth("admin", "1234")) 22 | 23 | // route public 24 | q.Get("/v1/user", func(c *quick.Ctx) error { 25 | c.Set("Content-Type", "application/json") 26 | return c.SendString("Public quick route") 27 | }) 28 | 29 | // protected route 30 | gr.Get("/protected", func(c *quick.Ctx) error { 31 | c.Set("Content-Type", "application/json") 32 | return c.SendString("You have accessed a protected route!") 33 | }) 34 | 35 | // Start server 36 | log.Fatal(q.Listen("0.0.0.0:8080")) 37 | } 38 | -------------------------------------------------------------------------------- /example/middleware/cache/basic/main.go: -------------------------------------------------------------------------------- 1 | // Example of basic cache middleware usage in Quick 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "time" 7 | 8 | "github.com/jeffotoni/quick" 9 | "github.com/jeffotoni/quick/middleware/cache" 10 | ) 11 | 12 | func main() { 13 | // Create a new Quick app 14 | q := quick.New() 15 | 16 | // Use the cache middleware with default settings 17 | q.Use(cache.New()) 18 | 19 | // Route 1: Returns the current time 20 | q.Get("/time", func(c *quick.Ctx) error { 21 | return c.String("Current time: " + time.Now().Format(time.RFC1123)) 22 | }) 23 | 24 | // Route 2: Returns a random number 25 | q.Get("/random", func(c *quick.Ctx) error { 26 | return c.String("Random value: " + time.Now().Format("15:04:05.000")) 27 | }) 28 | 29 | // Route 3: Returns JSON data 30 | q.Get("/profile", func(c *quick.Ctx) error { 31 | return c.JSON(quick.M{ 32 | "user": "jeffotoni", 33 | "since": time.Now().Format("2006-01-02 15:04:05"), 34 | }) 35 | }) 36 | 37 | // Start the server 38 | fmt.Println("Server running on http://localhost:3000") 39 | fmt.Println("Try these endpoints:") 40 | fmt.Println(" - GET /time (cached for 1 minute)") 41 | fmt.Println(" - GET /random (cached for 1 minute)") 42 | fmt.Println(" - GET /profile (cached for 1 minute)") 43 | fmt.Println("Check the X-Cache-Status header in the response to see if it's a HIT or MISS") 44 | q.Listen(":3000") 45 | } 46 | -------------------------------------------------------------------------------- /example/middleware/cache/custom-key/main.go: -------------------------------------------------------------------------------- 1 | // Example of cache middleware with custom key generation in Quick 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "time" 7 | 8 | "github.com/jeffotoni/quick" 9 | "github.com/jeffotoni/quick/middleware/cache" 10 | ) 11 | 12 | func main() { 13 | // Create a new Quick q 14 | q := quick.New() 15 | 16 | // Use the cache middleware with custom key generation 17 | q.Use(cache.New(cache.Config{ 18 | Expiration: 1 * time.Minute, 19 | KeyGenerator: func(c *quick.Ctx) string { 20 | return c.Path() + "?lang=" + c.Query["lang"] + "&user=" + c.Query["user"] 21 | }, 22 | CacheHeader: "X-Cache-Status", 23 | StoreResponseHeaders: true, 24 | Methods: []string{quick.MethodGet}, 25 | CacheInvalidator: func(c *quick.Ctx) bool { 26 | return c.Query["clear"] == "1" 27 | }, 28 | })) 29 | 30 | // Route that returns a greeting in the requested language 31 | q.Get("/greeting", func(c *quick.Ctx) error { 32 | lang := c.Query["lang"] 33 | user := c.Query["user"] 34 | if user == "" { 35 | user = "Guest" 36 | } 37 | 38 | greeting := "Hello" 39 | switch lang { 40 | case "pt": 41 | greeting = "Olá" 42 | case "es": 43 | greeting = "Hola" 44 | case "fr": 45 | greeting = "Bonjour" 46 | case "it": 47 | greeting = "Ciao" 48 | case "de": 49 | greeting = "Hallo" 50 | } 51 | 52 | return c.String(fmt.Sprintf("%s, %s! (Generated at %s)", 53 | greeting, user, time.Now().Format(time.RFC3339))) 54 | }) 55 | 56 | // Start the server 57 | fmt.Println("Server running on http://localhost:3000") 58 | fmt.Println("Try these examples:") 59 | fmt.Println(" - GET /greeting?lang=en&user=John") 60 | fmt.Println(" - GET /greeting?lang=pt&user=Maria") 61 | fmt.Println(" - GET /greeting?lang=es&user=Carlos") 62 | fmt.Println(" - GET /greeting?lang=en&user=John (should be cached)") 63 | fmt.Println(" - GET /greeting?lang=en&user=John&clear=1 (invalidates cache)") 64 | fmt.Println("Check the X-Cache-Status header in the response to see if it's a HIT or MISS") 65 | q.Listen(":3000") 66 | } 67 | -------------------------------------------------------------------------------- /example/middleware/compress/compress.gzip.HandlerFunc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jeffotoni/quick" 7 | "github.com/jeffotoni/quick/middleware/compress" 8 | ) 9 | 10 | func main() { 11 | q := quick.New() 12 | 13 | // Enable Gzip middleware 14 | // This will automatically compress responses for clients that support Gzip 15 | q.Use(compress.Gzip()) 16 | 17 | // Define a route that returns a compressed JSON response 18 | q.Get("/v1/compress", func(c *quick.Ctx) error { 19 | // Setting response headers 20 | c.Set("Content-Type", "application/json") 21 | c.Set("Accept-Encoding", "gzip") // Enabling Gzip compression 22 | 23 | // Defining the response structure 24 | type my struct { 25 | Msg string `json:"msg"` 26 | Headers map[string][]string `json:"headers"` 27 | } 28 | 29 | // Returning a JSON response with headers 30 | return c.Status(200).JSON(&my{ 31 | Msg: "Quick", 32 | Headers: c.Headers, 33 | }) 34 | }) 35 | 36 | // Start the HTTP server on port 8080 37 | // The server will listen for incoming requests at http://localhost:8080 38 | log.Fatal(q.Listen("0.0.0.0:8080")) 39 | } 40 | 41 | // $ curl -X GET http://localhost:8080/v1/compress -H "Accept-Encoding: gzip" --compressed -i 42 | -------------------------------------------------------------------------------- /example/middleware/compress/compress.gzip.net.http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | //"github.com/jeffotoni/quick/middleware/compress" 7 | ) 8 | 9 | func main() { 10 | mux := http.NewServeMux() 11 | 12 | // Route with compression enabled using the Gzip middleware 13 | // mux.Handle("/v1/compress", compress.Gzip()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | // w.Header().Set("Content-Type", "application/json") 15 | // w.WriteHeader(http.StatusOK) 16 | // w.Write([]byte(`{"message": "Hello, net/http with Gzip!"}`)) 17 | // }))) 18 | 19 | // Starting the HTTP server on port 8080 20 | log.Println("Server running at http://localhost:8080") 21 | log.Fatal(http.ListenAndServe(":8080", mux)) 22 | } 23 | 24 | // $ curl -X GET http://localhost:8080/v1/compress -H "Accept-Encoding: gzip" --compressed -i 25 | -------------------------------------------------------------------------------- /example/middleware/compress/compress.gzip/compress_gzip.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jeffotoni/quick" 7 | "github.com/jeffotoni/quick/middleware/compress" 8 | ) 9 | 10 | func main() { 11 | q := quick.New() 12 | 13 | // Enable Gzip middleware 14 | // This will automatically compress responses for clients that support Gzip 15 | q.Use(compress.Gzip()) 16 | 17 | // Define a route that returns a compressed JSON response 18 | q.Get("/v1/compress", func(c *quick.Ctx) error { 19 | // Setting response headers 20 | c.Set("Content-Type", "application/json") 21 | c.Set("Accept-Encoding", "gzip") // Enabling Gzip compression 22 | 23 | // Defining the response structure 24 | type my struct { 25 | Msg string `json:"msg"` 26 | Headers map[string][]string `json:"headers"` 27 | } 28 | 29 | // Returning a JSON response with headers 30 | return c.Status(200).JSON(&my{ 31 | Msg: "Quick ", 32 | Headers: c.Headers, 33 | }) 34 | }) 35 | 36 | // Start the HTTP server on port 8080 37 | // The server will listen for incoming requests at http://localhost:8080 38 | log.Fatal(q.Listen("0.0.0.0:8080")) 39 | } 40 | 41 | //$ curl -X GET http://localhost:8080/v1/compress -H "Accept-Encoding: gzip" --compressed -i 42 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.nativo.net_http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | 7 | "github.com/jeffotoni/quick/middleware/cors" 8 | ) 9 | 10 | type MyHandler struct{} 11 | 12 | // / Example cURL to test 13 | // curl -X OPTIONS -v http://localhost:8080/v1/user 14 | // 15 | // curl -X OPTIONS -H "Origin: http://localhost:3000/" \ 16 | // -H "Access-Control-Request-Method: POST" -v \ 17 | // http://localhost:8080/v1/user 18 | func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 19 | w.Header().Add("Content-Type", "application/json") 20 | b, err := io.ReadAll(r.Body) 21 | if err != nil { 22 | w.WriteHeader(400) 23 | w.Write([]byte(`{"msg":"error"}`)) 24 | return 25 | } 26 | w.WriteHeader(200) 27 | w.Write(b) 28 | } 29 | 30 | func OtherHandler(w http.ResponseWriter, r *http.Request) { 31 | w.Header().Add("Content-Type", "application/json") 32 | w.WriteHeader(200) 33 | w.Write([]byte("Outro endpoint!")) 34 | } 35 | 36 | func main() { 37 | mux := http.NewServeMux() 38 | mux.Handle("/v1/user", &MyHandler{}) 39 | mux.HandleFunc("/outro", OtherHandler) 40 | 41 | newmux := cors.Default().Handler(mux) 42 | println("server: :8080") 43 | http.ListenAndServe(":8080", newmux) 44 | } 45 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.nativo.net_http_v2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/jeffotoni/quick/middleware/cors" 7 | ) 8 | 9 | func main() { 10 | c := cors.New(cors.Config{ 11 | AllowedOrigins: []string{"*"}, 12 | }) 13 | 14 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 | w.Header().Set("Content-Type", "application/json") 16 | w.Write([]byte("{\"name\": \"quick\"}")) 17 | }) 18 | 19 | wrappedHandler := c(handler) 20 | 21 | http.ListenAndServe(":8080", wrappedHandler) 22 | } 23 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.nativo.net_http_v3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/jeffotoni/quick/middleware/cors" 7 | ) 8 | 9 | func main() { 10 | 11 | corsMiddleware := cors.New(cors.Config{ 12 | AllowedOrigins: []string{"*"}, 13 | AllowedMethods: []string{"GET", "POST", "OPTIONS"}, 14 | AllowedHeaders: []string{"Content-Type", "Authorization"}, 15 | }) 16 | 17 | mux := http.NewServeMux() 18 | 19 | mux.HandleFunc("/v1/user", func(w http.ResponseWriter, r *http.Request) { 20 | w.Header().Set("Content-Type", "application/json") 21 | w.Write([]byte("{\"message\": \"User endpoint\"}")) 22 | }) 23 | 24 | wrappedMux := corsMiddleware(mux) 25 | 26 | http.ListenAndServe(":8080", wrappedMux) 27 | } 28 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.nativo.quick/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/jeffotoni/quick" 8 | "github.com/jeffotoni/quick/middleware/cors" 9 | // cors "github.com/rs/cors" 10 | ) 11 | 12 | // / Example cURL to test 13 | // curl -X OPTIONS -v http://localhost:8080/v1/user 14 | // 15 | // curl -X OPTIONS -H "Origin: http://localhost:3000/" \ 16 | // -H "Access-Control-Request-Method: POST" -v \ 17 | // http://localhost:8080/v1/user 18 | func main() { 19 | q := quick.New() 20 | 21 | q.Use(cors.New(cors.Config{ 22 | AllowedOrigins: []string{"*"}, 23 | AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 24 | AllowedHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"}, 25 | AllowCredentials: true, 26 | Debug: true, 27 | })) 28 | 29 | q.Post("/v1/user", func(c *quick.Ctx) error { 30 | c.Set("Content-Type", "application/json") 31 | type My struct { 32 | Name string `json:"name"` 33 | Year string `json:"year"` 34 | } 35 | 36 | var my My 37 | err := c.BodyParser(&my) 38 | fmt.Println("byte:", string(c.Body())) 39 | 40 | if err != nil { 41 | return c.Status(400).SendString(err.Error()) 42 | } 43 | 44 | fmt.Println("String:", c.BodyString()) 45 | return c.Status(200).JSON(my) 46 | }) 47 | 48 | log.Fatal(q.Listen("0.0.0.0:8080")) 49 | } 50 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.quick.cors.confg/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/jeffotoni/quick" 7 | "github.com/jeffotoni/quick/middleware/cors" 8 | ) 9 | 10 | func main() { 11 | // CORS middleware configuration 12 | q := quick.New() 13 | 14 | q.Use(cors.New(cors.Config{ 15 | AllowedOrigins: []string{"https://httpbin.org"}, // Allow requests only from this domain 16 | AllowedMethods: []string{"GET", "POST", "OPTIONS"}, // Allowed HTTP methods 17 | AllowedHeaders: []string{"Content-Type", "Authorization"}, // Allowed headers 18 | AllowCredentials: true, // Allow credentials (cookies, auth headers) 19 | MaxAge: 600, // Cache CORS preflight request for 10 minutes 20 | })) 21 | 22 | // OPTIONS route to handle preflight CORS requests 23 | q.Options("/api/data", func(c *quick.Ctx) error { 24 | return c.Status(204).Send(nil) // Returns an empty response with status 204 (No Content) 25 | }) 26 | 27 | // GET route protected by CORS 28 | q.Get("/api/data", func(c *quick.Ctx) error { 29 | c.Set("Content-Type", "application/json") 30 | return c.SendString(`{"message": "Hello, CORS!"}`) 31 | }) 32 | 33 | log.Println("Server running at http://localhost:8080") 34 | log.Fatal(q.Listen("0.0.0.0:8080")) 35 | } 36 | 37 | // Example cURL requests to test CORS 38 | 39 | // Preflight CORS request (OPTIONS method) 40 | // This is sent by browsers before making an actual cross-origin request 41 | // 42 | // curl -X OPTIONS -H "Origin: https://httpbin.org" \ 43 | // -H "Access-Control-Request-Method: GET" \ 44 | // -H "Access-Control-Request-Headers: Content-Type, Authorization" \ 45 | // -v http://localhost:8080/api/data 46 | 47 | // Actual GET request with CORS headers 48 | // 49 | // curl -X GET -H "Origin: https://httpbin.org" \ 50 | // -H "Content-Type: application/json" \ 51 | // -H "Authorization: Bearer mytoken123" \ 52 | // -v http://localhost:8080/api/data 53 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.quick.group/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/jeffotoni/quick" 8 | "github.com/jeffotoni/quick/middleware/cors" 9 | // cors "github.com/rs/cors" 10 | ) 11 | 12 | // / Example cURL to test 13 | // curl -X OPTIONS -v http://localhost:8080/v1/user 14 | // 15 | // curl -X OPTIONS -H "Origin: http://localhost:3000/" \ 16 | // -H "Access-Control-Request-Method: POST" -v \ 17 | // http://localhost:8080/v1/user 18 | func main() { 19 | q := quick.New() 20 | 21 | group := q.Group("/v1") 22 | 23 | group.Use(cors.New(cors.Config{ 24 | AllowedOrigins: []string{"*"}, 25 | })) 26 | 27 | group.Post("/user", func(c *quick.Ctx) error { 28 | c.Set("Content-Type", "application/json") 29 | type My struct { 30 | Name string `json:"name"` 31 | Year string `json:"year"` 32 | } 33 | 34 | var my My 35 | err := c.BodyParser(&my) 36 | fmt.Println("byte:", string(c.Body())) 37 | 38 | if err != nil { 39 | return c.Status(400).SendString(err.Error()) 40 | } 41 | 42 | fmt.Println("String:", c.BodyString()) 43 | return c.Status(200).JSON(my) 44 | }) 45 | 46 | q.Post("/v2/user", func(c *quick.Ctx) error { 47 | c.Set("Content-Type", "application/json") 48 | return c.Status(200).SendString("Quick in action!!") 49 | }) 50 | log.Fatal(q.Listen("0.0.0.0:8080")) 51 | } 52 | -------------------------------------------------------------------------------- /example/middleware/cors/cors.with.middleware/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/jeffotoni/quick" 8 | "github.com/jeffotoni/quick/middleware/cors" 9 | ) 10 | 11 | // / Example cURL to test 12 | // curl -X OPTIONS -v http://localhost:8080/v1/user 13 | // 14 | // curl -X OPTIONS -H "Origin: http://localhost:3000/" \ 15 | // -H "Access-Control-Request-Method: POST" -v \ 16 | // http://localhost:8080/v1/user 17 | func main() { 18 | app := quick.New() 19 | 20 | app.Use(cors.New(cors.Config{ 21 | AllowedOrigins: []string{"*"}, 22 | AllowedMethods: []string{"*"}, 23 | AllowedHeaders: []string{"*"}, 24 | })) 25 | 26 | app.Post("/v1/user", func(c *quick.Ctx) error { 27 | c.Set("Content-Type", "application/json") 28 | 29 | my := struct { 30 | Name string `json:"name"` 31 | Year int `json:"year"` 32 | }{ 33 | Name: "Teste", 34 | Year: 2024, 35 | } 36 | 37 | fmt.Println("Enviando resposta:", my) 38 | return c.Status(200).JSON(my) 39 | }) 40 | 41 | log.Fatal(app.Listen("0.0.0.0:8080")) 42 | } 43 | -------------------------------------------------------------------------------- /example/middleware/cors/server.html/main.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | package main 4 | 5 | import "github.com/jeffotoni/quick" 6 | 7 | func main() { 8 | 9 | // start Quick 10 | q := quick.New() 11 | 12 | // start dir files 13 | q.Static("/static", "./static") 14 | 15 | // server files 16 | q.Get("/", func(c *quick.Ctx) error { 17 | c.File("./static/index.html") 18 | return nil 19 | }) 20 | 21 | q.Listen("0.0.0.0:3000") 22 | } 23 | 24 | // $ curl --location 'http://localhost:8080/' 25 | -------------------------------------------------------------------------------- /example/middleware/cors/server.html/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quick Post 7 | 8 | 9 |

Send Data to API - Quick

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

Status Code:

20 |

21 | 
22 |  
47 | 
48 | 


--------------------------------------------------------------------------------
/example/middleware/dynamic/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 	"net/http"
 6 | 	"time"
 7 | 
 8 | 	"github.com/jeffotoni/quick"
 9 | 	"github.com/jeffotoni/quick/rand"
10 | )
11 | 
12 | func main() {
13 | 	q := quick.New()
14 | 
15 | 	// Trace-ID middleware
16 | 	q.Use(func(h http.Handler) http.Handler {
17 | 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18 | 			start := time.Now()
19 | 			traceID := rand.TraceID() // internal quick
20 | 
21 | 			// Inject the Trace-ID into the request and response header
22 | 			r.Header.Set("X-Trace-ID", traceID)
23 | 			w.Header().Set("X-Trace-ID", traceID)
24 | 
25 | 			// Print simple log with Trace-ID
26 | 			log.Printf("[Trace-ID: %s] -> Request start %s %s\n", traceID, r.Method, r.URL.Path)
27 | 			h.ServeHTTP(w, r)
28 | 			duration := time.Since(start)
29 | 
30 | 			log.Printf("[Trace-ID: %s] <- End of request duration:[(%v)]\n", traceID, duration)
31 | 		})
32 | 	})
33 | 
34 | 	q.Get("/v1/user/:name", func(c *quick.Ctx) error {
35 | 		name := c.Param("name")
36 | 		c.Set("Content-Type", "application/json")
37 | 		return c.Status(200).JSON(quick.M{
38 | 			"msg": name,
39 | 		})
40 | 	})
41 | 
42 | 	q.Listen("0.0.0.0:8080")
43 | }
44 | 


--------------------------------------------------------------------------------
/example/middleware/etag/README.md:
--------------------------------------------------------------------------------
1 | ## 📌 ETag Middleware in Quick ![Quick Logo](/quick.png)
2 | 
3 | 🚧 **Coming soon!** We’re working on it! 🛠️


--------------------------------------------------------------------------------
/example/middleware/healthcheck/README.md:
--------------------------------------------------------------------------------
 1 | ## 🛠️ Healthcheck Middleware in Quick ![Quick Logo](/quick.png)
 2 | 
 3 | **Healthcheck** is a middleware this package provides a simple way to check the health of your application.
 4 | 
 5 | ---
 6 | ### ✨ Features
 7 | 
 8 | - Simple healthcheck endpoint
 9 | - Customizable endpoint
10 | 
11 | ---
12 | ### 🧩 Example Usage
13 | ```go
14 | package main
15 | 
16 | import (
17 | 	"github.com/jeffotoni/quick"
18 | 	"github.com/seuusuario/healthcheck"
19 | 	
20 | )
21 | 
22 | func main() {
23 | 	q := quick.New()
24 | 
25 | 	// Use Healthcheck middleware with default healthcheck endpoint
26 | 	q.Use(healthcheck.New(
27 | 		healthcheck.Options{
28 | 			App: q,
29 | 		},
30 | 	))
31 | 
32 | 	q.Get("/", func(c *quick.Ctx) error {
33 | 		return c.Status(200).String("Home page")
34 | 	})
35 | 
36 | 	log.Fatalln(q.Listen(":8080"))
37 | }
38 | ```
39 | ### 📌 cURL
40 | ```bash
41 | $ curl -X GET 'http://localhost:8080/healthcheck'
42 | ```
43 | 
44 | ### 📥 Example Output
45 | 
46 | Here's an example of the response returned:
47 | 
48 | ```sh
49 | OK
50 | ```
51 | 
52 | ---
53 | ### ⚙️ Custom Configuration
54 | 
55 | You can change the endpoint by providing an Options struct:
56 | 
57 | ```go
58 | q.Use(healthcheck.New(
59 | 	healthcheck.Options{
60 | 		App: q,
61 | 		Endpoint: "/v1/health",
62 | 	},
63 | ))
64 | ```
65 | 
66 | 


--------------------------------------------------------------------------------
/example/middleware/healthcheck/custom/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/healthcheck"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	// register healthcheck middleware with custom endpoint
14 | 	q.Use(healthcheck.New(
15 | 		healthcheck.Options{
16 | 			Endpoint: "/health",
17 | 			Probe: func(c *quick.Ctx) bool {
18 | 				return true
19 | 			},
20 | 			App: q,
21 | 		},
22 | 	))
23 | 
24 | 	q.Get("/", func(c *quick.Ctx) error {
25 | 		return c.Status(200).String("Home page")
26 | 	})
27 | 
28 | 	log.Fatalln(q.Listen(":8080"))
29 | }
30 | 


--------------------------------------------------------------------------------
/example/middleware/healthcheck/simple/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/healthcheck"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	// register healthcheck middleware
14 | 	q.Use(healthcheck.New(
15 | 		healthcheck.Options{
16 | 			App: q,
17 | 		},
18 | 	))
19 | 
20 | 	q.Get("/", func(c *quick.Ctx) error {
21 | 		return c.Status(200).String("Home page")
22 | 	})
23 | 
24 | 	log.Fatalln(q.Listen(":8080"))
25 | }
26 | 


--------------------------------------------------------------------------------
/example/middleware/helmet/custom/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/helmet"
 6 | )
 7 | 
 8 | func main() {
 9 | 	q := quick.New()
10 | 
11 | 	// Use Helmet middleware with custom security headers
12 | 	q.Use(helmet.Helmet(helmet.Options{
13 | 		ContentSecurityPolicy: "default-src 'self'; img-src *; style-src 'self' fonts.googleapis.com",
14 | 		CSPReportOnly:         true,
15 | 		HSTSMaxAge:            86400,
16 | 		HSTSExcludeSubdomains: true,
17 | 		HidePoweredBy:         true,
18 | 		PermissionsPolicy:     "geolocation=(), microphone=()",
19 | 		Next: func(c *quick.Ctx) bool {
20 | 			return c.Path() == "/public"
21 | 		},
22 | 	}))
23 | 
24 | 	// Simple route to test headers
25 | 	q.Get("/v1/user", func(c *quick.Ctx) error {
26 | 
27 | 		// list all headers
28 | 		headers := make(map[string]string)
29 | 		for k, v := range c.Response.Header() {
30 | 			if len(v) > 0 {
31 | 				headers[k] = v[0]
32 | 			}
33 | 		}
34 | 		return c.Status(200).JSONIN(headers)
35 | 	})
36 | 
37 | 	q.Listen(":8080")
38 | }
39 | 
40 | // $ curl -X GET 'http://localhost:8080/v1/user'
41 | 


--------------------------------------------------------------------------------
/example/middleware/helmet/simple/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/helmet"
 6 | )
 7 | 
 8 | func main() {
 9 | 	q := quick.New()
10 | 
11 | 	// Use Helmet middleware with default security headers
12 | 	q.Use(helmet.Helmet())
13 | 
14 | 	// Simple route to test headers
15 | 	q.Get("/v1/user", func(c *quick.Ctx) error {
16 | 
17 | 		// list all headers
18 | 		headers := make(map[string]string)
19 | 		for k, v := range c.Response.Header() {
20 | 			if len(v) > 0 {
21 | 				headers[k] = v[0]
22 | 			}
23 | 		}
24 | 		return c.Status(200).JSONIN(headers)
25 | 	})
26 | 
27 | 	q.Listen(":8080")
28 | }
29 | 
30 | // $ curl -X GET 'http://localhost:8080/v1/user'
31 | 


--------------------------------------------------------------------------------
/example/middleware/limiter/basic/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/limiter"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	// Apply the rate limiter middleware
14 | 	q.Use(limiter.New(limiter.Config{
15 | 		// Maximum 10 requests allowed per IP
16 | 		Max: 10,
17 | 		// The limit resets after 5 seconds
18 | 		Expiration: 5 * time.Second,
19 | 		KeyGenerator: func(c *quick.Ctx) string {
20 | 			// Uses the client's IP address as the key
21 | 			return c.RemoteIP()
22 | 		},
23 | 		LimitReached: func(c *quick.Ctx) error {
24 | 			c.Set("Content-Type", "application/json")
25 | 			// The client should wait 10 seconds before retrying
26 | 			c.Set("Retry-After", "10")
27 | 			return c.Status(quick.StatusTooManyRequests).JSON(map[string]string{
28 | 				"error":   "Too many requests",
29 | 				"message": "You have exceeded the request limit. Please wait 1 second and try again.",
30 | 				// Suggests a 1-second delay before retrying
31 | 				"retry_after": "10s",
32 | 			})
33 | 		},
34 | 	}))
35 | 
36 | 	// Define a simple GET route
37 | 	q.Get("/", func(c *quick.Ctx) error {
38 | 		return c.Status(200).JSON(map[string]string{"msg": "Quick in action!"})
39 | 	})
40 | 
41 | 	// Start the server on port 8080
42 | 	q.Listen(":8080")
43 | }
44 | 
45 | // To test with Rate Limiter, use these curl commands:
46 | // $ curl --location 'http://localhost:8080/'
47 | 
48 | //Script
49 | // Function to test the Rate Limiter
50 | // async function testRateLimiter() {
51 | 
52 | //     function delay(ms) {
53 | //         return new Promise(resolve => setTimeout(resolve, ms));
54 | //     }
55 | 
56 | //     for (let i = 0; i < 10; i++) {
57 | //         pm.sendRequest("http://localhost:8080/", function (err, res) {
58 | //             console.log(`Request ${i + 1}: Status - ${res.code} | Body - ${res.text()}`);
59 | //         });
60 | 
61 | //         await delay(200);
62 | //     }
63 | // }
64 | // testRateLimiter();
65 | 


--------------------------------------------------------------------------------
/example/middleware/logger/net.http/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick/middleware/logger"
 5 | 	"net/http"
 6 | )
 7 | 
 8 | func main() {
 9 | 	loggerMiddleware := logger.New()
10 | 
11 | 	slogLoggerMiddleware := logger.New(logger.Config{
12 | 		Format: "slog",
13 | 	})
14 | 
15 | 	jsonLoggerMiddleware := logger.New(logger.Config{
16 | 		Format: "json",
17 | 	})
18 | 
19 | 	CustomLoggerMiddleware := logger.New(logger.Config{
20 | 		Format:  "slog",
21 | 		Pattern: "[${time}] ${level} ${method} ${path} ${status} - ${latency}",
22 | 	})
23 | 
24 | 	mux := http.NewServeMux()
25 | 
26 | 	mux.Handle("/v1/logger", loggerMiddleware(
27 | 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28 | 			w.Write([]byte("Hello, Quick middleware default 💕!"))
29 | 		})))
30 | 
31 | 	mux.Handle("/v1/logger/json", jsonLoggerMiddleware(
32 | 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
33 | 			w.Write([]byte("Hello, Quick middleware json💕!"))
34 | 		})))
35 | 
36 | 	mux.Handle("/v1/logger/slog", slogLoggerMiddleware(
37 | 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
38 | 			w.Write([]byte("Hello, Quick middleware slog 💕!"))
39 | 		})))
40 | 
41 | 	mux.Handle("/v1/logger/custom", CustomLoggerMiddleware(
42 | 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
43 | 			w.Write([]byte("Hello, Quick middleware custom💕!"))
44 | 		})))
45 | 
46 | 	http.ListenAndServe(":8080", mux)
47 | }
48 | 


--------------------------------------------------------------------------------
/example/middleware/logger/simple/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/logger"
 6 | )
 7 | 
 8 | // curl -i -XGET localhost:8080/v1/logger
 9 | func main() {
10 | 
11 | 	q := quick.New()
12 | 	q.Use(logger.New())
13 | 
14 | 	q.Use(logger.New(logger.Config{
15 | 		Level: "DEGUB",
16 | 	}))
17 | 
18 | 	q.Use(logger.New(logger.Config{
19 | 		Level: "WARN",
20 | 	}))
21 | 
22 | 	q.Get("/v1/logger", func(c *quick.Ctx) error {
23 | 		c.Set("Content-Type", "application/json")
24 | 
25 | 		return c.Status(200).JSON(quick.M{
26 | 			"msg": "Quick",
27 | 		})
28 | 	})
29 | 
30 | 	q.Listen("0.0.0.0:8080")
31 | }
32 | 


--------------------------------------------------------------------------------
/example/middleware/logger/with.format/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/logger"
 6 | )
 7 | 
 8 | func main() {
 9 | 
10 | 	q := quick.New()
11 | 
12 | 	q.Use(logger.New(logger.Config{
13 | 		Format:  "text",
14 | 		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
15 | 		Level:   "DEBUG",
16 | 		CustomFields: map[string]string{
17 | 			"user_id": "usr-001",
18 | 			"trace":   "trace-debug",
19 | 		},
20 | 	}))
21 | 
22 | 	q.Use(logger.New(logger.Config{
23 | 		Format:  "text",
24 | 		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
25 | 		Level:   "INFO",
26 | 		CustomFields: map[string]string{
27 | 			"user_id": "usr-002",
28 | 			"trace":   "trace-info",
29 | 		},
30 | 	}))
31 | 
32 | 	q.Use(logger.New(logger.Config{
33 | 		Format:  "text",
34 | 		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
35 | 		Level:   "WARN",
36 | 		CustomFields: map[string]string{
37 | 			"user_id": "usr-003",
38 | 			"trace":   "trace-warn",
39 | 		},
40 | 	}))
41 | 
42 | 	// Definir rota GET para gerar logs
43 | 	q.Get("/v1/logger", func(c *quick.Ctx) error {
44 | 		c.Set("Content-Type", "application/json")
45 | 
46 | 		// Retornar resposta JSON
47 | 		return c.Status(200).JSON(quick.M{
48 | 			"msg": "Quick",
49 | 		})
50 | 	})
51 | 
52 | 	// Iniciar o servidor na porta 8080
53 | 	q.Listen("0.0.0.0:8080")
54 | }
55 | 
56 | // $ curl -i -XGET localhost:8080/v1/logger
57 | 


--------------------------------------------------------------------------------
/example/middleware/logger/with.json/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/logger"
 6 | )
 7 | 
 8 | func main() {
 9 | 
10 | 	q := quick.New()
11 | 
12 | 	// Apply logger with JSON format
13 | 	q.Use(logger.New(logger.Config{
14 | 		Format: "json",
15 | 		Level:  "INFO",
16 | 	}))
17 | 
18 | 	q.Use(logger.New(logger.Config{
19 | 		Format:  "json",
20 | 		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
21 | 		Level:   "DEBUG",
22 | 		CustomFields: map[string]string{
23 | 			"user_id": "usr-001",
24 | 			"trace":   "trace-debug",
25 | 		},
26 | 	}))
27 | 
28 | 	// Apply the logger middleware with structured logging (slog)
29 | 	q.Use(logger.New(logger.Config{
30 | 		Format: "json",
31 | 		Level:  "WARN",
32 | 		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
33 | 			"user=${user_id} trace=${trace}\n",
34 | 		CustomFields: map[string]string{
35 | 			"user_id": "usr-001",
36 | 			"trace":   "trace-warn",
37 | 		},
38 | 	}))
39 | 
40 | 	// Define an endpoint that triggers logging
41 | 	q.Get("/v1/logger/json", func(c *quick.Ctx) error {
42 | 		c.Set("Content-Type", "application/json")
43 | 
44 | 		return c.Status(200).JSON(quick.M{
45 | 			"msg": "JSON logging example",
46 | 		})
47 | 	})
48 | 
49 | 	// Start the server
50 | 	q.Listen("0.0.0.0:8080")
51 | }
52 | 
53 | // $ curl -i -XGET localhost:8080/v1/logger/json
54 | 


--------------------------------------------------------------------------------
/example/middleware/logger/with.slog/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | 	"github.com/jeffotoni/quick/middleware/logger"
 6 | )
 7 | 
 8 | func main() {
 9 | 
10 | 	q := quick.New()
11 | 
12 | 	// Apply the logger middleware with structured logging (slog)
13 | 	q.Use(logger.New(logger.Config{
14 | 		Format: "slog",
15 | 		Level:  "DEBUG",
16 | 		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
17 | 			"user=${user_id} trace=${trace}\n",
18 | 		CustomFields: map[string]string{
19 | 			"user_id": "99999",
20 | 			"trace":   "trace-debug",
21 | 		},
22 | 	}))
23 | 
24 | 	// Apply the logger middleware with structured logging (slog)
25 | 	q.Use(logger.New(logger.Config{
26 | 		Format: "slog",
27 | 		Level:  "INFO",
28 | 		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
29 | 			"user=${user_id} trace=${trace}\n",
30 | 		CustomFields: map[string]string{
31 | 			"user_id": "99999",
32 | 			"trace":   "trace-info",
33 | 		},
34 | 	}))
35 | 
36 | 	// Apply the logger middleware with structured logging (slog)
37 | 	q.Use(logger.New(logger.Config{
38 | 		Format: "slog",
39 | 		Level:  "WARN",
40 | 		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
41 | 			"user=${user_id} trace=${trace}\n",
42 | 		CustomFields: map[string]string{
43 | 			"user_id": "99999",
44 | 			"trace":   "trace-warn",
45 | 		},
46 | 	}))
47 | 
48 | 	// Define a test route
49 | 	q.Get("/v1/logger/slog", func(c *quick.Ctx) error {
50 | 		c.Set("Content-Type", "application/json")
51 | 
52 | 		return c.Status(200).JSON(quick.M{
53 | 			"msg": "Structured logging with slog",
54 | 		})
55 | 	})
56 | 
57 | 	// Start the server
58 | 	q.Listen("0.0.0.0:8080")
59 | }
60 | 
61 | // $ curl -i -XGET localhost:8080/v1/logger/slog
62 | 


--------------------------------------------------------------------------------
/example/middleware/maxbody/any.config/maxbody.any.config.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/maxbody"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	q.Use(maxbody.New(50000))
14 | 
15 | 	q.Post("/v1/user/maxbody/any", func(c *quick.Ctx) error {
16 | 		c.Set("Content-Type", "application/json")
17 | 
18 | 		log.Printf("body: %s", c.BodyString())
19 | 		return c.Status(200).Send(c.Body())
20 | 	})
21 | 
22 | 	log.Fatal(q.Listen("0.0.0.0:8080"))
23 | }
24 | 
25 | // $ curl -i -XPOST http://0.0.0.0:8080/v1/user/maxbody/any -d '{"data":"quick is awesome!"}'
26 | 


--------------------------------------------------------------------------------
/example/middleware/maxbody/default.config/maxbody.default.config.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/maxbody"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	q.Use(maxbody.New())
14 | 
15 | 	q.Post("/v1/user/maxbody", func(c *quick.Ctx) error {
16 | 		c.Set("Content-Type", "application/json")
17 | 
18 | 		log.Printf("body: %s", c.BodyString())
19 | 		return c.Status(200).Send(c.Body())
20 | 	})
21 | 
22 | 	log.Fatal(q.Listen("0.0.0.0:8080"))
23 | }
24 | 
25 | // $ curl -i -XPOST http://0.0.0.0:8080/v1/user/maxbody -d '{"data":"quick is awesome!"}'
26 | 


--------------------------------------------------------------------------------
/example/middleware/maxbody/length.too.large/maxbody.entity.large.config.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | 	"github.com/jeffotoni/quick/middleware/maxbody"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New()
12 | 
13 | 	q.Use(maxbody.New(0))
14 | 
15 | 	q.Post("/v1/user/maxbody/large", func(c *quick.Ctx) error {
16 | 		c.Set("Content-Type", "application/json")
17 | 
18 | 		log.Printf("body: %s", c.BodyString())
19 | 		return c.Status(200).Send(c.Body())
20 | 	})
21 | 
22 | 	log.Fatal(q.Listen("0.0.0.0:8080"))
23 | }
24 | 
25 | // $ curl -i -XPOST http://0.0.0.0:8080/v1/user/maxbody/large -d '{"data":"quick is awesome!"}'
26 | 


--------------------------------------------------------------------------------
/example/middleware/maxbody/max.bytes.reader/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"io"
 5 | 	"log"
 6 | 	"net/http"
 7 | 
 8 | 	"github.com/jeffotoni/quick"
 9 | )
10 | 
11 | const maxBodySize = 1024 // 1KB
12 | 
13 | // Start the server and apply request body size limit middleware
14 | func main() {
15 | 	q := quick.New()
16 | 
17 | 	// Define route with extra validation using MaxBytesReader
18 | 	q.Post("/v1/user/maxbody/max", func(c *quick.Ctx) error {
19 | 		c.Set("Content-Type", "application/json")
20 | 
21 | 		// Apply MaxBytesReader to prevent oversized payloads
22 | 		c.Request.Body = quick.MaxBytesReader(c.Response, c.Request.Body, maxBodySize)
23 | 
24 | 		// Securely read the request body
25 | 		body, err := io.ReadAll(c.Request.Body)
26 | 		if err != nil {
27 | 			log.Printf("Error reading request body: %v", err)
28 | 			return c.Status(http.StatusRequestEntityTooLarge).String("Request body too large")
29 | 		}
30 | 		return c.Status(http.StatusOK).Send(body)
31 | 	})
32 | 
33 | 	log.Println("Server running at http://0.0.0.0:8080")
34 | 	log.Fatal(q.Listen("0.0.0.0:8080"))
35 | }
36 | 
37 | // $ curl -X POST http://0.0.0.0:8080/v1/user/maxbody/max \
38 | //      -H "Content-Type: application/json" \
39 | //      --data-binary @<(head -c 2048  2 milliseconds
25 | 	// - "2s"        => 2 seconds
26 | 	// - "2min"      => 2 minutes
27 | 	//
28 | 	// Example Usage:
29 | 	//
30 | 	// client.WithRetry(
31 | 	// 		client.RetryConfig{
32 | 	// 			MaxRetries: 2,
33 | 	// 			Delay:      1 * time.Second,
34 | 	// 			UseBackoff: true,
35 | 	// 			Statuses:   []int{500},
36 | 	// 			EnableLog:  true,
37 | 	// 		}),
38 | 	//
39 | 	// This configuration will retry up to 3 times with an exponential backoff starting at 2 seconds,
40 | 	// and will only retry if the response status is 500, 502, 503, or 504.
41 | 	cClient := client.New(
42 | 		client.WithRetry(
43 | 			client.RetryConfig{
44 | 				MaxRetries: 2,
45 | 				Delay:      1 * time.Second,
46 | 				UseBackoff: true,
47 | 				Statuses:   []int{500},
48 | 				FailoverURLs: []string{
49 | 					"http://backup1",
50 | 					"https://reqres.in/api/users",
51 | 					"https://httpbin.org/get"},
52 | 				EnableLog: true,
53 | 			}),
54 | 	)
55 | 
56 | 	resp, err := cClient.Get("https://httpbin_error.org/get")
57 | 	if err != nil {
58 | 		log.Fatal(err)
59 | 	}
60 | 	fmt.Println("GET response:", string(resp.Body))
61 | }
62 | 


--------------------------------------------------------------------------------
/example/quick.http.client/retry.new/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"log"
 6 | 	"time"
 7 | 
 8 | 	"github.com/jeffotoni/quick/http/client"
 9 | )
10 | 
11 | // Example of creating an HTTP client using a fluent and modular approach.
12 | // This allows fine-grained control over HTTP settings without requiring a full config struct.
13 | //
14 | // - WithRetry: Enables automatic retries for specific HTTP status codes (500, 502, 503, 504)
15 | // - WithHeaders: Adds custom headers (e.g., Authorization: "Bearer token").
16 | func main() {
17 | 	// Create a new Quick HTTP client with retry and custom headers
18 | 	cC := client.New(
19 | 		// Enables automatic retries on failed requests
20 | 		client.WithRetry(client.RetryConfig{
21 | 			MaxRetries: 3,                         // Maximum number of retry attempts
22 | 			Delay:      1 * time.Second,           // Delay before each retry
23 | 			UseBackoff: false,                     // Disables exponential backoff (fixed retry delay)
24 | 			Statuses:   []int{502, 503, 504, 403}, // Retries only on these HTTP status codes
25 | 			FailoverURLs: []string{ // Backup URLs to try if the primary request fails
26 | 				"http://backup1",
27 | 				"https://reqres.in/api/users",
28 | 				"https://httpbin.org/post",
29 | 			},
30 | 			EnableLog: true, // Enables logging for debugging retries
31 | 		}),
32 | 		// Sets custom headers for the request
33 | 		client.WithHeaders(map[string]string{
34 | 			"Authorization": "Bearer token", // Adds an authentication token
35 | 		}),
36 | 	)
37 | 
38 | 	// Perform the POST request
39 | 	resp, err := cC.Post("https://httpbin_error.org/post", map[string]string{
40 | 		"name":  "Jefferson",
41 | 		"email": "jeff@example.com",
42 | 	})
43 | 	if err != nil {
44 | 		log.Fatal("Error making POST request:", err)
45 | 	}
46 | 
47 | 	// Print the response body and status code
48 | 	fmt.Println("POST Response Status:", resp.StatusCode)
49 | 	fmt.Println("POST Response Body:", string(resp.Body))
50 | 
51 | }
52 | 


--------------------------------------------------------------------------------
/example/quick.http.client/retry.post/retry.post.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"log"
 6 | 	"time"
 7 | 
 8 | 	"github.com/jeffotoni/quick/http/client"
 9 | )
10 | 
11 | // Example of creating an HTTP client using a fluent and modular approach.
12 | // This allows fine-grained control over HTTP settings without requiring a full config struct.
13 | //
14 | // - WithRetry: Enables automatic retries for specific HTTP status codes (500, 502, 503, 504)
15 | func main() {
16 | 	// Retry Delay Format Support
17 | 	//
18 | 	// The retry delay parameter supports various formats for flexibility in defining the wait time between retries.
19 | 	// Additionally, it allows enabling exponential backoff by appending "-bex".
20 | 	//
21 | 	// Supported formats:
22 | 	//
23 | 	// - "2mil"      => 2 milliseconds
24 | 	// - "2s"        => 2 seconds
25 | 	// - "2min"      => 2 minutes
26 | 	//
27 | 	// Example Usage:
28 | 	//
29 | 	// client.WithRetry(
30 | 	//
31 | 	//	client.RetryConfig{
32 | 	//		MaxRetries: 2,
33 | 	//		Delay:      1 * time.Second,
34 | 	//		UseBackoff: true,
35 | 	//		Statuses:   []int{500},
36 | 	//		EnableLog:  true,
37 | 	//	}),
38 | 	//
39 | 	// This configuration will retry up to 3 times with an exponential backoff starting at 2 seconds,
40 | 	// and will only retry if the response status is 500, 502, 503, or 504.
41 | 
42 | 	cClient := client.New(
43 | 		client.WithRetry(
44 | 			client.RetryConfig{
45 | 				MaxRetries: 2,
46 | 				Delay:      1 * time.Second,
47 | 				UseBackoff: true,
48 | 				Statuses:   []int{500},
49 | 				FailoverURLs: []string{
50 | 					"http://backup1",
51 | 					"https://reqres.in/api/users",
52 | 					"https://httpbin.org/post"},
53 | 				EnableLog: true,
54 | 			}),
55 | 	)
56 | 
57 | 	// Perform the POST request
58 | 	resp, err := cClient.Post("https://httpbin_error.org/post", map[string]string{
59 | 		"name":  "Jefferson",
60 | 		"email": "jeff@example.com",
61 | 	})
62 | 	if err != nil {
63 | 		log.Fatal("Error making POST request:", err)
64 | 	}
65 | 
66 | 	// Print the response body and status code
67 | 	fmt.Println("POST Response Status:", resp.StatusCode)
68 | 	fmt.Println("POST Response Body:", string(resp.Body))
69 | }
70 | 


--------------------------------------------------------------------------------
/example/quick.http.client/servers/server1/main.go:
--------------------------------------------------------------------------------
 1 | //go:build !exclude_test
 2 | 
 3 | package main
 4 | 
 5 | import (
 6 | 	"encoding/json"
 7 | 	"net/http"
 8 | 
 9 | 	"github.com/jeffotoni/quick"
10 | )
11 | 
12 | // Middleware that processes the form before passing it to the next handler
13 | func postform(next http.Handler) http.Handler {
14 | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15 | 		// Ensures that the request is of type POST
16 | 		if r.Method != http.MethodPost {
17 | 			http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
18 | 			return
19 | 		}
20 | 
21 | 		// Parse the form data
22 | 		err := r.ParseForm()
23 | 		if err != nil {
24 | 			http.Error(w, "Error parsing form", http.StatusBadRequest)
25 | 			return
26 | 		}
27 | 
28 | 		// Create a map to store the received data
29 | 		formData := make(map[string]string)
30 | 		for key, values := range r.Form {
31 | 			formData[key] = values[0] // Get the first value for each key
32 | 		}
33 | 
34 | 		// Convert to JSON to respond to the client
35 | 		w.Header().Set("Content-Type", "application/json")
36 | 		w.WriteHeader(http.StatusOK)
37 | 		json.NewEncoder(w).Encode(map[string]any{
38 | 			"message": "Form received successfully",
39 | 			"data":    formData,
40 | 		})
41 | 
42 | 		// Pass the request to the next handler (if any)
43 | 		if next != nil {
44 | 			next.ServeHTTP(w, r)
45 | 		}
46 | 	})
47 | }
48 | 
49 | func adaptHandler(h http.Handler) quick.HandleFunc {
50 | 	return func(c *quick.Ctx) error {
51 | 		h.ServeHTTP(c.Response, c.Request)
52 | 		return nil
53 | 	}
54 | }
55 | 
56 | func main() {
57 | 
58 | 	h := func(c *quick.Ctx) error {
59 | 		c.Set("Content-Type", "application/json")
60 | 
61 | 		if c.Body() == nil {
62 | 			return c.Status(200).Send([]byte(`{"data":"quick is awesome!"}`))
63 | 		}
64 | 		return c.Status(200).Send(c.Body())
65 | 	}
66 | 
67 | 	q := quick.New()
68 | 
69 | 	q.Get("/v1/user/:id", h)
70 | 	q.Post("/v1/user", h)
71 | 	q.Put("/v1/user/:id", h)
72 | 	q.Delete("/v1/user/:id", h)
73 | 
74 | 	q.Post("/postform", adaptHandler(postform(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
75 | 		return
76 | 	}))))
77 | 
78 | 	q.Listen("0.0.0.0:3000")
79 | }
80 | 


--------------------------------------------------------------------------------
/example/quick.http.client/servers/server2/main.go:
--------------------------------------------------------------------------------
 1 | //go:build !exclude_test
 2 | 
 3 | package main
 4 | 
 5 | import (
 6 | 	"github.com/jeffotoni/quick"
 7 | )
 8 | 
 9 | func main() {
10 | 
11 | 	h := func(c *quick.Ctx) error {
12 | 		c.Set("Content-Type", "application/json")
13 | 
14 | 		if c.Body() == nil {
15 | 			return c.Status(200).Send([]byte(`{"data":"quick is awesome!"}`))
16 | 		}
17 | 		return c.Status(200).Send(c.Body())
18 | 	}
19 | 
20 | 	forms := func(c *quick.Ctx) error {
21 | 
22 | 		// Get individual values from the form
23 | 		name := c.FormValue("name")
24 | 		email := c.FormValue("email")
25 | 
26 | 		// Get all values from the form
27 | 		allValues := c.FormValues()
28 | 
29 | 		// Respond with the received data
30 | 		return c.Status(200).JSON(map[string]any{
31 | 			"message": "Form received",
32 | 			"name":    name,
33 | 			"email":   email,
34 | 			"data":    allValues,
35 | 		})
36 | 	}
37 | 
38 | 	q := quick.New()
39 | 
40 | 	q.Get("/v1/user/:id", h)
41 | 	q.Post("/v1/user", h)
42 | 	q.Put("/v1/user/:id", h)
43 | 	q.Delete("/v1/user/:id", h)
44 | 	q.Post("/postform", forms)
45 | 
46 | 	// show run server
47 | 	// export PRINT_SERVER=true
48 | 	q.Listen("0.0.0.0:3000")
49 | }
50 | 


--------------------------------------------------------------------------------
/example/quick.http2/multiplex/cert.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIIFCTCCAvGgAwIBAgIUaljMNxSSDDEX8q+9pIFm01ytrP0wDQYJKoZIhvcNAQEL
 3 | BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDMwOTAxMTQyN1oXDTI2MDMw
 4 | OTAxMTQyN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
 5 | AAOCAg8AMIICCgKCAgEAsuZ7rNu2TGV1q4kE4cBC+1xAwqUjcm95Dcx98reQPq93
 6 | guimVeCzYGdPG8IDOtTaqxhKQKuTwmVSKo1Tapo+64G847xyDDPdkPHs+kTULprz
 7 | a0YT3tHFNr8oc4OveiWA1lryVtDuS6UZW4Dwj3c+vO+6BR5JjzJld7ayxjekTnq0
 8 | Zj4yG9Cuol3A6OAwH6sfX3pUSN7rUkj9QU2t9FJiHVbw/n8rw45zL3lZWo9i/Unq
 9 | zQA3pP8xo8Bu5k4T7yDYvuypUEIfJpV4ZwqXPqR2RsG7101nQzqEaiots0dxHQs/
10 | 39F47k8EfeHYlI4cIjCgqWzwt1IX5eWx038pedGqnVFBKqOCIxU4prOhVf/OW7l3
11 | rbLvi10YhaiPrvbujwx/FYy7an4//5SPaUywC7Oa9FU9SZl8H1P7ucZMA6sja4iO
12 | oDLrhKGDb/5a5dF2ztAdM/5LJk13IpUmTl28/MW4ImRJSPuD2t2jkdifabucpFHi
13 | 2byCHztz4fZ0D9fxdEuIES6CNttTxC1cis5FfbVFrzpMogP1tbVWaI5RlNjSJWbz
14 | LF0u7L0GeO13JLiaxRoXNFVg50qoRnfpuPLn1odNnZYrJHtMZ9bGR3/FhUqgYijZ
15 | zT+nCpknIZht793Kmgad0PKVwYPn4eDqjRAbixU+Q50UNU9H3YMabD3I3B1eE18C
16 | AwEAAaNTMFEwHQYDVR0OBBYEFA5Ova+9FGu+Rk+87RzAsZZS6/KJMB8GA1UdIwQY
17 | MBaAFA5Ova+9FGu+Rk+87RzAsZZS6/KJMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
18 | hvcNAQELBQADggIBAJIZpojnXE849w/Wglc7gCO5uuGhKrgTFlaejhlmr/6ZxcMW
19 | zJmvVYlrnzA6hDUdE7PS/u8T8yBU5d7PkXpRtWY9rFK1nPaORPK/pG//1KuuAihp
20 | EoUzHwA24NhQKmkMmrPttq9G2ZXVIdZ1dhSOoC9nTmBjNRRhvuts7Kqu1l0Pn28F
21 | dAYmeSVZvtyVmpaDZzhxU8sf7usnpEv6sDj6+mBGtoNSWjPrZDgxZnjagWLEue8N
22 | 9U/M9WmdBqK5ikjiEK6d7Hmrrva52Dz7Y56k63zL3kYniZWfAeHfMd37OLp1mZzq
23 | 3qcVyw4jtsME1A+WYIE5yxFYqNGNzT8EI9NoflZqkHHU1/ihFC7NsboeawKwNuFQ
24 | sohNxp81ytETZzybsuNox2kcBQwzVbgj+xNY561QKIyboNG69jAiPGGHe2zCW7zF
25 | 9h4+owIRAN+bf5TGsnJr+D2Li0viTHUiFG5JXTlolwPCk/yWFXS54GUhZkKARGxl
26 | uvu3lq7yHDL4nFHADdxrLBD25FgDzQEAfDFrRpaa6DyzwTioM+RNKHlJgGV8AcfK
27 | w3mELs5XmnMpNhIuymbccxgZgD5jdm5h5l0+k2JxsHDyQ91VzIUm2TnEx3ZnWEHZ
28 | DD9VZmHCA34GjFD1NWOJFiRzYRBZ3xM0trcvFPAuhhNLWaiboj/UG2JUocBR
29 | -----END CERTIFICATE-----
30 | 


--------------------------------------------------------------------------------
/example/quick.http2/multiplex/k6.script.get.http.js:
--------------------------------------------------------------------------------
 1 | import http from 'k6/http';
 2 | import { check } from 'k6';
 3 | 
 4 | export let options = {
 5 |     stages: [
 6 |         { duration: '10s', target: 500 },
 7 |         { duration: '5s', target: 500 }, 
 8 |         { duration: '2s', target: 0 },   
 9 |     ],
10 | };
11 | 
12 | export default function () {
13 | 
14 |      let randomId = Math.floor(Math.random() * 1000); 
15 |     let url = `http://localhost:8080/v1/user/${randomId}`;
16 | 
17 |      const params = {
18 |         headers: {
19 |             'Content-Type': 'application/json',
20 |         },
21 |     };
22 | 
23 |     let res = http.get(url, params, {
24 |         timeout: '5s', // 5 second timeout
25 |     });
26 | 
27 |     check(res, {
28 |         'status is 200': (r) => r.status === 200,
29 |         'no errors': (r) => !r.error,
30 |     });
31 | }


--------------------------------------------------------------------------------
/example/quick.http2/multiplex/k6.script.get.http2.js:
--------------------------------------------------------------------------------
 1 | import http from 'k6/http';
 2 | import { check, sleep } from 'k6';
 3 | 
 4 | export const options = {
 5 |     stages: [
 6 |         { duration: '10s', target: 250 }, // Scales to 50 users in 30 seconds
 7 |         //{ duration: '20s', target: 1000 }, // Keeps 100 users for 1 minute
 8 |         { duration: '15s', target: 0 },  // Reduces to 0 users in 30 seconds
 9 |     ],
10 |     http2: true,
11 |     noConnectionReuse: false, // Reuse HTTP/2 connections
12 |     insecureSkipTLSVerify: true, // Skip TLS certificate verification
13 |     batchPerHost: 100,
14 | };
15 | 
16 | export default function () {
17 |     let randomId = Math.floor(Math.random() * 1000); // Generates an ID between 0 and 999
18 |     let url = `https://localhost:443/v1/user/${randomId}`;
19 | 
20 |     const params = {
21 |         headers: {
22 |             'Content-Type': 'application/json',
23 |         },
24 |     };
25 | 
26 |     let res = http.get(url, params, {
27 |         timeout: '5s', // 5 second timeout
28 |     });
29 | 
30 |     // Check if the request was successful
31 |     check(res, {
32 |         'status is 200': (r) => r.status === 200,
33 |         'protocol is HTTP/2': (r) => r.proto === 'HTTP/2.0',
34 |         'no errors': (r) => !r.error,
35 |     });
36 | 
37 |     // Logs for debugging
38 |     // console.log(`debug..: ${res.body}`)
39 |     // console.log(`Requesting user with ID: ${randomId}`);
40 |     // console.log(`Response status: ${res.status}, Protocol: ${res.proto}`);
41 |     // sleep(1); // 1 second interval between requests
42 | }


--------------------------------------------------------------------------------
/example/quick.http2/multiplex/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"log"
 6 | 
 7 | 	"github.com/jeffotoni/quick"
 8 | )
 9 | 
10 | func main() {
11 | 	q := quick.New(quick.Config{
12 | 		// GCPercent:         500, // GC more aggressive for high load
13 | 		// ReadTimeout:       10 * time.Second,
14 | 		// WriteTimeout:      10 * time.Second,
15 | 		// IdleTimeout:       30 * time.Second,
16 | 		// ReadHeaderTimeout: 2 * time.Second,
17 | 		// MaxHeaderBytes:    1024 * 1024 * 20, // 20MB
18 | 
19 | 		// cors
20 | 		// CorsConfig: &quick.CorsConfig{
21 | 		// 	Enabled: true,
22 | 		// 	Options: map[string]string{
23 | 		// 		"Access-Control-Allow-Origin":  "*",
24 | 		// 		"Access-Control-Allow-Methods": "GET,POST",
25 | 		// 	},
26 | 		// },
27 | 	})
28 | 
29 | 	q.Get("/v1/user/:id", func(c *quick.Ctx) error {
30 | 		c.Set("Content-Type", "application/json")
31 | 		// time.Sleep(2 * time.Second)
32 | 		// fmt.Println("log:", c.Request.URL.Path)
33 | 		myuser := struct {
34 | 			Name string `json:"name"`
35 | 		}{
36 | 			Name: c.Param("id"),
37 | 		}
38 | 		return c.Status(200).JSON(myuser)
39 | 	})
40 | 
41 | 	// Start the HTTPS server with HTTP/2 enabled
42 | 	fmt.Println("Server run https://localhost:443...")
43 | 
44 | 	// Start the HTTPS server with TLS encryption
45 | 	// - The server will listen on port 443
46 | 	// - cert.pem: SSL/TLS certificate file
47 | 	// - key.pem: Private key file for SSL/TLS encryption
48 | 	// - http2: [true or false]
49 | 	if err := q.ListenTLS(":443", "cert.pem", "key.pem", false); err != nil {
50 | 		log.Fatal(err)
51 | 	}
52 | 
53 | }
54 | 


--------------------------------------------------------------------------------
/example/quick.listen.tls/cert.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIID7zCCAtegAwIBAgIUDKcclLVnkJOgvfPfMokMCyxyVvgwDQYJKoZIhvcNAQEL
 3 | BQAwgYYxCzAJBgNVBAYTAkJSMRUwEwYDVQQIDAxNSU5BUyBHRVJBSVMxETAPBgNV
 4 | BAcMCENPTlRBR0VNMQ8wDQYDVQQKDAZHT09HTEUxCzAJBgNVBAsMAlRJMRIwEAYD
 5 | VQQDDAlsb2NhbGhvc3QxGzAZBgkqhkiG9w0BCQEWDG15QGdtYWlsLmNvbTAeFw0y
 6 | NTAzMDcxOTA5MzNaFw0yNjAzMDcxOTA5MzNaMIGGMQswCQYDVQQGEwJCUjEVMBMG
 7 | A1UECAwMTUlOQVMgR0VSQUlTMREwDwYDVQQHDAhDT05UQUdFTTEPMA0GA1UECgwG
 8 | R09PR0xFMQswCQYDVQQLDAJUSTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZI
 9 | hvcNAQkBFgxteUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
10 | AoIBAQC9h55aW21BjQNStK563Jid+AMeCC9d8du0twYKTUaSzAcAb82VHnSxlV9D
11 | MZpQSRa59+YXIvJZ7KS5/LfHSV9nwPyIZ6bvpvX6Js1kXPcDn1fkpVC9TVSSV7ec
12 | tIVnfnig67cC05OgVlK4lEkuDPPqcerpE5nflMI3PZRV8FlSPonDjG4QSIoScklp
13 | Z8xvqnPxz42hyCGCRH04YjHyQ6HU7WYAjAtXmrOJnbkYgfz37L2D1o+mk+hM0izO
14 | KOY5bmxTMR0zWy99dXLhFjp/ibw7jvTU8dWFtpwdLBAwQ6itoM2ADZ+5eouNPieb
15 | kP33XWtUK59RSD9eK1MZB18vX4efAgMBAAGjUzBRMB0GA1UdDgQWBBRXKQNPg1pk
16 | y6s90tC2YWterTtxlDAfBgNVHSMEGDAWgBRXKQNPg1pky6s90tC2YWterTtxlDAP
17 | BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJN7XKA+py3knwnFDQ
18 | WpwHx4QbIlZ+ggV3r+Lz/IF710PiGUIOFFg6tYpCmIdJTv5MekqJlvX42dy1MsUS
19 | YhU1HiKIRcBXEoxAqEMMUD7yWwQJeBg8aW8GH+37v8VUSuSG31c7MHr0OY2bhq6K
20 | 9joVagTG2GhwBatC0soUYO7ZbF+jo/ExhFBfPZByY0Gt2rQYgkkHMq90l5P3ZZRK
21 | sK9R4hi6MJ5j7PR0SVXYxuhm+Crf2aI2pZJyilq8Vy8/b2VIAhkI7NB22rHZjViH
22 | sSPZM5ShSiN29JT8vxj3IGPQzkXtmMeluLbZcWkIxlDANiDeKyOvL84EPPNuzb5e
23 | YXSg
24 | -----END CERTIFICATE-----
25 | 


--------------------------------------------------------------------------------
/example/quick.listen.tls/key.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN PRIVATE KEY-----
 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9h55aW21BjQNS
 3 | tK563Jid+AMeCC9d8du0twYKTUaSzAcAb82VHnSxlV9DMZpQSRa59+YXIvJZ7KS5
 4 | /LfHSV9nwPyIZ6bvpvX6Js1kXPcDn1fkpVC9TVSSV7ectIVnfnig67cC05OgVlK4
 5 | lEkuDPPqcerpE5nflMI3PZRV8FlSPonDjG4QSIoScklpZ8xvqnPxz42hyCGCRH04
 6 | YjHyQ6HU7WYAjAtXmrOJnbkYgfz37L2D1o+mk+hM0izOKOY5bmxTMR0zWy99dXLh
 7 | Fjp/ibw7jvTU8dWFtpwdLBAwQ6itoM2ADZ+5eouNPiebkP33XWtUK59RSD9eK1MZ
 8 | B18vX4efAgMBAAECggEAF4L1b/wbh6A4o0/qvS8Ud7RGnrkhNyRDvx1dUHXZpWhO
 9 | Dg3QkQkgLM9869cBb6hBe3x3PLOHIQSsQq5JFh3SCgr0yO8i1GP/67JYlHS75+Ui
10 | gCjJ5VHsheL/0h7K1dXonP3gzCb3D1LmHCO5GE9fCtxq6dMWqZfIqBWpiyevzPXh
11 | 905F5kP3ypFqcpHy94jTI+p4O2PAZJi6UpzHrwmA0JMsRU4DLhvqMMLou7riLblH
12 | 7tarpVxspaA7hPZHocQMjSX1EtkG2DHgmGHfMIv5E7eAHuO1X4PxHi/JkIuAFhlm
13 | CRY6vFp4v6kfWoYpsQstatdbj78Zd5xL63UJHKlcwQKBgQD8C0n1JdEpLnIF1L5e
14 | 7jewGKBGyJzDFnF10IDQSGdVzlDad7/Y04dI6N0gP3n7TysB0ISyss6uMATAmU99
15 | 7kNOkhnta7lpfXRJgWHpxbKs5fZCaUYZOByPZS1wwws0nBHipNUYulePwUEXP/Mu
16 | wJzKp3ttO8XI5kk1l3sJQc/E7wKBgQDAgSXHrsTGZF9To+PCObl3bzUQ4bwYkbNy
17 | 3VwtNbG6vH0HtZCrTSAw+6ftFyNneSnYKYIGY826hTCjRfw2+0LZMq80impdGv+w
18 | MtmsiF3nmxq/skXk4m/l++r7XK/mcl6cLuiNdwGSPmvOz3IYy6oxhS1xh5o6zpwJ
19 | fLtcfLFIUQKBgQCwFcrzpBn+pV4vyLO/aropIutGyH0Mpq9Z2fNEkHDxN5SxLJwx
20 | hYasX1OtZet4sZSgTeeHWPigJRgM3o70sfL//35xqqL8mhMWD3gydFYiCP8E4ruA
21 | fv0lCVC3yOwZMzgjmXjvL7Bg1Gj/L0RcU2C3DE9kvy+s7YYUFOJ2Uy4urQKBgEFc
22 | mGNQZ+ektOlFQFP2HY7751ywCJme/vsFRgLjtFDfJuSzNUe0jDWVfNL3hEHh1d9b
23 | WJaq1HN8PaNyY8yXS39Lwklopeyu+hntahM2yguVkmN8OFcauzuvaX2nXuIz+wO0
24 | uakuuw3Yu+ogbXEV1deyjFd731YQSNK/0y33+InBAoGBAMmZX/dpeZ4y9pO7loZj
25 | SmAQCgax9e6JI+MqKcKGpqGp3SiYGqmYUq7cQ3N+N1yoWHWV2jVuhtFBmFOJMXaC
26 | 9ETIrno8qXZNkSZUtznEOebu60bhIs147ATBs0eTURfiWbqV7YRp96EDx7JBQIAm
27 | bAPC4GAb/aB7fXm9pMJGN5+Z
28 | -----END PRIVATE KEY-----
29 | 


--------------------------------------------------------------------------------
/example/quick.listen.tls/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/jeffotoni/quick"
 7 | )
 8 | 
 9 | // $ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
10 | //
11 | // $ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
12 | func main() {
13 | 	// Initialize Quick instance
14 | 	q := quick.New()
15 | 
16 | 	// Print a message indicating that the server is starting on port 443
17 | 	fmt.Println("Run Server port:443")
18 | 
19 | 	// Start the HTTPS server with TLS encryption
20 | 	// - The server will listen on port 443
21 | 	// - cert.pem: SSL/TLS certificate file
22 | 	// - key.pem: Private key file for SSL/TLS encryption
23 | 	// - http2 [true or false]
24 | 	err := q.ListenTLS(":443", "cert.pem", "key.pem", false)
25 | 	if err != nil {
26 | 		// Log an error message if the server fails to start
27 | 		fmt.Printf("Error when trying to connect with TLS: %v\n", err)
28 | 	}
29 | }
30 | 
31 | //**Note for Linux Users**
32 | //By default, this example runs on **port 443**, which is a privileged port (below 1024).
33 | //On **Linux**, running on this port requires **superuser privileges**.
34 | 
35 | //To run this example on Linux, use:
36 | //sudo go run main.go
37 | 


--------------------------------------------------------------------------------
/example/quick.new/full/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"net/http"
 6 | 
 7 | 	"github.com/jeffotoni/quick"
 8 | )
 9 | 
10 | // This example demonstrates advanced usage of Quick with custom configurations and middlewares
11 | func main() {
12 | 	// Initialize Quick with a custom configuration
13 | 	config := quick.Config{
14 | 		RouteCapacity: 500, // Custom route capacity
15 | 	}
16 | 	q := quick.New(config)
17 | 
18 | 	// Middleware example: Logging requests
19 | 	q.Use(func(h http.Handler) http.Handler {
20 | 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21 | 			fmt.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
22 | 			h.ServeHTTP(w, r)
23 | 		})
24 | 	})
25 | 
26 | 	// Define multiple routes
27 | 	q.Get("/", func(c *quick.Ctx) error {
28 | 		return c.Status(200).String("Welcome to Quick!")
29 | 	})
30 | 
31 | 	q.Post("/data", func(c *quick.Ctx) error {
32 | 		return c.Status(201).String("Data received!")
33 | 	})
34 | 
35 | 	q.Put("/update", func(c *quick.Ctx) error {
36 | 		return c.Status(200).String("Data updated!")
37 | 	})
38 | 
39 | 	q.Delete("/delete", func(c *quick.Ctx) error {
40 | 		return c.Status(204).String("") // No content response
41 | 	})
42 | 
43 | 	// Start the server
44 | 	fmt.Println("Server running at http://localhost:8080")
45 | 	q.Listen(":8080")
46 | }
47 | 
48 | // $ curl -X GET http://localhost:8080/
49 | 
50 | // $ curl -X POST http://localhost:8080/data
51 | 
52 | // $ curl -X PUT http://localhost:8080/update
53 | 
54 | // $ curl -X DELETE http://localhost:8080/delete
55 | 


--------------------------------------------------------------------------------
/example/quick.new/simple/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | )
 6 | 
 7 | // This example demonstrates simple usage of Quick
 8 | func main() {
 9 | 	// Initialize a new Quick instance
10 | 	q := quick.New()
11 | 
12 | 	// Define a simple GET route at the root path
13 | 	q.Get("/", func(c *quick.Ctx) error {
14 | 		// Set response header to indicate plain text response
15 | 		c.Set("Content-Type", "text/plain")
16 | 
17 | 		// Return a 200 OK response with a message
18 | 		return c.Status(200).String("Quick in action!")
19 | 	})
20 | 
21 | 	// Start the Quick server on port 8080
22 | 	q.Listen(":8080")
23 | }
24 | 
25 | // $ curl -X GET http://localhost:8080/ -H "Content-Type: text/plain"
26 | 


--------------------------------------------------------------------------------
/example/quick.newerror/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import "github.com/jeffotoni/quick"
 4 | 
 5 | func main() {
 6 | 
 7 | 	q := quick.New()
 8 | 
 9 | 	q.Get("/", func(c *quick.Ctx) error {
10 | 		return quick.NewError(782, "Custom error message")
11 | 	})
12 | 
13 | 	q.Listen(":8080")
14 | }
15 | 


--------------------------------------------------------------------------------
/example/quick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/quick.png


--------------------------------------------------------------------------------
/example/quick.post/README.md:
--------------------------------------------------------------------------------
 1 | # 📌 POST - Quick Framework 
 2 | 
 3 | This example demonstrates how to create a POST route in the Quick Framework to receive and process JSON data at the `/v1/user` endpoint.
 4 | 
 5 | ### 📜 Code Implementation
 6 | 
 7 | ```go
 8 | package main
 9 | 
10 | import "github.com/jeffotoni/quick"
11 | 
12 | // Struct representing a user model
13 | type My struct {
14 | 	Name string `json:"name"` // User's name
15 | 	Year int    `json:"year"` // User's birth year
16 | }
17 | 
18 | func main() {
19 | 	q := quick.New() // Initialize Quick framework
20 | 
21 | 	// Define a POST route at /v1/user
22 | 	q.Post("/v1/user", func(c *quick.Ctx) error {
23 | 		var my My // Create a variable to store incoming user data
24 | 
25 | 		// Parse the request body into the struct
26 | 		err := c.BodyParser(&my)
27 | 		if err != nil {
28 | 			// If parsing fails, return a 400 Bad Request response
29 | 			return c.Status(400).SendString(err.Error())
30 | 		}
31 | 
32 | 		// Return the parsed JSON data as a response with 200 OK
33 | 		return c.Status(200).JSON(&my)
34 | 
35 | 		// Alternative:
36 | 		// return c.Status(200).String(c.BodyString()) 
37 | 		// Return raw request body as a string
38 | 	})
39 | 
40 | 	// Start the server and listen on port 8080
41 | 	q.Listen("0.0.0.0:8080")
42 | }
43 | 
44 | ```
45 | #### 📌 Testing with cURL
46 | 
47 | ##### 🔹 Create a User (POST Request):
48 | 
49 | ```bash
50 | $ curl --location 'http://localhost:8080/v1/user' \
51 | --header 'Content-Type: application/json/' \
52 | --data '{"name":"crow3442","year":2005}'
53 | ```
54 | 
55 | ---
56 | 
57 | #### 📌 What I included in this README
58 | - ✅ Overview of the POST endpoint in Quick Framework
59 | - ✅ Go implementation with JSON parsing using BodyParser
60 | - ✅ POST route to create a new user dynamically
61 | - ✅ Handling of success (200 OK) and error (400 Bad Request) responses
62 | - ✅ cURL example for valid and invalid requests
63 | 
64 | 
65 | Now you can **complete with your specific examples** where I left the spaces
66 | 
67 | ##### 🚀 **If you need adjustments or improvements, just let me know!** 😃🔥


--------------------------------------------------------------------------------
/example/quick.post/post.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import "github.com/jeffotoni/quick"
 4 | 
 5 | // Struct representing a user model
 6 | type My struct {
 7 | 	Name string `json:"name"` // User's name
 8 | 	Year int    `json:"year"` // User's birth year
 9 | }
10 | 
11 | func main() {
12 | 	q := quick.New() // Initialize Quick framework
13 | 
14 | 	// Define a POST route at /v1/user
15 | 	q.Post("/v1/user", func(c *quick.Ctx) error {
16 | 		var my My // Create a variable to store incoming user data
17 | 
18 | 		// Parse the request body into the struct
19 | 		err := c.BodyParser(&my)
20 | 		if err != nil {
21 | 			// If parsing fails, return a 400 Bad Request response
22 | 			return c.Status(400).SendString(err.Error())
23 | 		}
24 | 
25 | 		// Return the parsed JSON data as a response with 200 OK
26 | 		return c.Status(200).JSON(quick.M{
27 | 			"name": "jeff",
28 | 		})
29 | 
30 | 		// Alternative:
31 | 		// return c.Status(200).String(c.BodyString())
32 | 		// Return raw request body as a string
33 | 	})
34 | 
35 | 	// Start the server and listen on port 8080
36 | 	q.Listen("0.0.0.0:8080")
37 | }
38 | 


--------------------------------------------------------------------------------
/example/quick.put/put.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import "github.com/jeffotoni/quick"
 4 | 
 5 | func main() {
 6 | 	q := quick.New() // Initialize Quick framework
 7 | 
 8 | 	// PUT route to update a user by ID
 9 | 	q.Put("/users/:id", func(c *quick.Ctx) error {
10 | 		userID := c.Param("id") // Retrieve the user ID from the URL parameter
11 | 		// Logic to update user data would go here
12 | 		return c.Status(200).SendString("User " + userID + " updated successfully!")
13 | 	})
14 | 
15 | 	// PUT route to update a specific type by ID
16 | 	q.Put("/tipos/:id", func(c *quick.Ctx) error {
17 | 		tiposID := c.Param("id") // Retrieve the type ID from the URL parameter
18 | 		// Logic to update the type would go here
19 | 		return c.Status(200).SendString("User " + tiposID + " type updated successfully!")
20 | 	})
21 | 
22 | 	// Start the server and listen on port 8080
23 | 	q.Listen(":8080")
24 | }
25 | 


--------------------------------------------------------------------------------
/example/quick.regex/README.md:
--------------------------------------------------------------------------------
 1 | # 📌 Using Regex in Routes - Quick Framework ![Quick Logo](/quick.png)
 2 | 
 3 | `Regex` (or "Regular Expressions") is a powerful technique used in programming to match and manipulate text patterns.
 4 | 
 5 | The Quick Framework supports dynamic routes but does not support inline regex definitions like `{id:[0-9]+}` in route parameters. Instead, Quick uses colon-prefixed `(:param)` parameters for dynamic route matching.
 6 | 
 7 | ### 📜 Code Implementation
 8 | 
 9 | ```go
10 | package main
11 | 
12 | import (
13 | 	"github.com/jeffotoni/quick"
14 | 	"github.com/jeffotoni/quick/middleware/msgid"
15 | )
16 | 
17 | func main() {
18 | 	q := quick.New()
19 | 
20 | 	// Adding middleware msgid
21 | 	q.Use(msgid.New())
22 | 
23 | 	// Corrected route using :id instead of {id:[0-9]+}
24 | 	q.Get("/v1/user/:id", func(c *quick.Ctx) error {
25 | 		c.Set("Content-Type", "application/json")
26 | 		return c.Status(200).String("Quick ação total!!!")
27 | 	})
28 | 
29 | 	q.Listen(":8080")
30 | }
31 | 
32 | ```
33 | #### 📌 Testing with cURL
34 | 
35 | ##### 🔹 Retrieve user details by ID:
36 | 
37 | ```go
38 | $ curl --location --request GET "http://localhost:8080/v1/user/123" \
39 | --header "Content-Type: application/json"
40 | ```
41 | ---
42 | 
43 | #### 📌 What I included in this README
44 | - ✅ Introduction to Regex and its usage in Quick Framework.
45 | - ✅ Explanation of Quick’s dynamic route handling.
46 | - ✅ Example of defining a dynamic route with :param.
47 | - ✅ cURL example to test dynamic route behavior.
48 | 
49 | ---
50 | 
51 | Now you can **complete with your specific examples** where I left the spaces
52 | 
53 | ##### 🚀 **If you need adjustments or improvements, just let me know!** 😃🔥


--------------------------------------------------------------------------------
/example/quick.regex/lower/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | )
 6 | 
 7 | func main() {
 8 | 	q := quick.New()
 9 | 
10 | 	// Route that accepts only lowercase slugs (words with lowercase letters)
11 | 	// - The {slug:[a-z]+} parameter ensures that the slug consists only of lowercase letters (a-z).
12 | 	// - If uppercase letters or numbers are included, the request will not match.
13 | 	q.Get("/profile/{slug:[a-z]+}", func(c *quick.Ctx) error {
14 | 		slug := c.Param("slug")
15 | 		return c.JSON(map[string]string{
16 | 			"message": "Profile found",
17 | 			"profile": slug,
18 | 		})
19 | 	})
20 | 
21 | 	// Start the server on port 8080
22 | 	q.Listen(":8080")
23 | }
24 | 
25 | // $curl --location 'http://localhost:8080/profile/golang'
26 | 


--------------------------------------------------------------------------------
/example/quick.regex/numeric/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | )
 6 | 
 7 | func main() {
 8 | 	q := quick.New()
 9 | 
10 | 	// Route that accepts only numeric IDs (using regex [0-9]+)
11 | 	q.Get("/users/{id:[0-9]+}", func(c *quick.Ctx) error {
12 | 		id := c.Param("id")
13 | 		return c.JSON(map[string]string{
14 | 			"message": "User found",
15 | 			"user_id": id,
16 | 		})
17 | 	})
18 | 
19 | 	// Start the server on port 8080
20 | 	q.Listen(":8080")
21 | }
22 | 
23 | //$ curl --location 'http://localhost:8080/users/123'
24 | 


--------------------------------------------------------------------------------
/example/quick.regex/version/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"github.com/jeffotoni/quick"
 5 | )
 6 | 
 7 | func main() {
 8 | 	q := quick.New()
 9 | 
10 | 	// Route that accepts an API version (v1, v2, etc.) and a numeric user ID
11 | 	// - The {version:v[0-9]+} parameter ensures that the version starts with "v" followed by digits.
12 | 	// - The {id:[0-9]+} parameter ensures that the user ID consists of only numbers.
13 | 	q.Get("/api/{version:v[0-9]+}/users/{id:[0-9]+}", func(c *quick.Ctx) error {
14 | 		version := c.Param("version")
15 | 		id := c.Param("id")
16 | 		return c.JSON(map[string]string{
17 | 			"message": "API Versioned User",
18 | 			"version": version,
19 | 			"user_id": id,
20 | 		})
21 | 	})
22 | 
23 | 	// Start the server on port 8080
24 | 	q.Listen(":8080")
25 | }
26 | 
27 | // $ curl --location 'http://localhost:8080/api/v1/users/456'
28 | 


--------------------------------------------------------------------------------
/example/quick.start/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import "github.com/jeffotoni/quick"
 4 | 
 5 | func main() {
 6 | 	q := quick.New()
 7 | 	q.Get("/v1/user",
 8 | 		func(c *quick.Ctx) error {
 9 | 			return c.Status(200).
10 | 				String("Quick! ❤️")
11 | 		})
12 | 	_ = q.Listen(":8080")
13 | }
14 | 
15 | // ----------
16 | 
17 | /**
18 | $ curl -i XGET localhost:8080/v1/user
19 | HTTP/1.1 200 OK
20 | Quick! ❤️
21 | */
22 | 


--------------------------------------------------------------------------------
/example/quick.use/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"log"
 5 | 	"net/http"
 6 | 	"time"
 7 | 
 8 | 	"github.com/jeffotoni/quick"
 9 | 	"github.com/jeffotoni/quick/rand"
10 | )
11 | 
12 | func main() {
13 | 
14 | 	q := quick.New()
15 | 
16 | 	// Match any request
17 | 	q.Use(func(c *quick.Ctx) error {
18 | 		return c.Next()
19 | 	})
20 | 
21 | 	// Trace-ID middleware
22 | 	q.Use(func(h http.Handler) http.Handler {
23 | 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24 | 			start := time.Now()
25 | 			traceID := rand.TraceID() // internal quick
26 | 
27 | 			// Inject the Trace-ID into the request and response header
28 | 			r.Header.Set("X-Trace-ID", traceID)
29 | 			w.Header().Set("X-Trace-ID", traceID)
30 | 
31 | 			// Print simple log with Trace-ID
32 | 			log.Printf("[Trace-ID: %s] -> Request start %s %s\n", traceID, r.Method, r.URL.Path)
33 | 			h.ServeHTTP(w, r)
34 | 			duration := time.Since(start)
35 | 
36 | 			log.Printf("[Trace-ID: %s] <- End of request duration:[(%v)]\n", traceID, duration)
37 | 		})
38 | 	})
39 | 
40 | 	q.Get("/v1/user/:name", func(c *quick.Ctx) error {
41 | 		name := c.Param("name")
42 | 		c.Set("Content-Type", "application/json")
43 | 		return c.Status(200).JSON(quick.M{
44 | 			"msg": name,
45 | 		})
46 | 	})
47 | 
48 | 	// Start server
49 | 	q.Listen("0.0.0.0:8080")
50 | }
51 | 
52 | // curl -i http://localhost:8080//v1/user/artur
53 | 


--------------------------------------------------------------------------------
/example/quick_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/quick_logo.png


--------------------------------------------------------------------------------
/example/site/docs/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 | 
3 | 


--------------------------------------------------------------------------------
/example/site/docs/middleware/CORS.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | 
 3 | //SIGNATURE func New(config Config) func(next http.Handler) http.Handler
 4 | 
 5 | //import (
 6 | // "fmt"
 7 | // "log"
 8 | // "github.com/jeffotoni/quick"
 9 | // "github.com/jeffotoni/quick/middleware/cors"
10 | // )
11 | 
12 | 
13 | //func main() {
14 | 	// Create a new Quick instance
15 | // 	app := quick.New()
16 | 
17 | 	// Apply CORS middleware to allow all origins, methods, and headers
18 | // 	app.Use(cors.New(cors.Config{
19 | // 		AllowedOrigins: []string{"*"}, // Allows requests from any origin
20 | // 		AllowedMethods: []string{"*"}, // Allows all HTTP methods (GET, POST, PUT, DELETE, etc.)
21 | // 		AllowedHeaders: []string{"*"}, // Allows all headers
22 | // 	}))
23 | 
24 | 	// Define a POST route for creating a user
25 | // 	app.Post("/v1/user", func(c *quick.Ctx) error {
26 | 		// Set response content type as JSON
27 | // 		c.Set("Content-Type", "application/json")
28 | 
29 | 		// Define a struct to hold incoming JSON data
30 | // 		type My struct {
31 | // 			Name string `json:"name"`
32 | // 			Year int    `json:"year"`
33 | // 		}
34 | 
35 | // 		var my My
36 | 
37 | 		// Parse the request body into the struct
38 | // 		err := c.BodyParser(&my)
39 | // 		fmt.Println("byte:", c.Body()) // Print raw request body
40 | 
41 | // 		if err != nil {
42 | 			// Return a 400 Bad Request if parsing fails
43 | // 			return c.Status(400).SendString(err.Error())
44 | // 		}
45 | 
46 | 		// Print the request body as a string
47 | // 		fmt.Println("String:", c.BodyString())
48 | 
49 |  		// Return the parsed JSON data with a 200 OK status
50 | // 		return c.Status(200).JSON(&my)
51 | // 	})
52 | 
53 |  	// Start the server on port 8080
54 | // 	log.Fatal(app.Listen("0.0.0.0:8080"))
55 | // }


--------------------------------------------------------------------------------
/example/site/docs/middleware/Healthcheck.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | 
 3 | //Signature func New(opts Options) func(next http.Handler) http.Handler
 4 | 
 5 | // import (
 6 | // 	"github.com/jeffotoni/quick"
 7 | // 	"github.com/seuusuario/healthcheck"
 8 | // )
 9 | 
10 | // func main() {
11 | // 	q := quick.New()
12 | 
13 | // 	 Use Healthcheck middleware with default healthcheck endpoint
14 | // 	q.Use(healthcheck.New(
15 | // 		healthcheck.Options{
16 | // 			App: q,
17 | // 		},
18 | // 	))
19 | 
20 | // 	q.Get("/", func(c *quick.Ctx) error {
21 | // 		return c.Status(200).String("Home page")
22 | // 	})
23 | 
24 | // 	log.Fatalln(q.Listen(":8080"))
25 | // }


--------------------------------------------------------------------------------
/example/site/docs/middleware/basicauth.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | 
 3 | // SIGNATURE func(next http.Handler) http.Handler
 4 | 
 5 | //import (
 6 | // 	"log"
 7 | // 	"github.com/jeffotoni/quick"
 8 | // 	middleware "github.com/jeffotoni/quick/middleware/basicauth"
 9 | // )
10 | 
11 | //Var(
12 | // 	User     = os.Getenv("USER")
13 | // 	Password = os.Getenv("PASSWORD")
14 | // )
15 | 
16 | 
17 | // func main() {
18 | 
19 | 	// q := quick.New()
20 | 
21 | 	// Adding BasicAuth middleware
22 | 	// q.Use(middleware.BasicAuth(User, Password))
23 | 
24 | 	// Protected route
25 | 	// q.Get("/protected", func(c *quick.Ctx) error {
26 | 		// c.Set("Content-Type", "application/json")
27 | 		// return c.SendString("You have accessed a protected route!")
28 | 	// })
29 | 
30 | 	// Starting the server
31 | // log.Fatal(q.Listen("0.0.0.0:8080"))
32 | // }
33 | 
34 | //


--------------------------------------------------------------------------------
/example/site/docs/middleware/compress.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | //SIGNATURE func Gzip() func(next http.Handler) http.Handler
 3 | 
 4 | //import
 5 | // import (
 6 | // 	"log"
 7 | 
 8 | // 	"github.com/jeffotoni/quick"
 9 | // 	"github.com/jeffotoni/quick/middleware/compress"
10 | // )	
11 | 
12 | // func main() {
13 | // 	q := quick.New()
14 | 
15 | // 	// Enable GZIP compression
16 | // 	q.Use(compress.Gzip())
17 | 
18 | // 	// Define a compressed response route
19 | // 	q.Get("/v1/compress", func(c *quick.Ctx) error {
20 | // 		c.Set("Content-Type", "application/json")
21 | // 		c.Set("Accept-Encoding", "gzip")
22 | 
23 | // 		type response struct {
24 | // 			Msg     string              `json:"msg"`
25 | // 			Headers map[string][]string `json:"headers"`
26 | // 		}
27 | 
28 | // 		return c.Status(200).JSON(&response{
29 | // 			Msg:     "Quick ❤️",
30 | // 			Headers: c.Headers,
31 | // 		})
32 | // 	})
33 | 
34 | // 	log.Fatal(q.Listen("0.0.0.0:8080"))
35 | // }


--------------------------------------------------------------------------------
/example/site/docs/middleware/helmet.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | 
 3 | //Signature func Helmet() func(next http.Handler) http.Handler
 4 | 
 5 | // import (
 6 | // 	"github.com/jeffotoni/quick"
 7 | // 	"github.com/seuusuario/helmet"
 8 | 	
 9 | // )
10 | 
11 | // func main() {
12 | // 	q := quick.New()
13 | 
14 | // 	// Use Helmet middleware with default security headers
15 | // 	q.Use(helmet.Helmet())
16 | 
17 | // 	// Simple route to test headers
18 | // 	q.Get("/v1/user", func(c *quick.Ctx) error {
19 | 
20 | // 		// list all headers
21 | // 		headers := make(map[string]string)
22 | // 		for k, v := range c.Response.Header() {
23 | // 			if len(v) > 0 {
24 | // 				headers[k] = v[0]
25 | // 			}
26 | // 		}
27 | // 		return c.Status(200).JSONIN(headers)
28 | // 	})
29 | 
30 | // 	q.Listen("0.0.0.0:8080")
31 | // }


--------------------------------------------------------------------------------
/example/site/docs/middleware/limiter.go:
--------------------------------------------------------------------------------
 1 | package docs
 2 | 
 3 | // func New(config Config) func(next http.Handler) http.Handler
 4 | 
 5 | // import (
 6 | // 	"time"
 7 | // 	"github.com/jeffotoni/quick"
 8 | // 	"github.com/jeffotoni/quick/middleware/limiter"
 9 | // )
10 | 
11 | 
12 | 
13 | // func main() {
14 | // 	q := quick.New()
15 | 
16 | // 	// Apply the rate limiter middleware
17 | // 	q.Use(limiter.New(limiter.Config{
18 | // 		// Maximum 10 requests allowed per IP
19 | // 		Max: 10,
20 | // 		// The limit resets after 5 seconds
21 | // 		Expiration: 5 * time.Second,
22 | // 		KeyGenerator: func(c *quick.Ctx) string {
23 | // 			// Uses the client's IP address as the key
24 | // 			return c.RemoteIP()
25 | // 		},
26 | // 		LimitReached: func(c *quick.Ctx) error {
27 | // 			c.Set("Content-Type", "application/json")
28 | // 			// The client should wait 10 seconds before retrying
29 | // 			c.Set("Retry-After", "10")
30 | // 			return c.Status(quick.StatusTooManyRequests).JSON(map[string]string{
31 | // 				"error":   "Too many requests",
32 | // 				"message": "You have exceeded the request limit. 
33 | // 				Please wait 1 second and try again.",
34 | // 				"retry_after": "10s",
35 | // 			})
36 | // 		},
37 | // 	}))
38 | 
39 | // 	// Define a simple GET route
40 | // 	q.Get("/", func(c *quick.Ctx) error {
41 | // 		return c.Status(200).JSON(map[string]string{"msg": "Quick in action ❤️!"})
42 | // 	})
43 | 
44 | // 	// Start the server on port 8080
45 | // 	q.Listen(":8080")
46 | // }
47 | 
48 | 


--------------------------------------------------------------------------------
/example/site/docs/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 |   "name": "docs",
3 |   "lockfileVersion": 3,
4 |   "requires": true,
5 |   "packages": {}
6 | }
7 | 


--------------------------------------------------------------------------------
/example/static/echo/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | // import (
 4 | // 	"fmt"
 5 | 
 6 | // 	"github.com/labstack/echo/v4"
 7 | // )
 8 | 
 9 | // func main() {
10 | // 	e := echo.New()
11 | 
12 | // 	e.Static("/static", "static")
13 | 
14 | // 	e.GET("/", func(c echo.Context) error {
15 | // 		return c.File("static/index.html")
16 | // 	})
17 | 
18 | // 	port := 8080
19 | // 	fmt.Printf("Server Run http://localhost:%d\n", port)
20 | // 	e.Start(fmt.Sprintf(":%d", port))
21 | // }
22 | 


--------------------------------------------------------------------------------
/example/static/fiber/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | // import (
 4 | // 	"fmt"
 5 | 
 6 | // 	"github.com/gofiber/fiber/v2"
 7 | // )
 8 | 
 9 | // func main() {
10 | // 	app := fiber.New()
11 | 
12 | // 	app.Static("/static", "./static")
13 | 
14 | // 	app.Get("/", func(c *fiber.Ctx) error {
15 | // 		return c.SendFile("./static/index.html")
16 | // 	})
17 | 
18 | // 	port := 8080
19 | // 	fmt.Printf("Server Run http://localhost:%d\n", port)
20 | // 	app.Listen(fmt.Sprintf(":%d", port))
21 | // }
22 | 


--------------------------------------------------------------------------------
/example/static/gin/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | // import (
 4 | // 	"fmt"
 5 | 
 6 | // 	"github.com/gin-gonic/gin"
 7 | // )
 8 | 
 9 | // func main() {
10 | // 	r := gin.Default()
11 | 
12 | // 	r.Static("/static", "./static")
13 | 
14 | // 	r.GET("/", func(c *gin.Context) {
15 | // 		c.File("static/index.html")
16 | // 	})
17 | 
18 | // 	port := 8080
19 | // 	fmt.Printf("Server Run http://localhost:%d\n", port)
20 | // 	r.Run(fmt.Sprintf(":%d", port))
21 | // }
22 | 


--------------------------------------------------------------------------------
/example/static/net.http.embed/main.go:
--------------------------------------------------------------------------------
 1 | //go:build !exclude_test
 2 | 
 3 | package main
 4 | 
 5 | import (
 6 | 	"embed"
 7 | 	"fmt"
 8 | 	"net/http"
 9 | )
10 | 
11 | //go:embed static/*
12 | var staticFiles embed.FS
13 | 
14 | func main() {
15 | 	fs := http.FileServer(http.FS(staticFiles))
16 | 	http.Handle("/static/", http.StripPrefix("/static/", fs))
17 | 
18 | 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
19 | 		data, err := staticFiles.ReadFile("static/index.html")
20 | 		if err != nil {
21 | 			http.Error(w, "Arquivo não encontrado", http.StatusNotFound)
22 | 			return
23 | 		}
24 | 		w.Header().Set("Content-Type", "text/html")
25 | 		w.Write(data)
26 | 	})
27 | 
28 | 	port := 8080
29 | 	fmt.Printf("Server Run http://localhost:%d\n", port)
30 | 	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
31 | }
32 | 


--------------------------------------------------------------------------------
/example/static/net.http.embed/static/index.html:
--------------------------------------------------------------------------------
1 | 

File Server Go example html

2 | -------------------------------------------------------------------------------- /example/static/net.http/main.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | func main() { 11 | fs := http.FileServer(http.Dir("./static")) 12 | 13 | http.Handle("/static/", http.StripPrefix("/static/", fs)) 14 | 15 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 16 | http.ServeFile(w, r, "static/index.html") 17 | }) 18 | 19 | port := 8080 20 | fmt.Printf("Server Run http://localhost:%d\n", port) 21 | http.ListenAndServe(fmt.Sprintf(":%d", port), nil) 22 | } 23 | 24 | // $ curl --location 'http://localhost:8080/' 25 | -------------------------------------------------------------------------------- /example/static/quick.embed/embed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/static/quick.embed/embed -------------------------------------------------------------------------------- /example/static/quick.embed/main.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | // Embed.FS allows you to include files directly into 4 | // the binary during compilation, eliminating the need to load files 5 | // from the file system at runtime. This means that 6 | // static files (HTML, CSS, JS, images, etc.) 7 | // are embedded into the executable. 8 | package main 9 | 10 | import ( 11 | "embed" 12 | "strings" 13 | 14 | "github.com/jeffotoni/quick" 15 | ) 16 | 17 | //go:embed static/* 18 | var staticFiles embed.FS 19 | 20 | func main() { 21 | q := quick.New() 22 | 23 | q.Static("/static", staticFiles) 24 | 25 | q.Get("/*", func(c *quick.Ctx) error { 26 | path := strings.TrimPrefix(c.Path(), "/static/") 27 | c.File("./static/" + path) 28 | return nil 29 | }) 30 | 31 | q.Listen("0.0.0.0:8080") 32 | } 33 | 34 | // $ curl --location 'http://localhost:8080/' 35 | -------------------------------------------------------------------------------- /example/static/quick.embed/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quick in Action 7 | 8 | 9 | 10 |
11 |

🚀 Quick in Action

12 |

Lightning-fast web framework in Go

13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/static/quick.embed/static/script.js: -------------------------------------------------------------------------------- 1 | function showMessage() { 2 | const msg = document.getElementById("message"); 3 | msg.classList.remove("hidden"); 4 | msg.classList.add("visible"); 5 | } -------------------------------------------------------------------------------- /example/static/quick.embed/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 3 | background: linear-gradient(to right, #2b5876, #4e4376); 4 | color: white; 5 | text-align: center; 6 | padding: 50px; 7 | margin: 0; 8 | } 9 | 10 | .container { 11 | max-width: 600px; 12 | margin: auto; 13 | background: rgba(0, 0, 0, 0.3); 14 | padding: 40px; 15 | border-radius: 12px; 16 | box-shadow: 0 0 10px #00000055; 17 | } 18 | 19 | button { 20 | padding: 10px 20px; 21 | font-size: 18px; 22 | border: none; 23 | background-color: #ffd700; 24 | color: #000; 25 | border-radius: 8px; 26 | cursor: pointer; 27 | transition: background 0.3s ease; 28 | } 29 | 30 | button:hover { 31 | background-color: #ffc400; 32 | } 33 | 34 | .message { 35 | margin-top: 20px; 36 | font-size: 20px; 37 | font-weight: bold; 38 | color: #00ff99; 39 | transition: opacity 0.3s ease; 40 | } 41 | 42 | .hidden { 43 | opacity: 0; 44 | visibility: hidden; 45 | } 46 | 47 | .visible { 48 | opacity: 1; 49 | visibility: visible; 50 | } -------------------------------------------------------------------------------- /example/static/quick/main.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | package main 4 | 5 | import ( 6 | "strings" 7 | 8 | "github.com/jeffotoni/quick" 9 | ) 10 | 11 | func main() { 12 | 13 | // start Quick 14 | q := quick.New() 15 | 16 | // start dir files 17 | q.Static("/static", "./static") 18 | 19 | // server files 20 | q.Get("/*", func(c *quick.Ctx) error { 21 | path := strings.TrimPrefix(c.Path(), "/static/") 22 | c.File("./static/" + path) 23 | return nil 24 | }) 25 | 26 | q.Listen("0.0.0.0:8080") 27 | } 28 | 29 | // $ curl --location 'http://localhost:8080/' 30 | -------------------------------------------------------------------------------- /example/static/quick/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quick in Action 7 | 8 | 9 | 10 |
11 |

🚀 Quick in Action

12 |

Lightning-fast web framework in Go

13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/static/quick/static/script.js: -------------------------------------------------------------------------------- 1 | function showMessage() { 2 | const msg = document.getElementById("message"); 3 | msg.classList.remove("hidden"); 4 | msg.classList.add("visible"); 5 | } -------------------------------------------------------------------------------- /example/static/quick/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 3 | background: linear-gradient(to right, #2b5876, #4e4376); 4 | color: white; 5 | text-align: center; 6 | padding: 50px; 7 | margin: 0; 8 | } 9 | 10 | .container { 11 | max-width: 600px; 12 | margin: auto; 13 | background: rgba(0, 0, 0, 0.3); 14 | padding: 40px; 15 | border-radius: 12px; 16 | box-shadow: 0 0 10px #00000055; 17 | } 18 | 19 | button { 20 | padding: 10px 20px; 21 | font-size: 18px; 22 | border: none; 23 | background-color: #ffd700; 24 | color: #000; 25 | border-radius: 8px; 26 | cursor: pointer; 27 | transition: background 0.3s ease; 28 | } 29 | 30 | button:hover { 31 | background-color: #ffc400; 32 | } 33 | 34 | .message { 35 | margin-top: 20px; 36 | font-size: 20px; 37 | font-weight: bold; 38 | color: #00ff99; 39 | transition: opacity 0.3s ease; 40 | } 41 | 42 | .hidden { 43 | opacity: 0; 44 | visibility: hidden; 45 | } 46 | 47 | .visible { 48 | opacity: 1; 49 | visibility: visible; 50 | } -------------------------------------------------------------------------------- /example/template/custom/main.go: -------------------------------------------------------------------------------- 1 | // . 2 | // ├── main.go 3 | // ├── views/ 4 | // │ ├── index.html 5 | // │ └── layouts/ 6 | // │ ├── main.html 7 | // │ └── base.html 8 | 9 | package main 10 | 11 | import ( 12 | "strings" 13 | 14 | "github.com/jeffotoni/quick" 15 | "github.com/jeffotoni/quick/template/html" 16 | ) 17 | 18 | func main() { 19 | engine := html.New("./views", ".html") 20 | 21 | // Example of adding a custom function 22 | engine.AddFunc("upper", strings.ToUpper) 23 | engine.Load() 24 | 25 | app := quick.New(quick.Config{ 26 | Views: engine, 27 | }) 28 | 29 | app.Get("/", func(c *quick.Ctx) error { 30 | return c.HTML("index", map[string]interface{}{ 31 | "Title": "Quick + Templates", 32 | "Message": "this is your index content in views", 33 | }) 34 | }) 35 | 36 | app.Get("/layout", func(c *quick.Ctx) error { 37 | return c.HTML("index", quick.M{ 38 | "Title": "Quick with Layout", 39 | "Message": "layout with main.html", 40 | }, "layouts/main") 41 | }) 42 | 43 | app.Get("/layout-nested", func(c *quick.Ctx) error { 44 | return c.HTML("index", quick.M{ 45 | "Title": "Nested Layouts", 46 | "Message": "this is nested layout content", 47 | }, "layouts/main", "layouts/base") 48 | }) 49 | 50 | app.Listen(":8080") 51 | } 52 | -------------------------------------------------------------------------------- /example/template/custom/views/index.html: -------------------------------------------------------------------------------- 1 | {{ define "index" }} 2 | 3 | 4 | 5 | Quick Template 6 | 7 | 8 |

Hello {{ .Title }}

9 | 10 |

{{ .Message}}

11 | 12 | {{ end }} 13 | -------------------------------------------------------------------------------- /example/template/custom/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | {{ define "layouts/base" }} 2 | 3 | 4 | 5 | {{ .Title }} 6 | 7 | 8 |
9 |

BASE HEADER

10 | {{ .yield }} 11 |

BASE FOOTER

12 |
13 | 14 | 15 | {{ end }} 16 | -------------------------------------------------------------------------------- /example/template/custom/views/layouts/main.html: -------------------------------------------------------------------------------- 1 | {{ define "layouts/main" }} 2 | 3 | 4 | 5 | {{ .Title }} 6 | 7 | 8 |

Main Layout

9 |
10 | {{ .yield }} 11 |
12 | 13 | 14 | {{ end }} 15 | -------------------------------------------------------------------------------- /example/template/embed/main.go: -------------------------------------------------------------------------------- 1 | // project/ 2 | // │ 3 | // ├── main.go 4 | // ├── views/ 5 | // │ ├── index.html 6 | // │ └── layouts/ 7 | // │ ├── main.html 8 | // │ └── base.html 9 | 10 | package main 11 | 12 | import ( 13 | "embed" 14 | "strings" 15 | 16 | "github.com/jeffotoni/quick" 17 | "github.com/jeffotoni/quick/template/html" 18 | ) 19 | 20 | //go:embed views/*.html views/layouts/*.html 21 | var viewsFS embed.FS 22 | 23 | func main() { 24 | engine := html.NewFileSystem(viewsFS, ".html") 25 | engine.Dir = "views" 26 | engine.AddFunc("upper", strings.ToUpper) 27 | engine.Load() 28 | 29 | app := quick.New(quick.Config{ 30 | Views: engine, 31 | }) 32 | 33 | app.Get("/", func(c *quick.Ctx) error { 34 | return c.HTML("index", map[string]interface{}{ 35 | "Title": "Quick + Templates (embed)", 36 | "Message": "this is your index content in views (embedded)", 37 | }) 38 | }) 39 | 40 | app.Get("/layout", func(c *quick.Ctx) error { 41 | return c.HTML("index", map[string]interface{}{ 42 | "Title": "Quick with Layout", 43 | "Message": "layout with main.html", 44 | }, "layouts/main") 45 | }) 46 | 47 | app.Get("/layout-nested", func(c *quick.Ctx) error { 48 | return c.HTML("index.html", map[string]interface{}{ 49 | "Title": "Nested Layouts", 50 | "Message": "this is nested layout content", 51 | }, "layouts/main", "layouts/base") 52 | }) 53 | 54 | app.Listen(":8080") 55 | } 56 | -------------------------------------------------------------------------------- /example/template/embed/views/index.html: -------------------------------------------------------------------------------- 1 |

{{ .Title }}

2 |

{{ .Message }}

3 | -------------------------------------------------------------------------------- /example/template/embed/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
{{ .yield }}
6 | 7 | 8 | -------------------------------------------------------------------------------- /example/template/embed/views/layouts/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Main Layout

4 |
{{ .yield }}
5 | 6 | 7 | -------------------------------------------------------------------------------- /example/upload.multipart/echo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // "io" 6 | // "net/http" 7 | // "os" 8 | 9 | // "github.com/labstack/echo/v4" 10 | // ) 11 | 12 | // func main() { 13 | // e := echo.New() 14 | 15 | // e.POST("/upload", func(c echo.Context) error { 16 | // // Get the file from the request 17 | // file, err := c.FormFile("file") 18 | // if err != nil { 19 | // return c.String(http.StatusBadRequest, "Error getting file") 20 | // } 21 | 22 | // // Open the uploaded file 23 | // src, err := file.Open() 24 | // if err != nil { 25 | // return c.String(http.StatusInternalServerError, "Error opening file") 26 | // } 27 | // defer src.Close() 28 | 29 | // // Create a local file 30 | // dst, err := os.Create(file.Filename) 31 | // if err != nil { 32 | // return c.String(http.StatusInternalServerError, "Error saving file") 33 | // } 34 | // defer dst.Close() 35 | 36 | // // Copy the content 37 | // _, err = io.Copy(dst, src) 38 | // if err != nil { 39 | // return c.String(http.StatusInternalServerError, "Error copying file") 40 | // } 41 | 42 | // return c.String(http.StatusOK, fmt.Sprintf("Upload successful: %s", file.Filename)) 43 | // }) 44 | 45 | // e.Start(":8080") 46 | // } 47 | -------------------------------------------------------------------------------- /example/upload.multipart/fiber/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // "io" 6 | // "os" 7 | 8 | // "github.com/gofiber/fiber/v2" 9 | // ) 10 | 11 | // func main() { 12 | // app := fiber.New() 13 | 14 | // app.Post("/upload", func(c *fiber.Ctx) error { 15 | // // Get the file from the request 16 | // file, err := c.FormFile("file") 17 | // if err != nil { 18 | // return c.Status(fiber.StatusBadRequest).SendString("Error getting file") 19 | // } 20 | 21 | // // Open the uploaded file 22 | // src, err := file.Open() 23 | // if err != nil { 24 | // return c.Status(fiber.StatusInternalServerError).SendString("Error opening file") 25 | // } 26 | // defer src.Close() 27 | 28 | // // Create a local file 29 | // dst, err := os.Create(file.Filename) 30 | // if err != nil { 31 | // return c.Status(fiber.StatusInternalServerError).SendString("Error saving file") 32 | // } 33 | // defer dst.Close() 34 | 35 | // // Copy the content 36 | // _, err = io.Copy(dst, src) 37 | // if err != nil { 38 | // return c.Status(fiber.StatusInternalServerError).SendString("Error copying file") 39 | // } 40 | 41 | // return c.SendString(fmt.Sprintf("Upload successful: %s", file.Filename)) 42 | // }) 43 | 44 | // app.Listen(":8080") 45 | // } 46 | -------------------------------------------------------------------------------- /example/upload.multipart/gin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // "io" 6 | // "os" 7 | 8 | // "github.com/gin-gonic/gin" 9 | // ) 10 | 11 | // func main() { 12 | // r := gin.Default() 13 | 14 | // r.POST("/upload", func(c *gin.Context) { 15 | // // Get the file from the request 16 | // file, err := c.FormFile("file") 17 | // if err != nil { 18 | // c.String(400, "Error getting file") 19 | // return 20 | // } 21 | 22 | // // Open the uploaded file 23 | // src, err := file.Open() 24 | // if err != nil { 25 | // c.String(500, "Error opening file") 26 | // return 27 | // } 28 | // defer src.Close() 29 | 30 | // // Create a local file 31 | // dst, err := os.Create(file.Filename) 32 | // if err != nil { 33 | // c.String(500, "Error saving file") 34 | // return 35 | // } 36 | // defer dst.Close() 37 | 38 | // // Copy the content 39 | // _, err = io.Copy(dst, src) 40 | // if err != nil { 41 | // c.String(500, "Error copying file") 42 | // return 43 | // } 44 | 45 | // c.String(200, fmt.Sprintf("Upload successful: %s", file.Filename)) 46 | // }) 47 | 48 | // r.Run(":8080") 49 | // } 50 | -------------------------------------------------------------------------------- /example/upload.multipart/net.http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func uploadHandler(w http.ResponseWriter, r *http.Request) { 11 | // Limite de 10MB para o upload 12 | r.ParseMultipartForm(10 << 20) 13 | 14 | // Recover the file 15 | file, handler, err := r.FormFile("file") 16 | if err != nil { 17 | http.Error(w, "Error receiving file", http.StatusBadRequest) 18 | return 19 | } 20 | defer file.Close() 21 | 22 | // Creates a local file to save the upload 23 | dst, err := os.Create(handler.Filename) 24 | if err != nil { 25 | http.Error(w, "Error saving file", http.StatusInternalServerError) 26 | return 27 | } 28 | defer dst.Close() 29 | 30 | // Copies the file data to the destination 31 | _, err = io.Copy(dst, file) 32 | if err != nil { 33 | http.Error(w, "Error copying file", http.StatusInternalServerError) 34 | return 35 | } 36 | 37 | fmt.Fprintf(w, "Upload successful: %s", handler.Filename) 38 | } 39 | 40 | func main() { 41 | http.HandleFunc("/upload", uploadHandler) 42 | fmt.Println("Server Run Port:8080") 43 | http.ListenAndServe(":8080", nil) 44 | } 45 | -------------------------------------------------------------------------------- /example/upload.multipart/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/example/upload.multipart/quick.png -------------------------------------------------------------------------------- /example/upload.multipart/quick/file/main.go: -------------------------------------------------------------------------------- 1 | // Uploading multiple files using Quick is simple, 2 | // and much more minimalistic than the patterns we've 3 | // seen in other frameworks. 4 | // 5 | // $ curl -v -X POST http://localhost:8080/upload -F "file=@quicktest.go" 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/jeffotoni/quick" 12 | ) 13 | 14 | type Msg struct { 15 | Msg string `json:"msg"` 16 | Error string `json:"error"` 17 | } 18 | 19 | func main() { 20 | // start Quick 21 | q := quick.New() 22 | 23 | q.Post("/upload", func(c *quick.Ctx) error { 24 | 25 | // Default Size Upload 1MB 26 | // Set upload limit (10MB) 27 | // c.FormFileLimit("10MB") 28 | 29 | uploadedFile, err := c.FormFile("file") 30 | if err != nil { 31 | return c.Status(400).JSON(Msg{ 32 | Msg: "Upload error", 33 | Error: err.Error(), 34 | }) 35 | } 36 | 37 | // saving file to disk 38 | uploadedFile.Save("/tmp/uploads") 39 | 40 | // or Can do It 41 | 42 | // saving file to disk 43 | uploadedFile.Save("/tmp/uploads", "codeJeff.go") 44 | 45 | // accessing file upload objects 46 | 47 | fmt.Println("Name:", uploadedFile.FileName()) 48 | fmt.Println("Size:", uploadedFile.Size()) 49 | fmt.Println("Content-Type:", uploadedFile.ContentType()) 50 | fmt.Println("Bytes:", len(uploadedFile.Bytes())) // remove the len to get the bytes 51 | 52 | // or 53 | fmt.Println("Name:", uploadedFile.Info.Filename) 54 | fmt.Println("Size:", uploadedFile.Info.Size) 55 | fmt.Println("Content-Type:", uploadedFile.Info.ContentType) 56 | fmt.Println("Bytes:", len(uploadedFile.Info.Bytes)) // remove the len to get the bytes 57 | 58 | // 59 | // Respond with success 60 | return c.Status(200).JSONIN(uploadedFile) 61 | }) 62 | 63 | q.Listen("0.0.0.0:8080") 64 | } 65 | -------------------------------------------------------------------------------- /fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/fork.png -------------------------------------------------------------------------------- /gcolor/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/gcolor/quick.png -------------------------------------------------------------------------------- /glog/bench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/glog/bench.png -------------------------------------------------------------------------------- /glog/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/glog/quick.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jeffotoni/quick 2 | 3 | go 1.24.0 4 | -------------------------------------------------------------------------------- /http/client/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | @rm -f ./coverage.out; 5 | 6 | bench: 7 | go test -bench=. -benchtime=1s -benchmem 8 | 9 | gosec: 10 | gosec ./... 11 | 12 | ineffassign: 13 | ineffassign ./... 14 | 15 | staticcheck: 16 | staticcheck ./... 17 | -------------------------------------------------------------------------------- /http/client/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=2 -cover -failfast -coverprofile coverage.out ./ 4 | go tool cover -html=coverage.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /http/client/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/http/client/quick.png -------------------------------------------------------------------------------- /http_status_example_test.go: -------------------------------------------------------------------------------- 1 | package quick 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // This function is named ExampleStatusText() 8 | // it with the Examples type. 9 | func ExampleStatusText() { 10 | fmt.Println(StatusText(200)) 11 | fmt.Println(StatusText(404)) 12 | fmt.Println(StatusText(500)) 13 | 14 | // Output: 15 | // OK 16 | // Not Found 17 | // Internal Server Error 18 | } 19 | -------------------------------------------------------------------------------- /internal/concat/concat.go: -------------------------------------------------------------------------------- 1 | package concat 2 | 3 | import "strings" 4 | 5 | func String(strs ...string) string { 6 | var sb strings.Builder 7 | for i := 0; i < len(strs); i++ { 8 | sb.WriteString(strs[i]) 9 | } 10 | return sb.String() 11 | } 12 | -------------------------------------------------------------------------------- /internal/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func Log(str ...string) { 9 | str = append(str, string("\n")) 10 | out := []byte(strings.Join(str, "")) 11 | _, err := os.Stdout.Write(out) 12 | if err != nil { 13 | panic(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/print/println.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func Stdout(str ...string) { 9 | _, err := os.Stdout.Write([]byte(strings.Join(str, ""))) 10 | if err != nil { 11 | panic(err) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /internal/qos/qos.go: -------------------------------------------------------------------------------- 1 | package qos 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | func FileExist(path string) error { 9 | _, err := os.Stat(path) 10 | if os.IsNotExist(err) { 11 | return errors.New("error file not exist") 12 | } else if err != nil { 13 | return err 14 | } else { 15 | return nil 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9h55aW21BjQNS 3 | tK563Jid+AMeCC9d8du0twYKTUaSzAcAb82VHnSxlV9DMZpQSRa59+YXIvJZ7KS5 4 | /LfHSV9nwPyIZ6bvpvX6Js1kXPcDn1fkpVC9TVSSV7ectIVnfnig67cC05OgVlK4 5 | lEkuDPPqcerpE5nflMI3PZRV8FlSPonDjG4QSIoScklpZ8xvqnPxz42hyCGCRH04 6 | YjHyQ6HU7WYAjAtXmrOJnbkYgfz37L2D1o+mk+hM0izOKOY5bmxTMR0zWy99dXLh 7 | Fjp/ibw7jvTU8dWFtpwdLBAwQ6itoM2ADZ+5eouNPiebkP33XWtUK59RSD9eK1MZ 8 | B18vX4efAgMBAAECggEAF4L1b/wbh6A4o0/qvS8Ud7RGnrkhNyRDvx1dUHXZpWhO 9 | Dg3QkQkgLM9869cBb6hBe3x3PLOHIQSsQq5JFh3SCgr0yO8i1GP/67JYlHS75+Ui 10 | gCjJ5VHsheL/0h7K1dXonP3gzCb3D1LmHCO5GE9fCtxq6dMWqZfIqBWpiyevzPXh 11 | 905F5kP3ypFqcpHy94jTI+p4O2PAZJi6UpzHrwmA0JMsRU4DLhvqMMLou7riLblH 12 | 7tarpVxspaA7hPZHocQMjSX1EtkG2DHgmGHfMIv5E7eAHuO1X4PxHi/JkIuAFhlm 13 | CRY6vFp4v6kfWoYpsQstatdbj78Zd5xL63UJHKlcwQKBgQD8C0n1JdEpLnIF1L5e 14 | 7jewGKBGyJzDFnF10IDQSGdVzlDad7/Y04dI6N0gP3n7TysB0ISyss6uMATAmU99 15 | 7kNOkhnta7lpfXRJgWHpxbKs5fZCaUYZOByPZS1wwws0nBHipNUYulePwUEXP/Mu 16 | wJzKp3ttO8XI5kk1l3sJQc/E7wKBgQDAgSXHrsTGZF9To+PCObl3bzUQ4bwYkbNy 17 | 3VwtNbG6vH0HtZCrTSAw+6ftFyNneSnYKYIGY826hTCjRfw2+0LZMq80impdGv+w 18 | MtmsiF3nmxq/skXk4m/l++r7XK/mcl6cLuiNdwGSPmvOz3IYy6oxhS1xh5o6zpwJ 19 | fLtcfLFIUQKBgQCwFcrzpBn+pV4vyLO/aropIutGyH0Mpq9Z2fNEkHDxN5SxLJwx 20 | hYasX1OtZet4sZSgTeeHWPigJRgM3o70sfL//35xqqL8mhMWD3gydFYiCP8E4ruA 21 | fv0lCVC3yOwZMzgjmXjvL7Bg1Gj/L0RcU2C3DE9kvy+s7YYUFOJ2Uy4urQKBgEFc 22 | mGNQZ+ektOlFQFP2HY7751ywCJme/vsFRgLjtFDfJuSzNUe0jDWVfNL3hEHh1d9b 23 | WJaq1HN8PaNyY8yXS39Lwklopeyu+hntahM2yguVkmN8OFcauzuvaX2nXuIz+wO0 24 | uakuuw3Yu+ogbXEV1deyjFd731YQSNK/0y33+InBAoGBAMmZX/dpeZ4y9pO7loZj 25 | SmAQCgax9e6JI+MqKcKGpqGp3SiYGqmYUq7cQ3N+N1yoWHWV2jVuhtFBmFOJMXaC 26 | 9ETIrno8qXZNkSZUtznEOebu60bhIs147ATBs0eTURfiWbqV7YRp96EDx7JBQIAm 27 | bAPC4GAb/aB7fXm9pMJGN5+Z 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /middleware/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/basicauth/basicauth_example_test.go: -------------------------------------------------------------------------------- 1 | // The BasicAuth middleware implements HTTP Basic Authentication 2 | // to secure specific routes on an HTTP server. 3 | // Example of how to use middleware in Quick 4 | // $ curl -H "Authorization: Basic $(echo -n 'wronguser:wrongpass' | base64)" http://localhost:8080/protected 5 | // $ curl -H "Authorization: Basic $(echo -n 'admin:1234' | base64)" http://localhost:8080/protected 6 | // $ curl http://localhost:8080/protected 7 | package basicauth 8 | 9 | import ( 10 | "log" 11 | 12 | "github.com/jeffotoni/quick" 13 | ) 14 | 15 | // This function is named ExampleBasicAuth() 16 | // it with the Examples type. 17 | func ExampleBasicAuth() { 18 | //starting Quick 19 | q := quick.New() 20 | 21 | // calling middleware 22 | q.Use(BasicAuth("admin", "1234")) 23 | 24 | // everything below Use will apply the middleware 25 | q.Get("/protected", func(c *quick.Ctx) error { 26 | c.Set("Content-Type", "application/json") 27 | return c.SendString("You have accessed a protected route!") 28 | }) 29 | 30 | // Start server 31 | log.Fatal(q.Listen("0.0.0.0:8080")) 32 | } 33 | 34 | //This function is named ExampleBasicAuth_withGroup 35 | func ExampleBasicAuth_withGroup() { 36 | //starting Quick 37 | q := quick.New() 38 | 39 | // using group to isolate routes and middlewares 40 | gr := q.Group("/") 41 | 42 | // middleware BasicAuth 43 | gr.Use(BasicAuth("admin", "1234")) 44 | 45 | // route public 46 | q.Get("/v1/user", func(c *quick.Ctx) error { 47 | c.Set("Content-Type", "application/json") 48 | return c.SendString("Public quick route") 49 | }) 50 | 51 | // protected route 52 | gr.Get("/protected", func(c *quick.Ctx) error { 53 | c.Set("Content-Type", "application/json") 54 | return c.SendString("You have accessed a protected route!") 55 | }) 56 | 57 | // Start server 58 | log.Fatal(q.Listen("0.0.0.0:8080")) 59 | } 60 | -------------------------------------------------------------------------------- /middleware/compress/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/compress/compress_example_test.go: -------------------------------------------------------------------------------- 1 | package compress 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "log" 9 | 10 | "github.com/jeffotoni/quick" 11 | ) 12 | 13 | // This function is named ExampleGzip() 14 | // it with the Examples type. 15 | func ExampleGzip() { 16 | // Starting Quick framework instance 17 | q := quick.New() 18 | 19 | // Enable Gzip middleware 20 | // This will automatically compress responses for clients that support Gzip 21 | q.Use(Gzip()) 22 | 23 | // Define a route that returns a compressed JSON response 24 | q.Get("/v1/compress", func(c *quick.Ctx) error { 25 | // Setting response headers 26 | c.Set("Content-Type", "application/json") 27 | 28 | // Defining the response structure 29 | type response struct { 30 | Msg string `json:"msg"` 31 | Headers map[string][]string `json:"headers"` 32 | } 33 | 34 | // Returning a JSON response with headers 35 | return c.Status(200).JSON(&response{ 36 | Msg: "Quick in action!", 37 | Headers: c.Headers, 38 | }) 39 | }) 40 | 41 | // Simulate a GET request with headers using Quick's testing functionality 42 | res, err := q.Qtest(quick.QuickTestOptions{ 43 | Method: quick.MethodGet, 44 | URI: "/v1/compress", 45 | Headers: map[string]string{"Accept-Encoding": "gzip"}, 46 | }) 47 | if err != nil { 48 | log.Fatalf("Error running test request: %v", err) 49 | } 50 | 51 | reader, err := gzip.NewReader(bytes.NewReader(res.Body())) 52 | if err != nil { 53 | panic(err) 54 | } 55 | defer reader.Close() 56 | 57 | var unzipped bytes.Buffer 58 | if _, err := io.Copy(&unzipped, reader); err != nil { 59 | panic(err) 60 | } 61 | 62 | // Print the response body 63 | // fmt.Println(res.BodyStr()) 64 | 65 | // Decompress 66 | fmt.Println(string(unzipped.Bytes())) 67 | 68 | // Output: 69 | // {"msg":"Quick in action!","headers":{"Accept-Encoding":["gzip"]}} 70 | } 71 | -------------------------------------------------------------------------------- /middleware/compress/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./ 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/compress/mock.go: -------------------------------------------------------------------------------- 1 | package compress 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | type testcompress struct { 9 | Request *http.Request 10 | HandlerFunc http.HandlerFunc 11 | } 12 | 13 | var testcompressSuccess = testcompress{ 14 | Request: &http.Request{ 15 | Header: http.Header{}, 16 | }, 17 | HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 18 | log.Printf("wr Content-Encoding -> %s", req.Header.Get("Content-Encoding")) 19 | }, 20 | ), 21 | } 22 | -------------------------------------------------------------------------------- /middleware/cors/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/cors/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./ 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/cors/mock.go: -------------------------------------------------------------------------------- 1 | package cors 2 | 3 | // type testCors struct { 4 | // Request *http.Request 5 | // HandlerFunc http.HandlerFunc 6 | // } 7 | 8 | // var ( 9 | // successDefaultCorsHeaders = map[string][]string{ 10 | // "Access-Control-Allow-Headers": {"Origin", "Content-Type"}, 11 | // "Access-Control-Allow-Methods": {"POST", "GET", "PUT", "DELETE", "PATH", "HEAD", "OPTIONS"}, 12 | // "Access-Control-Allow-Origin": {"*"}, 13 | // "X-Cors": {"true"}, 14 | // } 15 | 16 | // successCustomCorsHeaders = map[string][]string{ 17 | // "Access-Control-Allow-Headers": {"Origin", "Content-Type"}, 18 | // "Access-Control-Allow-Methods": {"GET", "POST"}, 19 | // "Access-Control-Allow-Origin": {"*"}, 20 | // "X-Cors": {"true"}, 21 | // } 22 | // ) 23 | 24 | // var testCorsSuccess = testCors{ 25 | // Request: &http.Request{ 26 | // Header: http.Header{}, 27 | // }, 28 | // HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 29 | // log.Printf("wr uuid -> %s", rw.Header()) 30 | // log.Printf("req uuid -> %s", req.Header) 31 | // }, 32 | // ), 33 | // } 34 | 35 | // func isHeaderEqual(come, want []string) bool { 36 | // var isAlltrue = false 37 | 38 | // for i := 0; i < len(come); i++ { 39 | // for j := 0; j < len(want); j++ { 40 | // dh := strings.Split(come[i], ",") 41 | // if len(dh) != len(want) { 42 | // return false 43 | // } 44 | // if dh[j] == want[j] { 45 | // isAlltrue = true 46 | // } 47 | // } 48 | // } 49 | 50 | // return isAlltrue 51 | // } 52 | 53 | // func isHeaderEqualDefault(come, want []string) bool { 54 | // var isAlltrue = false 55 | 56 | // for i := 0; i < len(come); i++ { 57 | // for j := 0; j < len(want); j++ { 58 | // if come[i] == want[j] { 59 | // isAlltrue = true 60 | // } 61 | // } 62 | // } 63 | 64 | // return isAlltrue 65 | // } 66 | -------------------------------------------------------------------------------- /middleware/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./... 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/healthcheck/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 🛠️ Healthcheck 3 | 4 | **Healthcheck** is a middleware this package provides a simple way to check the health of your application. 5 | 6 | --- 7 | ### ✨ Features 8 | 9 | - Simple healthcheck endpoint 10 | - Customizable endpoint 11 | 12 | --- 13 | ### 🧩 Example Usage 14 | ```go 15 | package main 16 | 17 | import ( 18 | "github.com/jeffotoni/quick" 19 | "github.com/seuusuario/healthcheck" 20 | 21 | ) 22 | 23 | func main() { 24 | q := quick.New() 25 | 26 | // Use Healthcheck middleware with default healthcheck endpoint 27 | q.Use(healthcheck.New( 28 | healthcheck.Options{ 29 | App: q, 30 | }, 31 | )) 32 | 33 | q.Get("/", func(c *quick.Ctx) error { 34 | return c.Status(200).String("Home page") 35 | }) 36 | 37 | log.Fatalln(q.Listen(":8080")) 38 | } 39 | ``` 40 | ### 📌 cURL 41 | ```bash 42 | $ curl -X GET 'http://localhost:8080/healthcheck' 43 | ``` 44 | 45 | ### 📌 Response 46 | Here's an example of the response returned: 47 | 48 | ```sh 49 | OK 50 | ``` 51 | 52 | --- 53 | ### ⚙️ Custom Configuration 54 | 55 | You can change the endpoint by providing an Options struct: 56 | 57 | ```go 58 | q.Use(healthcheck.New( 59 | healthcheck.Options{ 60 | App: q, 61 | Endpoint: "/v1/health", 62 | }, 63 | )) 64 | ``` 65 | 66 | 67 | --- 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /middleware/helmet/helmet_example_test.go: -------------------------------------------------------------------------------- 1 | package helmet 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jeffotoni/quick" 7 | ) 8 | 9 | // This function is named ExampleHelmet() 10 | // it with the Examples type. 11 | func ExampleHelmet() { 12 | 13 | q := quick.New() 14 | 15 | // Use Helmet middleware with default security headers 16 | q.Use(Helmet()) 17 | 18 | // Simple route to test headers 19 | q.Get("/v1/user", func(c *quick.Ctx) error { 20 | 21 | // list all headers 22 | headers := make(map[string]string) 23 | for k, v := range c.Response.Header() { 24 | if len(v) > 0 { 25 | headers[k] = v[0] 26 | } 27 | } 28 | return c.Status(200).JSONIN(headers) 29 | }) 30 | 31 | // Send test request using Quick's built-in test utility 32 | resp, _ := q.Qtest(quick.QuickTestOptions{ 33 | Method: quick.MethodGet, 34 | URI: "/v1/user", 35 | Headers: map[string]string{"Content-Type": "application/json"}, 36 | }) 37 | 38 | fmt.Println("Response Body:", string(resp.Body())) 39 | 40 | // Output: 41 | // Response Body: { 42 | // "Cache-Control": "no-cache, no-store, must-revalidate", 43 | // "Content-Security-Policy": "default-src 'self'", 44 | // "Cross-Origin-Embedder-Policy": "require-corp", 45 | // "Cross-Origin-Opener-Policy": "same-origin", 46 | // "Cross-Origin-Resource-Policy": "same-origin", 47 | // "Origin-Agent-Cluster": "?1", 48 | // "Referrer-Policy": "no-referrer", 49 | // "X-Content-Type-Options": "nosniff", 50 | // "X-Dns-Prefetch-Control": "off", 51 | // "X-Download-Options": "noopen", 52 | // "X-Frame-Options": "SAMEORIGIN", 53 | // "X-Permitted-Cross-Domain-Policies": "none", 54 | // "X-Xss-Protection": "0" 55 | // } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /middleware/logger/logger_example_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import "github.com/jeffotoni/quick" 4 | 5 | // This function is named ExampleNew() 6 | // 7 | // it with the Examples type. 8 | func ExampleNew() { 9 | 10 | q := quick.New() 11 | 12 | // Apply the logger middleware with default configuration 13 | q.Use(New()) 14 | 15 | // Define a simple GET route 16 | q.Get("/v1/logger", func(c *quick.Ctx) error { 17 | c.Set("Content-Type", "application/json") 18 | 19 | return c.Status(200).JSON(quick.M{ 20 | "msg": "Quick ❤️", 21 | }) 22 | }) 23 | 24 | // Start the server 25 | q.Listen("0.0.0.0:8080") 26 | } 27 | -------------------------------------------------------------------------------- /middleware/maxbody/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/maxbody/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./ 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/maxbody/maxbody_example_test.go: -------------------------------------------------------------------------------- 1 | package maxbody 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/jeffotoni/quick" 10 | ) 11 | 12 | // This function is named ExampleNew() 13 | // 14 | // it with the Examples type. 15 | func ExampleNew() { 16 | // Define a maximum body size of 1KB (1024 bytes) 17 | const maxBodySize = 1024 18 | 19 | // Create a new Quick instance 20 | q := quick.New() 21 | 22 | // Apply the maxbody middleware to limit request size 23 | q.Use(New(maxBodySize)) 24 | 25 | // Define a test route 26 | q.Post("/v1/user/maxbody", func(c *quick.Ctx) error { 27 | c.Set("Content-Type", "application/json") 28 | 29 | // Read the request body 30 | body, err := io.ReadAll(c.Request.Body) 31 | if err != nil { 32 | log.Printf("Error reading request body: %v", err) 33 | return c.Status(http.StatusRequestEntityTooLarge).String("Request body too large") 34 | } 35 | 36 | // Return the received body 37 | return c.Status(http.StatusOK).Send(body) 38 | }) 39 | 40 | // Simulate an HTTP request exceeding the 1KB limit 41 | oversizedBody := make([]byte, 2048) // 2KB payload to exceed the limit 42 | for i := range oversizedBody { 43 | oversizedBody[i] = 'A' 44 | } 45 | 46 | // Send test request using Quick's built-in test utility 47 | res, _ := q.Qtest(quick.QuickTestOptions{ 48 | Method: quick.MethodPost, 49 | URI: "/v1/user/maxbody", 50 | Headers: map[string]string{"Content-Type": "application/json"}, 51 | Body: oversizedBody, // Exceeding body limit 52 | }) 53 | 54 | // Print the response status and body 55 | fmt.Println(res.StatusCode()) // Expected: 413 (Payload Too Large) 56 | fmt.Println(res.BodyStr()) // Expected: "Request body too large" 57 | 58 | // Output: 59 | // 413 60 | // Request body too large 61 | } 62 | -------------------------------------------------------------------------------- /middleware/maxbody/mock.go: -------------------------------------------------------------------------------- 1 | package maxbody 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | type testMaxBody struct { 9 | Request *http.Request 10 | HandlerFunc http.HandlerFunc 11 | } 12 | 13 | const DefaultMaxBytes = 2 * 1024 * 1024 14 | 15 | var ( 16 | testMaxBodySuccess = testMaxBody{ 17 | Request: &http.Request{ 18 | Header: http.Header{}, 19 | }, 20 | HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 21 | log.Printf("req -> %v", req) 22 | }, 23 | ), 24 | } 25 | 26 | testMaxBodyFail = testMaxBody{ 27 | Request: &http.Request{ 28 | Header: http.Header{}, 29 | ContentLength: defaultMaxBytes + 1, 30 | }, 31 | HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 32 | }, 33 | ), 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /middleware/msgid/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ../coverage.sh; 3 | @rm -f ../cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/msgid/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./ 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/msgid/mock.go: -------------------------------------------------------------------------------- 1 | package msgid 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | type testMsgID struct { 9 | Request *http.Request 10 | HandlerFunc http.HandlerFunc 11 | } 12 | 13 | var testMsgIDSuccess = testMsgID{ 14 | Request: &http.Request{ 15 | Header: http.Header{}, 16 | }, 17 | HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 18 | log.Printf("wr uuid -> %s", rw.Header().Get(KeyMsgID)) 19 | log.Printf("req uuid -> %s", req.Header.Get(KeyMsgID)) 20 | }, 21 | ), 22 | } 23 | -------------------------------------------------------------------------------- /middleware/msgid/msgid_test.go: -------------------------------------------------------------------------------- 1 | package msgid 2 | 3 | import ( 4 | "net/http/httptest" 5 | "testing" 6 | ) 7 | 8 | // go test -v -failfast -count=1 -run ^TestNew$ 9 | // go test -v -count=1 -failfast -cover -coverprofile=coverage.out -run ^TestNew$; go tool cover -html=coverage.out 10 | func TestNew(t *testing.T) { 11 | 12 | type args struct { 13 | Config Config 14 | } 15 | 16 | tests := []struct { 17 | name string 18 | args args 19 | testMsgID testMsgID 20 | msgidValue string 21 | }{ 22 | { 23 | name: "success", 24 | args: args{ 25 | Config: Config{ 26 | Name: KeyMsgID, 27 | Start: 0, 28 | End: 0, 29 | Algo: nil, 30 | }, 31 | }, 32 | testMsgID: testMsgIDSuccess, 33 | msgidValue: "", 34 | }, 35 | } 36 | 37 | for _, ti := range tests { 38 | t.Run(ti.name, func(*testing.T) { 39 | t.Logf("==== TEST %s ====", ti.name) 40 | h := New(ti.args.Config) 41 | a := h(ti.testMsgID.HandlerFunc) 42 | rec := httptest.NewRecorder() 43 | ti.testMsgID.Request.Header.Set(KeyMsgID, ti.msgidValue) 44 | a.ServeHTTP(rec, ti.testMsgID.Request) 45 | resp := rec.Result() 46 | 47 | if resp.Header.Get(KeyMsgID) == "" && len(ti.msgidValue) == 0 { 48 | t.Errorf("was expected a uuid and nothing came") 49 | } 50 | }) 51 | } 52 | } 53 | 54 | // go test -bench=. -benchtime=1s -benchmem 55 | func BenchmarkNew(b *testing.B) { 56 | for n := 0; n < b.N; n++ { 57 | New() 58 | } 59 | // b.StopTimer() 60 | } 61 | 62 | // go test -bench=. -benchtime=1s -benchmem 63 | func BenchmarkAlgoDefault(b *testing.B) { 64 | for n := 0; n < b.N; n++ { 65 | AlgoDefault(9000000, 10000000) 66 | } 67 | // b.StopTimer() 68 | } 69 | -------------------------------------------------------------------------------- /middleware/msguuid/Makefile: -------------------------------------------------------------------------------- 1 | cover: 2 | @bash ./coverage.sh; 3 | @rm -f ./cover.out; 4 | 5 | bench: 6 | go test -bench=. -benchtime=1s -benchmem 7 | -------------------------------------------------------------------------------- /middleware/msguuid/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 🆔 MsgUUID 3 | Assigns a UUID (Universally Unique Identifier) to each request. 4 | 5 | - Allows easy tracking of requests in logs. 6 | - Useful for distributed systems where tracing requests across services is required. 7 | - Adds a unique identifier to every request automatically. 8 | 9 | 10 | --- 11 | 12 | ### ✅ Key Features 13 | 14 | | Feature | Benefit | 15 | |----------------------------|-------------------------------------------------------------| 16 | | 🆔 **Unique Identifier** | Adds a UUID to each request for tracking and correlation. | 17 | | 🔄 **Automatic Generation** | No need for manual UUID creation, added seamlessly. | 18 | | 📊 **Enhanced Debugging** | Makes log analysis easier by attaching request identifiers. | 19 | | 🚀 **Lightweight & Fast** | Does not impact performance, operates efficiently. | 20 | 21 | --- 22 | 23 | This example generates a unique request identifier with the MsgUUUID middleware. 24 | 25 | ```go 26 | package main 27 | 28 | import ( 29 | "fmt" 30 | "log" 31 | "github.com/jeffotoni/quick" 32 | "github.com/jeffotoni/quick/middleware/msguuid" 33 | ) 34 | 35 | func main() { 36 | q := quick.New() 37 | 38 | // Apply MsgUUID Middleware globally 39 | q.Use(msguuid.New()) 40 | 41 | // Define an endpoint that responds with a UUID 42 | q.Get("/v1/msguuid/default", func(c *quick.Ctx) error { 43 | c.Set("Content-Type", "application/json") 44 | 45 | // Log headers to validate UUID presence 46 | fmt.Println("Headers:", c.Response.Header()) 47 | 48 | // Return a 200 OK status 49 | return c.Status(200).JSON(nil) 50 | }) 51 | 52 | log.Fatal(q.Listen("0.0.0.0:8080")) 53 | } 54 | ``` 55 | ### 📌 cURL 56 | 57 | ```bash 58 | $ curl -i -XGET http://localhost:8080/v1/msguuid/default 59 | ``` 60 | ### 📌 Response 61 | ```bash 62 | "Headers":"map"[ 63 | "Content-Type":["application/json"], 64 | "Msguuid":[5f49cf4d-b62e-4d81-b46e-5125b52058a6] 65 | ] 66 | ``` 67 | -------------------------------------------------------------------------------- /middleware/msguuid/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile cover.out ./ 4 | go tool cover -html=cover.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /middleware/msguuid/mock.go: -------------------------------------------------------------------------------- 1 | package msguuid 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | type testMsgID struct { 9 | Request *http.Request 10 | HandlerFunc http.HandlerFunc 11 | } 12 | 13 | var testMsgIDSuccess = testMsgID{ 14 | Request: &http.Request{ 15 | Header: http.Header{}, 16 | }, 17 | HandlerFunc: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 18 | log.Printf("wr uuid -> %s", rw.Header().Get(KeyMsgUUID)) 19 | log.Printf("req uuid -> %s", req.Header.Get(KeyMsgUUID)) 20 | }, 21 | ), 22 | } 23 | -------------------------------------------------------------------------------- /middleware/msguuid/msguuid_example_test.go: -------------------------------------------------------------------------------- 1 | package msguuid 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jeffotoni/quick" 7 | ) 8 | 9 | // This function is named ExampleNew() 10 | // 11 | // it with the Examples type. 12 | func ExampleNew() { 13 | q := quick.New() 14 | 15 | // Apply MsgUUID Middleware globally 16 | q.Use(New()) 17 | 18 | // Define an endpoint that responds with a UUID 19 | q.Get("/v1/msguuid/default", func(c *quick.Ctx) error { 20 | c.Set("Content-Type", "application/json") 21 | 22 | // Retrieve the MsgUUID from the request headers 23 | _ = c.Request.Header.Get("Msguuid") 24 | 25 | // Return the MsgUUID in the JSON response 26 | return c.Status(200).JSON(map[string]string{"message": "generated msgId"}) 27 | //return c.Status(200).JSON(map[string]string{"msguuid": msguuid}) 28 | }) 29 | 30 | // Send test request using Quick's built-in test utility 31 | resp, err := q.Qtest(quick.QuickTestOptions{ 32 | Method: quick.MethodGet, 33 | URI: "/v1/msguuid/default", 34 | Headers: map[string]string{"Content-Type": "application/json"}, 35 | }) 36 | 37 | // Handle potential errors in test execution 38 | if err != nil { 39 | fmt.Println("Test execution error:", err) 40 | return 41 | } 42 | 43 | if err := resp.AssertStatus(200); err != nil { 44 | fmt.Println("status error:", err) 45 | } 46 | 47 | // Print response body to verify the MsgUUID 48 | fmt.Println(string(resp.Body())) 49 | //alternative print 50 | //{"msguuid":"8bf38aea-d27f-4217-bea0-0549181cc26a"} 51 | 52 | // Output: 53 | // {"message":"generated msgId"} 54 | } 55 | -------------------------------------------------------------------------------- /middleware/pprof/pprof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/middleware/pprof/pprof.jpg -------------------------------------------------------------------------------- /middleware/pprof/pprof_example_test.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jeffotoni/quick" 7 | ) 8 | 9 | // This function is named ExampleNew() 10 | // 11 | // it with the Examples type. 12 | func ExampleNew() { 13 | 14 | // Create a new Quick application instance 15 | q := quick.New() 16 | 17 | // Apply the pprof middleware to enable runtime profiling 18 | // This allows access to profiling endpoints like /debug/pprof/heap, /goroutine, etc. 19 | q.Use(New()) 20 | 21 | // Define a test route that matches /debug/pprof* 22 | // This is required so that the Quick router delegates the request to the pprof middleware 23 | q.Get("/debug/pprof*", func(c *quick.Ctx) error { 24 | return c.Next() 25 | }) 26 | 27 | // Simulate a GET request with headers 28 | res, _ := q.Qtest(quick.QuickTestOptions{ 29 | Method: quick.MethodGet, 30 | URI: "/debug/pprof", 31 | }) 32 | 33 | if err := res.AssertStatus(200); err != nil { 34 | fmt.Println("Status error:", err) 35 | } 36 | 37 | fmt.Println("Status:", res.StatusCode()) 38 | 39 | // Output: Status: 200 40 | 41 | } 42 | -------------------------------------------------------------------------------- /middleware/recover/README.md: -------------------------------------------------------------------------------- 1 | ## 🎗️ Recover 2 | 3 | **Recover** is a middleware this package provides a simple way to handle panics in your application and prints stack trace. 4 | 5 | --- 6 | ### 🧩 Example Usage 7 | ```go 8 | package main 9 | 10 | import ( 11 | "errors" 12 | 13 | "github.com/jeffotoni/quick" 14 | "github.com/jeffotoni/quick/middleware/recover" 15 | ) 16 | 17 | func main() { 18 | q := quick.New() 19 | 20 | // Apply the Recover middleware 21 | q.Use(recover.New(recover.Config{ 22 | App: q, 23 | })) 24 | 25 | // Define a test route 26 | q.Get("/v1/recover", func(c *quick.Ctx) error { 27 | c.Set("Content-Type", "application/json") 28 | 29 | // halt the server 30 | panic(errors.New("Panicking!")) 31 | }) 32 | 33 | // Start the server 34 | q.Listen("0.0.0.0:8080") 35 | } 36 | ``` 37 | 38 | --- 39 | ### 📌 cURL 40 | ```bash 41 | $ curl -i -XGET http://localhost:8080/v1/recover 42 | ``` 43 | 44 | ### 📌 Response 45 | 46 | Here's an example of the response returned: 47 | 48 | ```sh 49 | Internal Server Error 50 | ``` 51 | -------------------------------------------------------------------------------- /pullrequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/pullrequest.png -------------------------------------------------------------------------------- /pullrequesttext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/pullrequesttext.png -------------------------------------------------------------------------------- /quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/quick.png -------------------------------------------------------------------------------- /quick_head_test.go: -------------------------------------------------------------------------------- 1 | package quick 2 | 3 | import "testing" 4 | 5 | // TestRouteHEAD tests whether a HEAD route returns the expected response headers and status 6 | // without returning any response body. 7 | // 8 | // Usage: 9 | // 10 | // go test -v -run TestRouteHEAD 11 | func TestRouteHEAD(t *testing.T) { 12 | q := New() 13 | 14 | q.Head("/v1/user", func(c *Ctx) error { 15 | c.Set("Content-Type", "application/json") 16 | return c.String("Hello, HEAD!") // This should NOT be included in the body for HEAD 17 | }) 18 | 19 | res, err := q.Qtest(QuickTestOptions{ 20 | Method: MethodHead, 21 | URI: "/v1/user", 22 | Headers: map[string]string{"Content-Type": "application/json"}, 23 | }) 24 | if err != nil { 25 | t.Errorf("Error during Qtest: %v", err) 26 | return 27 | } 28 | 29 | if res.StatusCode() != 200 { 30 | t.Errorf("Expected status 200, got %d", res.StatusCode()) 31 | } 32 | 33 | // The body for a HEAD request must be empty 34 | if res.BodyStr() != "" { 35 | t.Errorf("Expected empty body for HEAD, got '%s'", res.BodyStr()) 36 | } 37 | 38 | // You can also check if the header is present 39 | if err := res.AssertHeader("Content-Type", "application/json"); err != nil { 40 | t.Errorf("Expected Content-Type 'application/json', got '%v'", err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /quick_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/quick_logo.png -------------------------------------------------------------------------------- /quick_server.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/quick_server.gif -------------------------------------------------------------------------------- /rand/README.md: -------------------------------------------------------------------------------- 1 | ## 🌀 Rand – Secure Random Generators ![Quick Logo](../../readmeLogs/quick.png) 2 | 3 | The `rand` package — utilities for generating random values in Go using only the **Go standard library**. 4 | 5 | It offers three main functions: 6 | 7 | - 🔐 `RandomInt(min, max)` — cryptographically secure random integer 8 | - 🧬 `TraceID()` — pseudo-random alphanumeric string (16 characters) 9 | - 🔢 `AlgoDefault(start, end)` — secure random number returned as a string 10 | 11 | --- 12 | 13 | ### 🔹 RandomInt(min, max) 14 | Generates a secure random integer in the range [min, max). 15 | 16 | ```go 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "log" 22 | 23 | "github.com/jeffotoni/quick/rand" 24 | ) 25 | 26 | func main() { 27 | n, err := rand.RandomInt(10, 20) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | fmt.Println("Random number generated:", n) 32 | } 33 | ``` 34 | --- 35 | 36 | ### 🔹 TraceID() 37 | Generates a 16-character alphanumeric ID for tracing or temporary identification. 38 | 39 | ```go 40 | package main 41 | 42 | import ( 43 | "fmt" 44 | 45 | "github.com/jeffotoni/quick/rand" 46 | ) 47 | 48 | func main() { 49 | id := rand.TraceID() 50 | fmt.Println("Trace ID generated:", id) 51 | } 52 | ``` 53 | --- 54 | 55 | ### 🔹AlgoDefault(start, end) 56 | Generates a secure random number between start and end (as a string). 57 | 58 | ```go 59 | package main 60 | 61 | import ( 62 | "fmt" 63 | 64 | "github.com/jeffotoni/quick/rand" 65 | ) 66 | 67 | func main() { 68 | msgID := rand.AlgoDefault(1000, 9999) 69 | fmt.Println("Msg ID generated:", msgID) 70 | } 71 | ``` -------------------------------------------------------------------------------- /rand/rand_example_test.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | // This function is named ExampleRandomInt() 9 | // it with the Examples type. 10 | func ExampleRandomInt() { 11 | _, err := RandomInt(10, 20) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | 16 | // Simulated value for documentation purposes 17 | //fmt.Println("Random number generated: 17") 18 | 19 | fmt.Println("Random number generated") 20 | // Output: Random number generated 21 | } 22 | 23 | // This function is named ExampleTraceID() 24 | // it with the Examples type. 25 | func ExampleTraceID() { 26 | _ = TraceID() 27 | 28 | // Simulated trace ID format 29 | //fmt.Println("Trace ID generated: a8b7XkP1ZcDqWmE2") 30 | 31 | fmt.Println("Trace ID generated") 32 | // Output: Trace ID generated 33 | } 34 | 35 | // This function is named ExampleAlgoDefault() 36 | // it with the Examples type. 37 | func ExampleAlgoDefault() { 38 | _ = AlgoDefault(1000, 9999) 39 | 40 | // Simulated Msg ID for example purposes 41 | //fmt.Println("Msg Id generated: 3842") 42 | 43 | fmt.Println("Msg Id generated") 44 | // Output: Msg Id generated 45 | } 46 | -------------------------------------------------------------------------------- /rand/rand_test.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | // TestRandomIntRange verifies that RandomInt returns values within the specified range [10, 20). 9 | // It also ensures that no error occurs during generation. 10 | // 11 | // To run: 12 | // 13 | // go test -v -run ^TestRandomIntRange$ 14 | func TestRandomIntRange(t *testing.T) { 15 | for i := 0; i < 10; i++ { 16 | n, err := RandomInt(10, 20) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | if n < 10 || n >= 20 { 21 | t.Errorf("expected value in range [10, 20), got %d", n) 22 | } 23 | } 24 | } 25 | 26 | // TestTraceIDLength ensures that the generated trace ID has a fixed length of 16 characters. 27 | // 28 | // To run: 29 | // 30 | // go test -v -run ^TestTraceIDLength$ 31 | func TestTraceIDLength(t *testing.T) { 32 | id := TraceID() 33 | if len(id) != 16 { 34 | t.Errorf("expected ID length 16, got %d", len(id)) 35 | } 36 | } 37 | 38 | // TestAlgoDefaultRange checks whether AlgoDefault generates a non-empty string 39 | // and the resulting integer falls within the expected range [Start, Start+End). 40 | // 41 | // To run: 42 | // 43 | // go test -v -run ^TestAlgoDefaultRange$ 44 | func TestAlgoDefaultRange(t *testing.T) { 45 | Start := 1000 46 | End := 9999 47 | 48 | for i := 0; i < 10; i++ { 49 | id := AlgoDefault(Start, End) 50 | if id == "" { 51 | t.Errorf("expected non-empty string, got empty") 52 | } 53 | n, err := strconv.Atoi(id) 54 | if err != nil { 55 | t.Fatal("invalid integer:", err) 56 | } 57 | // Acceptable range is [Start, Start+End) 58 | if n < Start || n >= Start+End { 59 | t.Errorf("expected value in range [%d, %d), got %d", Start, Start+End, n) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /readmeLogs/log.format.json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/log.format.json.png -------------------------------------------------------------------------------- /readmeLogs/log.format.slog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/log.format.slog.png -------------------------------------------------------------------------------- /readmeLogs/log.format.text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/log.format.text.png -------------------------------------------------------------------------------- /readmeLogs/log.simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/log.simple.png -------------------------------------------------------------------------------- /readmeLogs/msguuid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/msguuid.png -------------------------------------------------------------------------------- /readmeLogs/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/readmeLogs/quick.png -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -ne "\ncoverage starting\n" 3 | go test -v -count=1 -cover -failfast -coverprofile coverage.out ./ 4 | go tool cover -html=coverage.out -o coverage.html 5 | echo -ne "\ncoverage completed\n" 6 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go test -race -v ./... 4 | go test -v -tags musl -covermode atomic -coverprofile=coverage.out ./... 5 | gosec ./... 6 | staticcheck ./... 7 | ineffassign ./... 8 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 |

File Server Go example html

2 | -------------------------------------------------------------------------------- /template/html/html_benchmark_test.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | package html 4 | 5 | import ( 6 | "io" 7 | "testing" 8 | ) 9 | 10 | // Benchmark rendering without any layout 11 | func BenchmarkRenderNoLayout(b *testing.B) { 12 | dir := createTestTemplates(b) 13 | engine := New(dir, ".html") 14 | 15 | if err := engine.Load(); err != nil { 16 | b.Fatalf("failed to load templates: %v", err) 17 | } 18 | 19 | data := map[string]interface{}{ 20 | "Title": "Benchmark", 21 | "Message": "No layout", 22 | } 23 | 24 | b.ResetTimer() 25 | for i := 0; i < b.N; i++ { 26 | err := engine.Render(io.Discard, "index.html", data) 27 | if err != nil { 28 | b.Fatalf("render failed: %v", err) 29 | } 30 | } 31 | } 32 | 33 | // Benchmark rendering with a layout 34 | func BenchmarkRenderWithLayout(b *testing.B) { 35 | dir := createTestTemplates(b) 36 | engine := New(dir, ".html") 37 | 38 | if err := engine.Load(); err != nil { 39 | b.Fatalf("failed to load templates: %v", err) 40 | } 41 | 42 | data := map[string]interface{}{ 43 | "Title": "Benchmark", 44 | "Message": "With layout", 45 | } 46 | 47 | b.ResetTimer() 48 | for i := 0; i < b.N; i++ { 49 | err := engine.Render(io.Discard, "index.html", data, "layouts/main.html") 50 | if err != nil { 51 | b.Fatalf("render with layout failed: %v", err) 52 | } 53 | } 54 | } 55 | 56 | // Benchmark rendering with nested layouts 57 | func BenchmarkRenderWithNestedLayouts(b *testing.B) { 58 | dir := createTestTemplates(b) 59 | engine := New(dir, ".html") 60 | 61 | if err := engine.Load(); err != nil { 62 | b.Fatalf("failed to load templates: %v", err) 63 | } 64 | 65 | data := map[string]interface{}{ 66 | "Title": "Benchmark", 67 | "Message": "Nested layout", 68 | } 69 | 70 | b.ResetTimer() 71 | for i := 0; i < b.N; i++ { 72 | err := engine.Render(io.Discard, "index.html", data, "layouts/main.html", "layouts/base.html") 73 | if err != nil { 74 | b.Fatalf("render with nested layout failed: %v", err) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /template/html/html_example_test.go: -------------------------------------------------------------------------------- 1 | //go:build !exclude_test 2 | 3 | package html 4 | 5 | import ( 6 | "bytes" 7 | "embed" 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | //go:embed static/views/*.html static/views/layouts/*.html 13 | var templates embed.FS 14 | 15 | // ExampleNew demonstrates loading templates from filesystem. 16 | // This function is named ExampleNew() 17 | // it with the Examples type. 18 | func ExampleNew() { 19 | engine := New("./static/views", ".html") 20 | engine.AddFunc("upper", strings.ToUpper) 21 | _ = engine.Load() 22 | 23 | var buf bytes.Buffer 24 | engine.Render(&buf, "index", map[string]interface{}{ 25 | "Title": "From FS", 26 | "Message": "Hello Quick", 27 | }) 28 | 29 | fmt.Println("ok") 30 | // Output: ok 31 | } 32 | 33 | // ExampleNewFileSystem demonstrates loading templates from embed.FS. 34 | // This function is named ExampleNewFileSystem() 35 | // it with the Examples type. 36 | func ExampleNewFileSystem() { 37 | engine := NewFileSystem(templates, ".html") 38 | engine.Dir = "static/views" 39 | _ = engine.Load() 40 | 41 | var buf bytes.Buffer 42 | engine.Render(&buf, "index", map[string]interface{}{ 43 | "Title": "From embed", 44 | "Message": "Hello from embed.FS", 45 | }) 46 | 47 | fmt.Println("ok") 48 | // Output: ok 49 | } 50 | -------------------------------------------------------------------------------- /template/html/static/views/index.html: -------------------------------------------------------------------------------- 1 |

{{ .Title }}

2 |

{{ .Message }}

3 | -------------------------------------------------------------------------------- /template/html/static/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Base Layout Header
5 |
{{ .yield }}
6 | 7 | 8 | -------------------------------------------------------------------------------- /template/html/static/views/layouts/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Main Layout 4 | {{ .yield }} 5 | 6 | -------------------------------------------------------------------------------- /template/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffotoni/quick/a2ef37329b6e90c69144e56a0edda25939977d0a/template/quick.png -------------------------------------------------------------------------------- /template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // TemplateEngine is the interface for pluggable template engines. 8 | type TemplateEngine interface { 9 | Render(w io.Writer, name string, data interface{}, layouts ...string) error 10 | AddFunc(name string, fn interface{}) 11 | } 12 | -------------------------------------------------------------------------------- /test_uploads/file1.txt: -------------------------------------------------------------------------------- 1 | File 1 content -------------------------------------------------------------------------------- /test_uploads/file2.txt: -------------------------------------------------------------------------------- 1 | File 2 content -------------------------------------------------------------------------------- /test_uploads/quick.txt: -------------------------------------------------------------------------------- 1 | file for test -------------------------------------------------------------------------------- /uploads/quick.txt: -------------------------------------------------------------------------------- 1 | Hello, Quick! -------------------------------------------------------------------------------- /uuid/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16) 4 | 5 | 6 | ### Features 7 | 8 | * add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3)) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06)) 14 | * Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6)) 15 | 16 | ## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12) 17 | 18 | 19 | ### Features 20 | 21 | * Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29)) 22 | 23 | ## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) 24 | 25 | 26 | ### Features 27 | 28 | * UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) 29 | 30 | ### Fixes 31 | 32 | * Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) 33 | 34 | ## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) 40 | 41 | ## Changelog 42 | -------------------------------------------------------------------------------- /uuid/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We definitely welcome patches and contribution to this project! 4 | 5 | ### Tips 6 | 7 | Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). 8 | 9 | Always try to include a test case! If it is not possible or not necessary, 10 | please explain why in the pull request description. 11 | 12 | ### Releasing 13 | 14 | Commits that would precipitate a SemVer change, as described in the Conventional 15 | Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) 16 | to create a release candidate pull request. Once submitted, `release-please` 17 | will create a release. 18 | 19 | For tips on how to work with `release-please`, see its documentation. 20 | 21 | ### Legal requirements 22 | 23 | In order to protect both you and ourselves, you will need to sign the 24 | [Contributor License Agreement](https://cla.developers.google.com/clas). 25 | 26 | You may have already signed it for other Google projects. 27 | -------------------------------------------------------------------------------- /uuid/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Paul Borman 2 | bmatsuo 3 | shawnps 4 | theory 5 | jboverfelt 6 | dsymonds 7 | cd1 8 | wallclockbuilder 9 | dansouza 10 | -------------------------------------------------------------------------------- /uuid/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009,2014 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /uuid/README.md: -------------------------------------------------------------------------------- 1 | # uuid 2 | The uuid package generates and inspects UUIDs based on 3 | [RFC 9562](https://datatracker.ietf.org/doc/html/rfc9562) 4 | and DCE 1.1: Authentication and Security Services. 5 | 6 | This package is based on the github.com/pborman/uuid package (previously named 7 | code.google.com/p/go-uuid). It differs from these earlier packages in that 8 | a UUID is a 16 byte array rather than a byte slice. One loss due to this 9 | change is the ability to represent an invalid UUID (vs a NIL UUID). 10 | 11 | ###### Install 12 | ```sh 13 | go get github.com/google/uuid 14 | ``` 15 | 16 | ###### Documentation 17 | [![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) 18 | 19 | Full `go doc` style documentation for the package can be viewed online without 20 | installing this package by using the GoDoc site here: 21 | https://pkg.go.dev/github.com/google/uuid 22 | -------------------------------------------------------------------------------- /uuid/dce_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // This function is named ExampleNewDCESecurity() 8 | // it with the Examples type. 9 | func ExampleNewDCESecurity() { 10 | u, err := NewDCESecurity(Person, 1234) 11 | if err != nil { 12 | fmt.Println("error") 13 | return 14 | } 15 | fmt.Printf("Domain: %s, ID: %d, Version: %s\n", u.Domain(), u.ID(), u.Version()) 16 | // Output: Domain: Person, ID: 1234, Version: VERSION_2 17 | } 18 | 19 | // This function is named ExampleNewDCEPerson() 20 | // it with the Examples type. 21 | func ExampleNewDCEPerson() { 22 | u, err := NewDCEPerson() 23 | if err != nil { 24 | fmt.Println("error") 25 | return 26 | } 27 | fmt.Println(u.Domain()) 28 | // Output: Person 29 | } 30 | 31 | // This function is named ExampleNewDCEGroup() 32 | // it with the Examples type. 33 | func ExampleNewDCEGroup() { 34 | u, err := NewDCEGroup() 35 | if err != nil { 36 | fmt.Println("error") 37 | return 38 | } 39 | fmt.Println(u.Domain()) 40 | // Output: Group 41 | } 42 | -------------------------------------------------------------------------------- /uuid/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package uuid generates and inspects UUIDs. 6 | // 7 | // UUIDs are based on RFC 9562(obsoletes RFC 4122) and DCE 1.1: Authentication and Security 8 | // Services. 9 | // 10 | // A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to 11 | // maps or compared directly. 12 | package uuid 13 | -------------------------------------------------------------------------------- /uuid/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "crypto/md5" 9 | "crypto/sha1" 10 | "hash" 11 | ) 12 | 13 | // Well known namespace IDs and UUIDs 14 | var ( 15 | NameSpaceDNS = MustParse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") 16 | NameSpaceURL = MustParse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") 17 | NameSpaceOID = MustParse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") 18 | NameSpaceX500 = MustParse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") 19 | Nil UUID // empty UUID, all zeros 20 | 21 | // The Max UUID is special form of UUID that is specified to have all 128 bits set to 1. 22 | Max = UUID{ 23 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 24 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 25 | } 26 | ) 27 | 28 | // NewHash returns a new UUID derived from the hash of space concatenated with 29 | // data generated by h. The hash should be at least 16 byte in length. The 30 | // first 16 bytes of the hash are used to form the UUID. The version of the 31 | // UUID will be the lower 4 bits of version. NewHash is used to implement 32 | // NewMD5 and NewSHA1. 33 | func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { 34 | h.Reset() 35 | h.Write(space[:]) //nolint:errcheck 36 | h.Write(data) //nolint:errcheck 37 | s := h.Sum(nil) 38 | var uuid UUID 39 | copy(uuid[:], s) 40 | uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) 41 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 9562 variant 42 | return uuid 43 | } 44 | 45 | // NewMD5 returns a new MD5 (Version 3) UUID based on the 46 | // supplied name space and data. It is the same as calling: 47 | // 48 | // NewHash(md5.New(), space, data, 3) 49 | func NewMD5(space UUID, data []byte) UUID { 50 | return NewHash(md5.New(), space, data, 3) 51 | } 52 | 53 | // NewSHA1 returns a new SHA1 (Version 5) UUID based on the 54 | // supplied name space and data. It is the same as calling: 55 | // 56 | // NewHash(sha1.New(), space, data, 5) 57 | func NewSHA1(space UUID, data []byte) UUID { 58 | return NewHash(sha1.New(), space, data, 5) 59 | } 60 | -------------------------------------------------------------------------------- /uuid/hash_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | ) 7 | 8 | // This function is named ExampleNewMD5() 9 | // it with the Examples type. 10 | func ExampleNewMD5() { 11 | u := NewMD5(NameSpaceDNS, []byte("golang.org")) 12 | fmt.Println(u.String()) 13 | // Output: c4e4c1e8-2e52-3de5-aacb-c9bc7208105d 14 | } 15 | 16 | // This function is named ExampleNewSHA1() 17 | // it with the Examples type. 18 | func ExampleNewSHA1() { 19 | u := NewSHA1(NameSpaceDNS, []byte("golang.org")) 20 | fmt.Println(u.String()) 21 | // Output: 53447179-a84a-5086-927b-77f5951d9e4e 22 | } 23 | 24 | // This function is named ExampleNewHash() 25 | // it with the Examples type. 26 | func ExampleNewHash() { 27 | u := NewHash(md5.New(), NameSpaceURL, []byte("https://golang.org"), 3) 28 | fmt.Println(u.String()) 29 | // Output: 282a1487-8b16-3fc9-ab77-be3810dacc14 30 | } 31 | -------------------------------------------------------------------------------- /uuid/marshal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import "fmt" 8 | 9 | // MarshalText implements encoding.TextMarshaler. 10 | func (uuid UUID) MarshalText() ([]byte, error) { 11 | var js [36]byte 12 | encodeHex(js[:], uuid) 13 | return js[:], nil 14 | } 15 | 16 | // UnmarshalText implements encoding.TextUnmarshaler. 17 | func (uuid *UUID) UnmarshalText(data []byte) error { 18 | id, err := ParseBytes(data) 19 | if err != nil { 20 | return err 21 | } 22 | *uuid = id 23 | return nil 24 | } 25 | 26 | // MarshalBinary implements encoding.BinaryMarshaler. 27 | func (uuid UUID) MarshalBinary() ([]byte, error) { 28 | return uuid[:], nil 29 | } 30 | 31 | // UnmarshalBinary implements encoding.BinaryUnmarshaler. 32 | func (uuid *UUID) UnmarshalBinary(data []byte) error { 33 | if len(data) != 16 { 34 | return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) 35 | } 36 | copy(uuid[:], data) 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /uuid/marshal_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // This function is named ExampleUUID_MarshalText() 8 | // it with the Examples type. 9 | func ExampleUUID_MarshalText() { 10 | uuid, err := Parse("123e4567-e89b-12d3-a456-426614174000") 11 | if err != nil { 12 | fmt.Println("error parsing UUID:", err) 13 | return 14 | } 15 | text, err := uuid.MarshalText() 16 | if err != nil { 17 | fmt.Println("error marshaling to text:", err) 18 | return 19 | } 20 | fmt.Printf("%s\n", text) 21 | // Output: 123e4567-e89b-12d3-a456-426614174000 22 | } 23 | 24 | // This function is named ExampleUUID_UnmarshalText() 25 | // it with the Examples type. 26 | func ExampleUUID_UnmarshalText() { 27 | var uuid UUID 28 | data := []byte("123e4567-e89b-12d3-a456-426614174000") 29 | err := uuid.UnmarshalText(data) 30 | if err != nil { 31 | fmt.Println("error unmarshaling text:", err) 32 | return 33 | } 34 | fmt.Printf("UUID: %v\n", uuid) 35 | // Output: UUID: 123e4567-e89b-12d3-a456-426614174000 36 | } 37 | 38 | // This function is named ExampleUUID_MarshalBinary() 39 | // it with the Examples type. 40 | func ExampleUUID_MarshalBinary() { 41 | // Criando um UUID fixo para consistência nos testes 42 | uuid, err := Parse("123e4567-e89b-12d3-a456-426614174000") 43 | if err != nil { 44 | fmt.Printf("failed to parse UUID: %v\n", err) 45 | return 46 | } 47 | data, err := uuid.MarshalBinary() 48 | if err != nil { 49 | fmt.Printf("failed to marshal UUID: %v\n", err) 50 | return 51 | } 52 | fmt.Printf("Binary data: %x\n", data) 53 | // Output: Binary data: 123e4567e89b12d3a456426614174000 54 | } 55 | 56 | // This function is named ExampleUUID_UnmarshalBinary() 57 | // it with the Examples type. 58 | func ExampleUUID_UnmarshalBinary() { 59 | var uuid UUID 60 | // Representação binária exemplo de um UUID 61 | data := []byte{0x12, 0x3e, 0x45, 0x67, 0xe8, 0x9b, 0x12, 0xd3, 0xa4, 0x56, 0x42, 0x66, 0x14, 0x17, 0x40, 0x00} 62 | err := uuid.UnmarshalBinary(data) 63 | if err != nil { 64 | fmt.Println("error unmarshaling binary:", err) 65 | return 66 | } 67 | fmt.Printf("UUID: %v\n", uuid) 68 | // Output: UUID: 123e4567-e89b-12d3-a456-426614174000 69 | } 70 | -------------------------------------------------------------------------------- /uuid/node_js.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build js 6 | 7 | package uuid 8 | 9 | // getHardwareInterface returns nil values for the JS version of the code. 10 | // This removes the "net" dependency, because it is not used in the browser. 11 | // Using the "net" library inflates the size of the transpiled JS code by 673k bytes. 12 | func getHardwareInterface(name string) (string, []byte) { return "", nil } 13 | -------------------------------------------------------------------------------- /uuid/node_net.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !js 6 | 7 | package uuid 8 | 9 | import "net" 10 | 11 | var interfaces []net.Interface // cached list of interfaces 12 | 13 | // getHardwareInterface returns the name and hardware address of interface name. 14 | // If name is "" then the name and hardware address of one of the system's 15 | // interfaces is returned. If no interfaces are found (name does not exist or 16 | // there are no interfaces) then "", nil is returned. 17 | // 18 | // Only addresses of at least 6 bytes are returned. 19 | func getHardwareInterface(name string) (string, []byte) { 20 | if interfaces == nil { 21 | var err error 22 | interfaces, err = net.Interfaces() 23 | if err != nil { 24 | return "", nil 25 | } 26 | } 27 | for _, ifs := range interfaces { 28 | if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { 29 | return ifs.Name, ifs.HardwareAddr 30 | } 31 | } 32 | return "", nil 33 | } 34 | -------------------------------------------------------------------------------- /uuid/seq_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "flag" 9 | "runtime" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | // This test is only run when --regressions is passed on the go test line. 15 | var regressions = flag.Bool("regressions", false, "run uuid regression tests") 16 | 17 | // TestClockSeqRace tests for a particular race condition of returning two 18 | // identical Version1 UUIDs. The duration of 1 minute was chosen as the race 19 | // condition, before being fixed, nearly always occurred in under 30 seconds. 20 | func TestClockSeqRace(t *testing.T) { 21 | if !*regressions { 22 | t.Skip("skipping regression tests") 23 | } 24 | duration := time.Minute 25 | 26 | done := make(chan struct{}) 27 | defer close(done) 28 | 29 | ch := make(chan UUID, 10000) 30 | ncpu := runtime.NumCPU() 31 | switch ncpu { 32 | case 0, 1: 33 | // We can't run the test effectively. 34 | t.Skip("skipping race test, only one CPU detected") 35 | return 36 | default: 37 | runtime.GOMAXPROCS(ncpu) 38 | } 39 | for i := 0; i < ncpu; i++ { 40 | go func() { 41 | for { 42 | select { 43 | case <-done: 44 | return 45 | case ch <- Must(NewUUID()): 46 | } 47 | } 48 | }() 49 | } 50 | 51 | uuids := make(map[string]bool) 52 | cnt := 0 53 | start := time.Now() 54 | for u := range ch { 55 | s := u.String() 56 | if uuids[s] { 57 | t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s) 58 | return 59 | } 60 | uuids[s] = true 61 | if time.Since(start) > duration { 62 | return 63 | } 64 | cnt++ 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /uuid/sql.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "database/sql/driver" 9 | "fmt" 10 | ) 11 | 12 | // Scan implements sql.Scanner so UUIDs can be read from databases transparently. 13 | // Currently, database types that map to string and []byte are supported. Please 14 | // consult database-specific driver documentation for matching types. 15 | func (uuid *UUID) Scan(src interface{}) error { 16 | switch src := src.(type) { 17 | case nil: 18 | return nil 19 | 20 | case string: 21 | // if an empty UUID comes from a table, we return a null UUID 22 | if src == "" { 23 | return nil 24 | } 25 | 26 | // see Parse for required string format 27 | u, err := Parse(src) 28 | if err != nil { 29 | return fmt.Errorf("Scan: %v", err) 30 | } 31 | 32 | *uuid = u 33 | 34 | case []byte: 35 | // if an empty UUID comes from a table, we return a null UUID 36 | if len(src) == 0 { 37 | return nil 38 | } 39 | 40 | // assumes a simple slice of bytes if 16 bytes 41 | // otherwise attempts to parse 42 | if len(src) != 16 { 43 | return uuid.Scan(string(src)) 44 | } 45 | copy((*uuid)[:], src) 46 | 47 | default: 48 | return fmt.Errorf("Scan: unable to scan type %T into UUID", src) 49 | } 50 | 51 | return nil 52 | } 53 | 54 | // Value implements sql.Valuer so that UUIDs can be written to databases 55 | // transparently. Currently, UUIDs map to strings. Please consult 56 | // database-specific driver documentation for matching types. 57 | func (uuid UUID) Value() (driver.Value, error) { 58 | return uuid.String(), nil 59 | } 60 | -------------------------------------------------------------------------------- /uuid/time_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestGetTime(t *testing.T) { 9 | now := time.Now() 10 | tt := map[string]struct { 11 | input func() *time.Time 12 | expectedTime int64 13 | }{ 14 | "it should return the current time": { 15 | input: func() *time.Time { 16 | return nil 17 | }, 18 | expectedTime: now.Unix(), 19 | }, 20 | "it should return the provided time": { 21 | input: func() *time.Time { 22 | parsed, err := time.Parse(time.RFC3339, "2024-10-15T09:32:23Z") 23 | if err != nil { 24 | t.Errorf("timeParse unexpected error: %v", err) 25 | } 26 | return &parsed 27 | }, 28 | expectedTime: 1728984743, 29 | }, 30 | } 31 | 32 | for name, tc := range tt { 33 | t.Run(name, func(t *testing.T) { 34 | result, _, err := getTime(tc.input()) 35 | if err != nil { 36 | t.Errorf("getTime unexpected error: %v", err) 37 | } 38 | sec, _ := result.UnixTime() 39 | if sec != tc.expectedTime { 40 | t.Errorf("expected %v, got %v", tc.expectedTime, result) 41 | } 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /uuid/version1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "encoding/binary" 9 | ) 10 | 11 | // NewUUID returns a Version 1 UUID based on the current NodeID and clock 12 | // sequence, and the current time. If the NodeID has not been set by SetNodeID 13 | // or SetNodeInterface then it will be set automatically. If the NodeID cannot 14 | // be set NewUUID returns nil. If clock sequence has not been set by 15 | // SetClockSequence then it will be set automatically. If GetTime fails to 16 | // return the current NewUUID returns nil and an error. 17 | // 18 | // In most cases, New should be used. 19 | func NewUUID() (UUID, error) { 20 | var uuid UUID 21 | now, seq, err := GetTime() 22 | if err != nil { 23 | return uuid, err 24 | } 25 | 26 | timeLow := uint32(now & 0xffffffff) 27 | timeMid := uint16((now >> 32) & 0xffff) 28 | timeHi := uint16((now >> 48) & 0x0fff) 29 | timeHi |= 0x1000 // Version 1 30 | 31 | binary.BigEndian.PutUint32(uuid[0:], timeLow) 32 | binary.BigEndian.PutUint16(uuid[4:], timeMid) 33 | binary.BigEndian.PutUint16(uuid[6:], timeHi) 34 | binary.BigEndian.PutUint16(uuid[8:], seq) 35 | 36 | nodeMu.Lock() 37 | if nodeID == zeroID { 38 | setNodeInterface("") 39 | } 40 | copy(uuid[10:], nodeID[:]) 41 | nodeMu.Unlock() 42 | 43 | return uuid, nil 44 | } 45 | -------------------------------------------------------------------------------- /uuid/version1_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import "fmt" 4 | 5 | // This function is named ExampleNewUUID() 6 | // it with the Examples type. 7 | func ExampleNewUUID() { 8 | u, err := NewUUID() 9 | if err != nil { 10 | fmt.Println("error") 11 | return 12 | } 13 | fmt.Println(u.Version()) 14 | // Output: VERSION_1 15 | } 16 | -------------------------------------------------------------------------------- /uuid/version4_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // This function is named ExampleNew() 10 | // it with the Examples type. 11 | func ExampleNew() { 12 | u := New() 13 | fmt.Println(len(u), u.Version()) 14 | // Output: 16 VERSION_4 15 | } 16 | 17 | // This function is named ExampleNewString() 18 | // it with the Examples type. 19 | func ExampleNewString() { 20 | s := NewString() 21 | fmt.Println(len(s), strings.Count(s, "-")) 22 | // Output: 36 4 23 | } 24 | 25 | // This function is named ExampleNewRandom() 26 | // it with the Examples type. 27 | func ExampleNewRandom() { 28 | u, err := NewRandom() 29 | if err != nil { 30 | fmt.Println("error") 31 | return 32 | } 33 | fmt.Println(len(u), u.Version()) 34 | // Output: 16 VERSION_4 35 | } 36 | 37 | // This function is named ExampleNewRandomFromReader() 38 | // it with the Examples type. 39 | func ExampleNewRandomFromReader() { 40 | reader := bytes.NewReader([]byte("abcdefghijklmnopABCDEFGHIJKLMNOP")) 41 | u, err := NewRandomFromReader(reader) 42 | if err != nil { 43 | fmt.Println("error") 44 | return 45 | } 46 | fmt.Println(u.String()) 47 | } 48 | -------------------------------------------------------------------------------- /uuid/version6_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // This function is named ExampleNewV6() 9 | // it with the Examples type. 10 | func ExampleNewV6() { 11 | u, err := NewV6() 12 | if err != nil { 13 | fmt.Println("error") 14 | return 15 | } 16 | fmt.Println(u.Version(), u.Variant()) 17 | // Output: VERSION_6 RFC4122 18 | } 19 | 20 | // This function is named ExampleNewV6WithTime() 21 | // it with the Examples type. 22 | func ExampleNewV6WithTime() { 23 | t := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) 24 | u, err := NewV6WithTime(&t) 25 | if err != nil { 26 | fmt.Println("error") 27 | return 28 | } 29 | fmt.Println(u.Version(), u.Variant()) 30 | // Output: VERSION_6 RFC4122 31 | } 32 | -------------------------------------------------------------------------------- /uuid/version7_example_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | ) 7 | 8 | // This function is named ExampleNewV7() 9 | // it with the Examples type. 10 | func ExampleNewV7() { 11 | _, err := NewV7() 12 | //0195fd85-d68f-750a-b55b-6a0a82f17715 13 | if err != nil { 14 | fmt.Printf("error generating UUIDv7: %v\n", err) 15 | return 16 | } 17 | fmt.Printf("Generated UUIDv7") 18 | // Output: Generated UUIDv7 19 | } 20 | 21 | // This function is named ExampleNewV7FromReader() 22 | // it with the Examples type. 23 | func ExampleNewV7FromReader() { 24 | _, err := NewV7FromReader(rand.Reader) 25 | //0195fd88-13c5-7868-b042-f3aa076e28a7 26 | if err != nil { 27 | fmt.Printf("error generating UUIDv7 from reader: %v\n", err) 28 | return 29 | } 30 | fmt.Printf("Generated UUIDv7 from reader") 31 | // Output: Generated UUIDv7 from reader 32 | } 33 | --------------------------------------------------------------------------------