├── .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 |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  2 | 3 | 🚧 **Coming soon!** We’re working on it! 🛠️ -------------------------------------------------------------------------------- /example/middleware/healthcheck/README.md: -------------------------------------------------------------------------------- 1 | ## 🛠️ Healthcheck Middleware in Quick  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  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 |Lightning-fast web framework in Go
13 | 14 | 15 |Lightning-fast web framework in Go
13 | 14 | 15 |BASE HEADER
10 | {{ .yield }} 11 |BASE FOOTER
12 |{{ .Message }}
3 | -------------------------------------------------------------------------------- /example/template/embed/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |{{ .Message }}
3 | -------------------------------------------------------------------------------- /template/html/static/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |