Merge branch 'gin-gonic:master' into merge_json_render_write

This commit is contained in:
mstmdev 2022-06-30 20:12:23 +08:00 committed by GitHub
commit 501fc1e523
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 1053 additions and 383 deletions

View File

@ -37,7 +37,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -46,4 +46,4 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: '^1.16'
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v3.1.0
uses: golangci/golangci-lint-action@v3.2.0
with:
version: v1.45.0
args: --verbose
@ -28,7 +28,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: [1.14, 1.15, 1.16, 1.17, 1.18]
go: [1.15, 1.16, 1.17, 1.18]
test-tags: ['', nomsgpack]
include:
- os: ubuntu-latest
@ -43,7 +43,7 @@ jobs:
GOPROXY: https://proxy.golang.org
steps:
- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
@ -65,7 +65,7 @@ jobs:
run: make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
notification-gitter:

34
.github/workflows/goreleaser.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Goreleaser
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

57
.goreleaser.yaml Normal file
View File

@ -0,0 +1,57 @@
project_name: gin
builds:
-
# If true, skip the build.
# Useful for library projects.
# Default is false
skip: true
changelog:
# Set it to true if you wish to skip the changelog generation.
# This may result in an empty release notes on GitHub/GitLab/Gitea.
skip: false
# Changelog generation implementation to use.
#
# Valid options are:
# - `git`: uses `git log`;
# - `github`: uses the compare GitHub API, appending the author login to the changelog.
# - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog.
# - `github-native`: uses the GitHub release notes generation API, disables the groups feature.
#
# Defaults to `git`.
use: git
# Sorts the changelog by the commit's messages.
# Could either be asc, desc or empty
# Default is empty
sort: asc
# Group commits messages by given regex and title.
# Order value defines the order of the groups.
# Proving no regex means all commits will be grouped under the default group.
# Groups are disabled when using github-native, as it already groups things by itself.
#
# Default is no groups.
groups:
- title: Features
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: 'Bug fixes'
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: 'Enhancements'
regexp: "^.*chore[(\\w)]*:+.*$"
order: 2
- title: Others
order: 999
filters:
# Commit messages matching the regexp listed here will be removed from
# the changelog
# Default is empty
exclude:
- '^docs'
- 'CICD'
- typo

View File

@ -2,237 +2,405 @@ List of all the awesome people working to make Gin the best Web Framework in Go.
## gin 1.x series authors
**Gin Core Team:** Bo-Yi Wu (@appleboy), 田欧 (@thinkerou), Javier Provecho (@javierprovecho)
**Gin Core Team:** Bo-Yi Wu (@appleboy), thinkerou (@thinkerou), Javier Provecho (@javierprovecho)
## gin 0.x series authors
**Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
------
People and companies, who have contributed, in alphabetical order.
**@858806258 (杰哥)**
- Fix typo in example
**@achedeuzot (Klemen Sever)**
- Fix newline debug printing
**@adammck (Adam Mckaig)**
- Add MIT license
**@AlexanderChen1989 (Alexander)**
- Typos in README
**@alexanderdidenko (Aleksandr Didenko)**
- Add support multipart/form-data
**@alexandernyquist (Alexander Nyquist)**
- Using template.Must to fix multiple return issue
- ★ Added support for OPTIONS verb
- ★ Setting response headers before calling WriteHeader
- Improved documentation for model binding
- ★ Added Content.Redirect()
- ★ Added tons of Unit tests
**@austinheap (Austin Heap)**
- Added travis CI integration
**@andredublin (Andre Dublin)**
- Fix typo in comment
**@bredov (Ludwig Valda Vasquez)**
- Fix html templating in debug mode
**@bluele (Jun Kimura)**
- Fixes code examples in README
**@chad-russell**
- ★ Support for serializing gin.H into XML
**@dickeyxxx (Jeff Dickey)**
- Typos in README
- Add example about serving static files
**@donileo (Adonis)**
- Add NoMethod handler
**@dutchcoders (DutchCoders)**
- ★ Fix security bug that allows client to spoof ip
- Fix typo. r.HTMLTemplates -> SetHTMLTemplate
**@el3ctro- (Joshua Loper)**
- Fix typo in example
**@ethankan (Ethan Kan)**
- Unsigned integers in binding
**(Evgeny Persienko)**
- Validate sub structures
**@frankbille (Frank Bille)**
- Add support for HTTP Realm Auth
**@fmd (Fareed Dudhia)**
- Fix typo. SetHTTPTemplate -> SetHTMLTemplate
**@ironiridis (Christopher Harrington)**
- Remove old reference
**@jammie-stackhouse (Jamie Stackhouse)**
- Add more shortcuts for router methods
**@jasonrhansen**
- Fix spelling and grammar errors in documentation
**@JasonSoft (Jason Lee)**
- Fix typo in comment
**@jincheng9 (Jincheng Zhang)**
- ★ support TSR when wildcard follows named param
- Fix errors and typos in comments
**@joiggama (Ignacio Galindo)**
- Add utf-8 charset header on renders
**@julienschmidt (Julien Schmidt)**
- gofmt the code examples
**@kelcecil (Kel Cecil)**
- Fix readme typo
**@kyledinh (Kyle Dinh)**
- Adds RunTLS()
**@LinusU (Linus Unnebäck)**
- Small fixes in README
**@loongmxbt (Saint Asky)**
- Fix typo in example
**@lucas-clemente (Lucas Clemente)**
- ★ work around path.Join removing trailing slashes from routes
**@mattn (Yasuhiro Matsumoto)**
- Improve color logger
**@mdigger (Dmitry Sedykh)**
- Fixes Form binding when content-type is x-www-form-urlencoded
- No repeat call c.Writer.Status() in gin.Logger
- Fixes Content-Type for json render
**@mirzac (Mirza Ceric)**
- Fix debug printing
**@mopemope (Yutaka Matsubara)**
- ★ Adds Godep support (Dependencies Manager)
- Fix variadic parameter in the flexible render API
- Fix Corrupted plain render
- Add Pluggable View Renderer Example
**@msemenistyi (Mykyta Semenistyi)**
- update Readme.md. Add code to String method
**@msoedov (Sasha Myasoedov)**
- ★ Adds tons of unit tests.
**@ngerakines (Nick Gerakines)**
- ★ Improves API, c.GET() doesn't panic
- Adds MustGet() method
**@r8k (Rajiv Kilaparti)**
- Fix Port usage in README.
**@rayrod2030 (Ray Rodriguez)**
- Fix typo in example
**@rns**
- Fix typo in example
**@RobAWilkinson (Robert Wilkinson)**
- Add example of forms and params
**@rogierlommers (Rogier Lommers)**
- Add updated static serve example
**@rw-access (Ross Wolf)**
- Added support to mix exact and param routes
**@se77en (Damon Zhao)**
- Improve color logging
**@silasb (Silas Baronda)**
- Fixing quotes in README
**@SkuliOskarsson (Skuli Oskarsson)**
- Fixes some texts in README II
**@slimmy (Jimmy Pettersson)**
- Added messages for required bindings
**@smira (Andrey Smirnov)**
- Add support for ignored/unexported fields in binding
**@superalsrk (SRK.Lyu)**
- Update httprouter godeps
**@tebeka (Miki Tebeka)**
- Use net/http constants instead of numeric values
**@techjanitor**
- Update context.go reserved IPs
**@yosssi (Keiji Yoshida)**
- Fix link in README
**@yuyabee**
- Fixed README
- 178inaba <178inaba@users.noreply.github.com>
- A. F <hello@clivern.com>
- ABHISHEK SONI <abhishek.rocks26@gmail.com>
- Abhishek Chanda <achanda@users.noreply.github.com>
- Abner Chen <houjunchen@gmail.com>
- AcoNCodes <acongame@gmail.com>
- Adam Dratwinski <adam.dratwinski@gmail.com>
- Adam Mckaig <adam.mckaig@gmail.com>
- Adam Zielinski <MusicAdam@users.noreply.github.com>
- Adonis <donileo@gmail.com>
- Alan Wang <azzwacb9001@126.com>
- Albin Gilles <gilles.albin@gmail.com>
- Aleksandr Didenko <aa.didenko@yandex.ru>
- Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com>
- Alex <AWulkan@users.noreply.github.com>
- Alexander <alexanderchenmh@gmail.com>
- Alexander Lokhman <alex.lokhman@gmail.com>
- Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com>
- Alexander Nyquist <nyquist.alexander@gmail.com>
- Allen Ren <kulong0105@gmail.com>
- AllinGo <tanhp@outlook.com>
- Ammar Bandukwala <ammar@ammar.io>
- An Xiao (Luffy) <hac@zju.edu.cn>
- Andre Dublin <81dublin@gmail.com>
- Andrew Szeto <github@jabagawee.com>
- Andrey Abramov <andreyabramov.aaa@gmail.com>
- Andrey Nering <andrey.nering@gmail.com>
- Andrey Smirnov <Smirnov.Andrey@gmail.com>
- Andrii Bubis <firstrow@gmail.com>
- André Bazaglia <bazaglia@users.noreply.github.com>
- Andy Pan <panjf2000@gmail.com>
- Antoine GIRARD <sapk@users.noreply.github.com>
- Anup Kumar Panwar <1anuppanwar@gmail.com>
- Aravinth Sundaram <gosh.aravind@gmail.com>
- Artem <horechek@gmail.com>
- Ashwani <ashwanisharma686@gmail.com>
- Aurelien Regat-Barrel <arb@cyberkarma.net>
- Austin Heap <me@austinheap.com>
- Barnabus <jbampton@users.noreply.github.com>
- Bo-Yi Wu <appleboy.tw@gmail.com>
- Boris Borshevsky <BorisBorshevsky@gmail.com>
- Boyi Wu <p581581@gmail.com>
- BradyBromley <51128276+BradyBromley@users.noreply.github.com>
- Brendan Fosberry <brendan@shopkeep.com>
- Brian Wigginton <brianwigginton@gmail.com>
- Carlos Eduardo <carlosedp@gmail.com>
- Chad Russell <chaddouglasrussell@gmail.com>
- Charles <cxjava@gmail.com>
- Christian Muehlhaeuser <muesli@gmail.com>
- Christian Persson <saser@live.se>
- Christopher Harrington <ironiridis@gmail.com>
- Damon Zhao <yijun.zhao@outlook.com>
- Dan Markham <dmarkham@gmail.com>
- Dang Nguyen <hoangdang.me@gmail.com>
- Daniel Krom <kromdan@gmail.com>
- Daniel M. Lambea <dmlambea@gmail.com>
- Danieliu <liudanking@gmail.com>
- David Irvine <aviddiviner@gmail.com>
- David Zhang <crispgm@gmail.com>
- Davor Kapsa <dvrkps@users.noreply.github.com>
- DeathKing <DeathKing@users.noreply.github.com>
- Dennis Cho <47404603+forest747@users.noreply.github.com>
- Dmitry Dorogin <dmirogin@ya.ru>
- Dmitry Kutakov <vkd.castle@gmail.com>
- Dmitry Sedykh <dmitrys@d3h.local>
- Don2Quixote <35610661+Don2Quixote@users.noreply.github.com>
- Donn Pebe <iam@donnpebe.com>
- Dustin Decker <dustindecker@protonmail.com>
- Eason Lin <easonlin404@gmail.com>
- Edward Betts <edward@4angle.com>
- Egor Seredin <4819888+agmt@users.noreply.github.com>
- Emmanuel Goh <emmanuel@visenze.com>
- Equim <sayaka@ekyu.moe>
- Eren A. Akyol <eren@redmc.me>
- Eric_Lee <xplzv@126.com>
- Erik Bender <erik.bender@develerik.dev>
- Ethan Kan <ethankan@neoplot.com>
- Evgeny Persienko <e.persienko@office.ngs.ru>
- Faisal Alam <ifaisalalam@gmail.com>
- Fareed Dudhia <fareeddudhia@googlemail.com>
- Filip Figiel <figiel.filip@gmail.com>
- Florian Polster <couchpolster@icqmail.com>
- Frank Bille <github@frankbille.dk>
- Franz Bettag <franz@bett.ag>
- Ganlv <ganlvtech@users.noreply.github.com>
- Gaozhen Ying <yinggaozhen@hotmail.com>
- George Gabolaev <gabolaev98@gmail.com>
- George Kirilenko <necryin@users.noreply.github.com>
- Georges Varouchas <georges.varouchas@gmail.com>
- Gordon Tyler <gordon@doxxx.net>
- Harindu Perera <harinduenator@gmail.com>
- Helios <674876158@qq.com>
- Henry Kwan <piengeng@users.noreply.github.com>
- Henry Yee <henry@yearning.io>
- Himanshu Mishra <OrkoHunter@users.noreply.github.com>
- Hiroyuki Tanaka <h.tanaka.0325@gmail.com>
- Ibraheem Ahmed <ibrah1440@gmail.com>
- Ignacio Galindo <joiggama@gmail.com>
- Igor H. Vieira <zignd.igor@gmail.com>
- Ildar1111 <54001462+Ildar1111@users.noreply.github.com>
- Iskander (Alex) Sharipov <iskander.sharipov@intel.com>
- Ismail Gjevori <isgjevori@protonmail.com>
- Ivan Chen <allenivan@gmail.com>
- JINNOUCHI Yasushi <delphinus@remora.cx>
- James Pettyjohn <japettyjohn@users.noreply.github.com>
- Jamie Stackhouse <jamie.stackhouse@redspace.com>
- Jason Lee <jawc@hotmail.com>
- Javier Provecho <j.provecho@dartekstudios.com>
- Javier Provecho <javier.provecho@bq.com>
- Javier Provecho <javiertitan@gmail.com>
- Javier Provecho Fernandez <j.provecho@dartekstudios.com>
- Javier Provecho Fernandez <javiertitan@gmail.com>
- Jean-Christophe Lebreton <jclebreton@gmail.com>
- Jeff <laojianzi1994@gmail.com>
- Jeremy Loy <jeremy.b.loy@icloud.com>
- Jim Filippou <p3160253@aueb.gr>
- Jimmy Pettersson <jimmy@expertmaker.com>
- John Bampton <jbampton@users.noreply.github.com>
- Johnny Dallas <johnnydallas0308@gmail.com>
- Johnny Dallas <theonlyjohnny@theonlyjohnny.sh>
- Jonathan (JC) Chen <jc@dijonkitchen.org>
- Josep Jesus Bigorra Algaba <42377845+averageflow@users.noreply.github.com>
- Josh Horowitz <joshua.m.horowitz@gmail.com>
- Joshua Loper <josh.el3@gmail.com>
- Julien Schmidt <github@julienschmidt.com>
- Jun Kimura <jksmphone@gmail.com>
- Justin Beckwith <justin.beckwith@gmail.com>
- Justin Israel <justinisrael@gmail.com>
- Justin Mayhew <mayhew@live.ca>
- Jérôme Laforge <jerome-laforge@users.noreply.github.com>
- Kacper Bąk <56700396+53jk1@users.noreply.github.com>
- Kamron Batman <kamronbatman@users.noreply.github.com>
- Kane Rogers <kane@cleanstream.com.au>
- Kaushik Neelichetty <kaushikneelichetty6132@gmail.com>
- Keiji Yoshida <yoshida.keiji.84@gmail.com>
- Kel Cecil <kel.cecil@listhub.com>
- Kevin Mulvey <kmulvey@linux.com>
- Kevin Zhu <ipandtcp@gmail.com>
- Kirill Motkov <motkov.kirill@gmail.com>
- Klemen Sever <ksever@student.42.fr>
- Kristoffer A. Iversen <kristoffer.a.iversen@gmail.com>
- Krzysztof Szafrański <k.p.szafranski@gmail.com>
- Kumar McMillan <kumar.mcmillan@gmail.com>
- Kyle Mcgill <email@kylescottmcgill.com>
- Lanco <35420416+lancoLiu@users.noreply.github.com>
- Levi Olson <olson.levi@gmail.com>
- Lin Kao-Yuan <mosdeo@gmail.com>
- Linus Unnebäck <linus@folkdatorn.se>
- Lucas Clemente <lucas@clemente.io>
- Ludwig Valda Vasquez <bredov@gmail.com>
- Luis GG <lggomez@users.noreply.github.com>
- MW Lim <williamchange@gmail.com>
- Maksimov Sergey <konjoot@gmail.com>
- Manjusaka <lizheao940510@gmail.com>
- Manu MA <manu.mtza@gmail.com>
- Manu MA <manu.valladolid@gmail.com>
- Manu Mtz-Almeida <manu.valladolid@gmail.com>
- Manu Mtz.-Almeida <manu.valladolid@gmail.com>
- Manuel Alonso <manuelalonso@invisionapp.com>
- Mara Kim <hacker.root@gmail.com>
- Mario Kostelac <mario@intercom.io>
- Martin Karlsch <martin@karlsch.com>
- Matt Newberry <mnewberry@opentable.com>
- Matt Williams <gh@mattyw.net>
- Matthieu MOREL <mmorel-35@users.noreply.github.com>
- Max Hilbrunner <mhilbrunner@users.noreply.github.com>
- Maxime Soulé <btik-git@scoubidou.com>
- MetalBreaker <johnymichelson@gmail.com>
- Michael Puncel <mpuncel@squareup.com>
- MichaelDeSteven <51652084+MichaelDeSteven@users.noreply.github.com>
- Mike <38686456+icy4ever@users.noreply.github.com>
- Mike Stipicevic <mst@ableton.com>
- Miki Tebeka <miki.tebeka@gmail.com>
- Miles <MilesLin@users.noreply.github.com>
- Mirza Ceric <mirza.ceric@b2match.com>
- Mykyta Semenistyi <nikeiwe@gmail.com>
- Naoki Takano <honten@tinkermode.com>
- Ngalim Siregar <ngalim.siregar@gmail.com>
- Ni Hao <supernihaooo@qq.com>
- Nick Gerakines <nick@gerakines.net>
- Nikifor Seryakov <nikandfor@gmail.com>
- Notealot <714804968@qq.com>
- Olivier Mengué <dolmen@cpan.org>
- Olivier Robardet <orobardet@users.noreply.github.com>
- Pablo Moncada <pablo.moncada@bq.com>
- Pablo Moncada <pmoncadaisla@gmail.com>
- Panmax <967168@qq.com>
- Peperoncino <2wua4nlyi@gmail.com>
- Philipp Meinen <philipp@bind.ch>
- Pierre Massat <pierre@massat.io>
- Qt <golang.chen@gmail.com>
- Quentin ROYER <aydendevg@gmail.com>
- README Bot <35302948+codetriage-readme-bot@users.noreply.github.com>
- Rafal Zajac <rzajac@gmail.com>
- Rahul Datta Roy <rahuldroy@users.noreply.github.com>
- Rajiv Kilaparti <rajivk085@gmail.com>
- Raphael Gavache <raphael.gavache@datadoghq.com>
- Ray Rodriguez <rayrod2030@gmail.com>
- Regner Blok-Andersen <shadowdf@gmail.com>
- Remco <remco@dutchcoders.io>
- Rex Lee(李俊) <duguying2008@gmail.com>
- Richard Lee <dlackty@gmail.com>
- Riverside <wangyb65@gmail.com>
- Robert Wilkinson <wilkinson.robert.a@gmail.com>
- Rogier Lommers <rogier@lommers.org>
- Rohan Pai <me@rohanpai.com>
- Romain Beuque <rbeuque74@gmail.com>
- Roman Belyakovsky <ihryamzik@gmail.com>
- Roman Zaynetdinov <627197+zaynetro@users.noreply.github.com>
- Roman Zaynetdinov <roman.zaynetdinov@lekane.com>
- Ronald Petty <ronald.petty@rx-m.com>
- Ross Wolf <31489089+rw-access@users.noreply.github.com>
- Roy Lou <roylou@gmail.com>
- Rubi <14269809+codenoid@users.noreply.github.com>
- Ryan <46182144+ryanker@users.noreply.github.com>
- Ryan J. Yoder <me@ryanjyoder.com>
- SRK.Lyu <superalsrk@gmail.com>
- Sai <sairoutine@gmail.com>
- Samuel Abreu <sdepaula@gmail.com>
- Santhosh Kumar <santhoshkumarr1096@gmail.com>
- Sasha Melentyev <sasha@melentyev.io>
- Sasha Myasoedov <msoedov@gmail.com>
- Segev Finer <segev208@gmail.com>
- Sergey Egorov <egorovhome@gmail.com>
- Sergey Fedchenko <seregayoga@bk.ru>
- Sergey Gonimar <sergey.gonimar@gmail.com>
- Sergey Ponomarev <me@sergey-ponomarev.ru>
- Serica <943914044@qq.com>
- Shamus Taylor <Shamus03@me.com>
- Shilin Wang <jarvisfironman@gmail.com>
- Shuo <openset.wang@gmail.com>
- Skuli Oskarsson <skuli@codeiak.io>
- Snawoot <vladislav-ex-github@vm-0.com>
- Sridhar Ratnakumar <srid@srid.ca>
- Steeve Chailloux <steeve@chaahk.com>
- Sudhir Mishra <sudhirxps@gmail.com>
- Suhas Karanth <sudo-suhas@users.noreply.github.com>
- TaeJun Park <miking38@gmail.com>
- Tatsuya Hoshino <tatsuya7.hoshino7@gmail.com>
- Tevic <tevic.tt@gmail.com>
- Tevin Jeffrey <tev.jeffrey@gmail.com>
- The Gitter Badger <badger@gitter.im>
- Thibault Jamet <tjamet@users.noreply.github.com>
- Thomas Boerger <thomas@webhippie.de>
- Thomas Schaffer <loopfz@gmail.com>
- Tommy Chu <tommychu2256@gmail.com>
- Tudor Roman <tudurom@gmail.com>
- Uwe Dauernheim <djui@users.noreply.github.com>
- Valentine Oragbakosi <valentine13400@gmail.com>
- Vas N <pnvasanth@users.noreply.github.com>
- Vasilyuk Vasiliy <By-Vasiliy@users.noreply.github.com>
- Victor Castell <victor@victorcastell.com>
- Vince Yuan <vince.yuan@gmail.com>
- Vyacheslav Dubinin <vyacheslav.dubinin@gmail.com>
- Waynerv <ampedee@gmail.com>
- Weilin Shi <934587911@qq.com>
- Xudong Cai <fifsky@gmail.com>
- Yasuhiro Matsumoto <mattn.jp@gmail.com>
- Yehezkiel Syamsuhadi <ybs@ybs.im>
- Yoshiki Nakagawa <yyoshiki41@gmail.com>
- Yoshiyuki Kinjo <yskkin+github@gmail.com>
- Yue Yang <g1enyy0ung@gmail.com>
- ZYunH <zyunhjob@163.com>
- Zach Newburgh <zach.newburgh@gmail.com>
- Zasda Yusuf Mikail <zasdaym@gmail.com>
- ZhangYunHao <zyunhjob@163.com>
- ZhiFeng Hu <hufeng1987@gmail.com>
- Zhu Xi <zhuxi910511@163.com>
- a2tt <usera2tt@gmail.com>
- ahuigo <1781999+ahuigo@users.noreply.github.com>
- ali <anio@users.noreply.github.com>
- aljun <salameryy@163.com>
- andrea <crypto.andrea@protonmail.ch>
- andriikushch <andrii.kushch@gmail.com>
- anoty <anjunyou@foxmail.com>
- awkj <hzzbiu@gmail.com>
- axiaoxin <254606826@qq.com>
- bbiao <bbbiao@gmail.com>
- bestgopher <84328409@qq.com>
- betahu <zhong.wenhuang@foxmail.com>
- bigwheel <k.bigwheel+eng@gmail.com>
- bn4t <17193640+bn4t@users.noreply.github.com>
- bullgare <bullgare@gmail.com>
- chainhelen <chainhelen@gmail.com>
- chenyang929 <chenyang929code@gmail.com>
- chriswhelix <chris.williams@helix.com>
- collinmsn <4130944@qq.com>
- cssivision <cssivision@gmail.com>
- danielalves <alves.lopes.dan@gmail.com>
- delphinus <delphinus@remora.cx>
- dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- dickeyxxx <jeff@dickeyxxx.com>
- edebernis <emeric.debernis@gmail.com>
- error10 <error@ioerror.us>
- esplo <esplo@users.noreply.github.com>
- eudore <30709860+eudore@users.noreply.github.com>
- ffhelicopter <32922889+ffhelicopter@users.noreply.github.com>
- filikos <11477309+filikos@users.noreply.github.com>
- forging2012 <forging2012@users.noreply.github.com>
- goqihoo <goqihoo@gmail.com>
- grapeVine <treeui.old@gmail.com>
- guonaihong <guonaihong@qq.com>
- heige <daheige@users.noreply.github.com>
- heige <zhuwei313@hotmail.com>
- hellojukay <hellojukay@163.com>
- henrylee2cn <henrylee2cn@gmail.com>
- htobenothing <htobenothing@gmail.com>
- iamhesir <78344375+iamhesir@users.noreply.github.com>
- ijaa <kailiu2013@gmail.com>
- ishanray <ishan.iipm@gmail.com>
- ishanray <ishanray@users.noreply.github.com>
- itcloudy <272685110@qq.com>
- jarodsong6 <jarodsong6@gmail.com>
- jasonrhansen <jasonrodneyhansen@gmail.com>
- jincheng9 <perfume0607@gmail.com>
- joeADSP <75027008+joeADSP@users.noreply.github.com>
- junfengye <junfeng.yejf@gmail.com>
- kaiiak <aNxFi37X@outlook.com>
- kebo <kevinke2020@outlook.com>
- keke <19yamashita15@gmail.com>
- kishor kunal raj <68464660+kishorkunal-raj@users.noreply.github.com>
- kyledinh <kyledinh@gmail.com>
- lantw44 <lantw44@gmail.com>
- likakuli <1154584512@qq.com>
- linfangrong <linfangrong.liuxin@qq.com>
- linzi <873804682@qq.com>
- llgoer <yanghuxiao@vip.qq.com>
- long-road <13412081338@163.com>
- mbesancon <mathieu.besancon@gmail.com>
- mehdy <mehdy.khoshnoody@gmail.com>
- metal A-wing <freedom.awing.777@gmail.com>
- micanzhang <micanzhang@gmail.com>
- minarc <ragnhildmowinckel@gmail.com>
- mllu <mornlyn@gmail.com>
- mopemoepe <yutaka.matsubara@gmail.com>
- msoedov <msoedov@gmail.com>
- mstmdev <mstmdev@gmail.com>
- novaeye <fcoffee@gmail.com>
- olebedev <oolebedev@gmail.com>
- phithon <phith0n@users.noreply.github.com>
- pjgg <pablo.gonzalez.granados@gmail.com>
- qm012 <67568757+qm012@users.noreply.github.com>
- raymonder jin <rayjingithub@gmail.com>
- rns <ruslan.shvedov@gmail.com>
- root@andrea:~# <crypto.andrea@protonmail.ch>
- sekky0905 <20237968+sekky0905@users.noreply.github.com>
- senhtry <w169q169@gmail.com>
- shadrus <shadrus@gmail.com>
- silasb <silas.baronda@gmail.com>
- solos <lxl1217@gmail.com>
- songjiayang <songjiayang@users.noreply.github.com>
- sope <shenshouer@163.com>
- srt180 <30768686+srt180@users.noreply.github.com>
- stackerzzq <foo_stacker@yeah.net>
- sunshineplan <sunshineplan@users.noreply.github.com>
- syssam <s.y.s.sam.sys@gmail.com>
- techjanitor <puntme@gmail.com>
- techjanitor <techjanitor@users.noreply.github.com>
- thinkerou <thinkerou@gmail.com>
- thinkgo <49174849+thinkgos@users.noreply.github.com>
- tsirolnik <tsirolnik@users.noreply.github.com>
- tyltr <31768692+tylitianrui@users.noreply.github.com>
- vinhha96 <anhvinha1@gmail.com>
- voidman <retmain@foxmail.com>
- vz <vzvway@gmail.com>
- wei <wei840222@gmail.com>
- weibaohui <weibaohui@yeah.net>
- whirosan <whirosan@users.noreply.github.com>
- willnewrelic <will@newrelic.com>
- wssccc <wssccc@qq.com>
- wuhuizuo <wuhuizuo@126.com>
- xyb <xyb4638@gmail.com>
- y-yagi <yuuji.yaginuma@gmail.com>
- yiranzai <wuqingdzx@gmail.com>
- youzeliang <youzel@126.com>
- yugu <chenzilong_1227@foxmail.com>
- yuyabe <yuyabee@gmail.com>
- zebozhuang <zebozhuang@163.com>
- zero11-0203 <93071220+zero11-0203@users.noreply.github.com>
- zesani <7sin@outlook.co.th>
- zhanweidu <zhanweidu@163.com>
- zhing <zqwillseven@gmail.com>
- ziheng <zihenglv@gmail.com>
- zzjin <zzjin@users.noreply.github.com>
- 森 優太 <59682979+uta-mori@users.noreply.github.com>
- 杰哥 <858806258@qq.com>
- 涛叔 <hi@taoshu.in>
- 市民233 <mengrenxiong@gmail.com>
- 尹宝强 <wmdandme@gmail.com>
- 梦溪笔谈 <loongmxbt@gmail.com>
- 飞雪无情 <ls8707@gmail.com>
- 寻寻觅觅的Gopher <zoujh99@qq.com>

View File

@ -1,5 +1,53 @@
# Gin ChangeLog
## Gin v1.8.1
### ENHANCEMENTS
* feat(context): add ContextWithFallback feature flag [#3172](https://github.com/gin-gonic/gin/pull/3172)
## Gin v1.8.0
## Break Changes
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP`
* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751)
### BUGFIXES
* Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861)
* Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983)
* Fix: missing sameSite when do context.reset() [#3123](https://github.com/gin-gonic/gin/pull/3123)
### ENHANCEMENTS
* Use Header() instead of deprecated HeaderMap [#2694](https://github.com/gin-gonic/gin/pull/2694)
* RouterGroup.Handle regular match optimization of http method [#2685](https://github.com/gin-gonic/gin/pull/2685)
* Add support go-json, another drop-in json replacement [#2680](https://github.com/gin-gonic/gin/pull/2680)
* Use errors.New to replace fmt.Errorf will much better [#2707](https://github.com/gin-gonic/gin/pull/2707)
* Use Duration.Truncate for truncating precision [#2711](https://github.com/gin-gonic/gin/pull/2711)
* Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723)
* Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files)
* Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737)
* Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765)
* Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720)
* Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787)
* Fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() [#2769](https://github.com/gin-gonic/gin/pull/2769)
* Some codes optimize [#2830](https://github.com/gin-gonic/gin/pull/2830) [#2834](https://github.com/gin-gonic/gin/pull/2834) [#2838](https://github.com/gin-gonic/gin/pull/2838) [#2837](https://github.com/gin-gonic/gin/pull/2837) [#2788](https://github.com/gin-gonic/gin/pull/2788) [#2848](https://github.com/gin-gonic/gin/pull/2848) [#2851](https://github.com/gin-gonic/gin/pull/2851) [#2701](https://github.com/gin-gonic/gin/pull/2701)
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967)
* Test(route): expose performRequest func [#3012](https://github.com/gin-gonic/gin/pull/3012)
* Support h2c with prior knowledge [#1398](https://github.com/gin-gonic/gin/pull/1398)
* Feat attachment filename support utf8 [#3071](https://github.com/gin-gonic/gin/pull/3071)
* Feat: add StaticFileFS [#2749](https://github.com/gin-gonic/gin/pull/2749)
* Feat(context): return GIN Context from Value method [#2825](https://github.com/gin-gonic/gin/pull/2825)
* Feat: automatically SetMode to TestMode when run go test [#3139](https://github.com/gin-gonic/gin/pull/3139)
* Add TOML bining for gin [#3081](https://github.com/gin-gonic/gin/pull/3081)
* IPv6 add default trusted proxies [#3033](https://github.com/gin-gonic/gin/pull/3033)
### DOCS
* Add note about nomsgpack tag to the readme [#2703](https://github.com/gin-gonic/gin/pull/2703)
## Gin v1.7.7
### BUGFIXES

View File

@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
To install Gin package, you need to install Go and set your Go workspace first.
1. The first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin.
1. You first need [Go](https://golang.org/) installed (**version 1.15+ is required**), then you can use the below Go command to install Gin.
```sh
$ go get -u github.com/gin-gonic/gin
@ -114,12 +114,16 @@ $ cat example.go
```go
package main
import "github.com/gin-gonic/gin"
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
@ -300,7 +304,7 @@ func main() {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"status": "posted",
"message": message,
"nick": nick,
@ -570,7 +574,7 @@ func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
   router.Run(":8080")
@ -602,7 +606,7 @@ func main() {
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
router.Run(":8080")
@ -630,7 +634,7 @@ func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
router.Run(":8080")
@ -649,7 +653,7 @@ func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
router.Run(":8080")
@ -658,7 +662,7 @@ func main() {
### Model binding and validation
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz).
Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags).
@ -666,10 +670,10 @@ Note that you need to set the corresponding binding tag on all fields you want t
Also, Gin provides two sets of methods for binding:
- **Type** - Must bind
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindTOML`
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
- **Type** - Should bind
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`, `ShouldBindTOML`,
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
@ -848,6 +852,7 @@ package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
@ -870,7 +875,7 @@ func startPage(c *gin.Context) {
log.Println(person.Name)
log.Println(person.Address)
}
c.String(200, "Success")
c.String(http.StatusOK, "Success")
}
```
@ -884,6 +889,7 @@ package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
@ -916,7 +922,7 @@ func startPage(c *gin.Context) {
log.Println(person.UnixTime)
}
c.String(200, "Success")
c.String(http.StatusOK, "Success")
}
```
@ -932,7 +938,11 @@ See the [detail information](https://github.com/gin-gonic/gin/issues/846).
```go
package main
import "github.com/gin-gonic/gin"
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
ID string `uri:"id" binding:"required,uuid"`
@ -944,10 +954,10 @@ func main() {
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err.Error()})
c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run(":8088")
}
@ -966,6 +976,8 @@ package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
@ -980,11 +992,11 @@ func main() {
h := testHeader{}
if err := c.ShouldBindHeader(&h); err != nil {
c.JSON(200, err)
c.JSON(http.StatusOK, err)
}
fmt.Printf("%#v\n", h)
c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain})
})
r.Run()
@ -1014,7 +1026,7 @@ type myForm struct {
func formHandler(c *gin.Context) {
var fakeForm myForm
c.ShouldBind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
c.JSON(http.StatusOK, gin.H{"color": fakeForm.Colors})
}
...
@ -1219,14 +1231,14 @@ func main() {
// Serves unicode entities
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// Serves literal characters
r.GET("/purejson", func(c *gin.Context) {
c.PureJSON(200, gin.H{
c.PureJSON(http.StatusOK, gin.H{
"html": "<b>Hello, world!</b>",
})
})
@ -1473,7 +1485,7 @@ r.GET("/test", func(c *gin.Context) {
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
```
@ -1626,6 +1638,7 @@ package main
import (
"log"
"net/http"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
@ -1636,7 +1649,7 @@ func main() {
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
@ -1650,6 +1663,7 @@ package main
import (
"log"
"net/http"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
@ -1661,7 +1675,7 @@ func main() {
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
m := autocert.Manager{
@ -1922,7 +1936,7 @@ type StructD struct {
func GetDataB(c *gin.Context) {
var b StructB
c.Bind(&b)
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"a": b.NestedStruct,
"b": b.FieldB,
})
@ -1931,7 +1945,7 @@ func GetDataB(c *gin.Context) {
func GetDataC(c *gin.Context) {
var b StructC
c.Bind(&b)
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"a": b.NestedStructPointer,
"c": b.FieldC,
})
@ -1940,7 +1954,7 @@ func GetDataC(c *gin.Context) {
func GetDataD(c *gin.Context) {
var b StructD
c.Bind(&b)
c.JSON(200, gin.H{
c.JSON(http.StatusOK, gin.H{
"x": b.NestedAnonyStruct,
"d": b.FieldD,
})
@ -2090,6 +2104,7 @@ package main
import (
"html/template"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
@ -2118,7 +2133,7 @@ func main() {
log.Printf("Failed to push: %v", err)
}
}
c.HTML(200, "https", gin.H{
c.HTML(http.StatusOK, "https", gin.H{
"status": "success",
})
})
@ -2274,10 +2289,16 @@ The `net/http/httptest` package is preferable way for HTTP testing.
```go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
c.String(http.StatusOK, "pong")
})
return r
}
@ -2305,10 +2326,10 @@ func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
```

View File

@ -31,7 +31,7 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
return "", false
}
for _, pair := range a {
if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 {
return pair.user, true
}
}

View File

@ -22,6 +22,7 @@ const (
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
MIMETOML = "application/toml"
)
// Binding describes the interface which needs to be implemented for binding the
@ -83,6 +84,7 @@ var (
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
TOML = tomlBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
@ -103,6 +105,8 @@ func Default(method, contentType string) Binding {
return MsgPack
case MIMEYAML:
return YAML
case MIMETOML:
return TOML
case MIMEMultipartPOSTForm:
return FormMultipart
default: // case MIMEPOSTForm:

View File

@ -20,6 +20,7 @@ const (
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEYAML = "application/x-yaml"
MIMETOML = "application/toml"
)
// Binding describes the interface which needs to be implemented for binding the
@ -79,6 +80,7 @@ var (
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
TOML = tomlBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
@ -99,6 +101,8 @@ func Default(method, contentType string) Binding {
return YAML
case MIMEMultipartPOSTForm:
return FormMultipart
case MIMETOML:
return TOML
default: // case MIMEPOSTForm:
return Form
}

View File

@ -165,6 +165,9 @@ func TestBindingDefault(t *testing.T) {
assert.Equal(t, YAML, Default("POST", MIMEYAML))
assert.Equal(t, YAML, Default("PUT", MIMEYAML))
assert.Equal(t, TOML, Default("POST", MIMETOML))
assert.Equal(t, TOML, Default("PUT", MIMETOML))
}
func TestBindingJSONNilBody(t *testing.T) {
@ -454,6 +457,20 @@ func TestBindingXMLFail(t *testing.T) {
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
}
func TestBindingTOML(t *testing.T) {
testBodyBinding(t,
TOML, "toml",
"/", "/",
`foo="bar"`, `bar="foo"`)
}
func TestBindingTOMLFail(t *testing.T) {
testBodyBindingFail(t,
TOML, "toml",
"/", "/",
`foo=\n"bar"`, `bar="foo"`)
}
func TestBindingYAML(t *testing.T) {
testBodyBinding(t,
YAML, "yaml",
@ -1339,10 +1356,10 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
err := b.Bind(req, &obj)
assert.Error(t, err)
invalid_obj := FooStruct{}
invalidobj := FooStruct{}
req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`))
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = b.Bind(req, &invalid_obj)
err = b.Bind(req, &invalidobj)
assert.Error(t, err)
assert.Equal(t, err.Error(), "obj is not ProtoMessage")

View File

@ -1,3 +1,7 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (

View File

@ -1,3 +1,7 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (

35
binding/toml.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"io"
"net/http"
"github.com/pelletier/go-toml/v2"
)
type tomlBinding struct{}
func (tomlBinding) Name() string {
return "toml"
}
func decodeToml(r io.Reader, obj any) error {
decoder := toml.NewDecoder(r)
if err := decoder.Decode(obj); err != nil {
return err
}
return decoder.Decode(obj)
}
func (tomlBinding) Bind(req *http.Request, obj any) error {
return decodeToml(req.Body, obj)
}
func (tomlBinding) BindBody(body []byte, obj any) error {
return decodeToml(bytes.NewReader(body), obj)
}

22
binding/toml_test.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTOMLBindingBindBody(t *testing.T) {
var s struct {
Foo string `toml:"foo"`
}
tomlBody := `foo="FOO"`
err := tomlBinding{}.BindBody([]byte(tomlBody), &s)
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}

View File

@ -34,11 +34,15 @@ const (
MIMEPOSTForm = binding.MIMEPOSTForm
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
MIMEYAML = binding.MIMEYAML
MIMETOML = binding.MIMETOML
)
// BodyBytesKey indicates a default body bytes key.
const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
// ContextKey is the key that a Context returns itself for.
const ContextKey = "_gin-gonic/gin/contextkey"
// abortIndex represents a typical value used in abort functions.
const abortIndex int8 = math.MaxInt8 >> 1
@ -98,6 +102,7 @@ func (c *Context) reset() {
c.Accepted = nil
c.queryCache = nil
c.formCache = nil
c.sameSite = 0
*c.params = (*c.params)[:0]
*c.skippedNodes = (*c.skippedNodes)[:0]
}
@ -632,6 +637,11 @@ func (c *Context) BindYAML(obj any) error {
return c.MustBindWith(obj, binding.YAML)
}
// BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML).
func (c *Context) BindTOML(obj interface{}) error {
return c.MustBindWith(obj, binding.TOML)
}
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
func (c *Context) BindHeader(obj any) error {
return c.MustBindWith(obj, binding.Header)
@ -690,6 +700,11 @@ func (c *Context) ShouldBindYAML(obj any) error {
return c.ShouldBindWith(obj, binding.YAML)
}
// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).
func (c *Context) ShouldBindTOML(obj interface{}) error {
return c.ShouldBindWith(obj, binding.TOML)
}
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
func (c *Context) ShouldBindHeader(obj any) error {
return c.ShouldBindWith(obj, binding.Header)
@ -733,7 +748,7 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error
}
// ClientIP implements one best effort algorithm to return the real client IP.
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
// It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
// the remote IP (coming from Request.RemoteAddr) is returned.
@ -961,6 +976,11 @@ func (c *Context) YAML(code int, obj any) {
c.Render(code, render.YAML{Data: obj})
}
// TOML serializes the given struct as TOML into the response body.
func (c *Context) TOML(code int, obj interface{}) {
c.Render(code, render.TOML{Data: obj})
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj any) {
c.Render(code, render.ProtoBuf{Data: obj})
@ -1065,6 +1085,7 @@ type Negotiate struct {
XMLData any
YAMLData any
Data any
TOMLData any
}
// Negotiate calls different Render according to acceptable Accept format.
@ -1086,6 +1107,10 @@ func (c *Context) Negotiate(code int, config Negotiate) {
data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data)
case binding.MIMETOML:
data := chooseData(config.TOMLData, config.Data)
c.TOML(code, data)
default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
}
@ -1133,7 +1158,7 @@ func (c *Context) SetAccepted(formats ...string) {
// Deadline returns that there is no deadline (ok==false) when c.Request has no Context.
func (c *Context) Deadline() (deadline time.Time, ok bool) {
if c.Request == nil || c.Request.Context() == nil {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return
}
return c.Request.Context().Deadline()
@ -1141,7 +1166,7 @@ func (c *Context) Deadline() (deadline time.Time, ok bool) {
// Done returns nil (chan which will wait forever) when c.Request has no Context.
func (c *Context) Done() <-chan struct{} {
if c.Request == nil || c.Request.Context() == nil {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Done()
@ -1149,7 +1174,7 @@ func (c *Context) Done() <-chan struct{} {
// Err returns nil when c.Request has no Context.
func (c *Context) Err() error {
if c.Request == nil || c.Request.Context() == nil {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Err()
@ -1162,12 +1187,15 @@ func (c *Context) Value(key any) any {
if key == 0 {
return c.Request
}
if key == ContextKey {
return c
}
if keyAsString, ok := key.(string); ok {
if val, exists := c.Get(keyAsString); exists {
return val
}
}
if c.Request == nil || c.Request.Context() == nil {
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
return nil
}
return c.Request.Context().Value(key)

View File

@ -17,6 +17,16 @@ import (
"github.com/stretchr/testify/assert"
)
type interceptedWriter struct {
ResponseWriter
b *bytes.Buffer
}
func (i interceptedWriter) WriteHeader(code int) {
i.Header().Del("X-Test")
i.ResponseWriter.WriteHeader(code)
}
func TestContextFormFileFailed17(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
@ -31,3 +41,32 @@ func TestContextFormFileFailed17(t *testing.T) {
assert.Nil(t, f)
})
}
func TestInterceptedHeader(t *testing.T) {
w := httptest.NewRecorder()
c, r := CreateTestContext(w)
r.Use(func(c *Context) {
i := interceptedWriter{
ResponseWriter: c.Writer,
b: bytes.NewBuffer(nil),
}
c.Writer = i
c.Next()
c.Header("X-Test", "overridden")
c.Writer = i.ResponseWriter
})
r.GET("/", func(c *Context) {
c.Header("X-Test", "original")
c.Header("X-Test-2", "present")
c.String(http.StatusOK, "hello world")
})
c.Request = httptest.NewRequest("GET", "/", nil)
r.HandleContext(c)
// Result() has headers frozen when WriteHeaderNow() has been called
// Compared to this time, this is when the response headers will be flushed
// As response is flushed on c.String, the Header cannot be set by the first
// middleware. Assert this
assert.Equal(t, "", w.Result().Header.Get("X-Test"))
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
}

View File

@ -1773,6 +1773,23 @@ func TestContextShouldBindWithYAML(t *testing.T) {
assert.Equal(t, 0, w.Body.Len())
}
func TestContextShouldBindWithTOML(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
var obj struct {
Foo string `toml:"foo"`
Bar string `toml:"bar"`
}
assert.NoError(t, c.ShouldBindTOML(&obj))
assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, 0, w.Body.Len())
}
func TestContextBadAutoShouldBind(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
@ -1880,6 +1897,7 @@ func TestContextGolangContext(t *testing.T) {
assert.Equal(t, ti, time.Time{})
assert.False(t, ok)
assert.Equal(t, c.Value(0), c.Request)
assert.Equal(t, c.Value(ContextKey), c)
assert.Nil(t, c.Value("foo"))
c.Set("foo", "bar")
@ -2079,12 +2097,18 @@ func TestRemoteIPFail(t *testing.T) {
}
func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
deadline, ok := c.Deadline()
assert.Zero(t, deadline)
assert.False(t, ok)
c2 := &Context{}
c2, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c2.engine.ContextWithFallback = true
c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
d := time.Now().Add(time.Second)
ctx, cancel := context.WithDeadline(context.Background(), d)
@ -2096,10 +2120,16 @@ func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) {
}
func TestContextWithFallbackDoneFromRequestContext(t *testing.T) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
assert.Nil(t, c.Done())
c2 := &Context{}
c2, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c2.engine.ContextWithFallback = true
c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
ctx, cancel := context.WithCancel(context.Background())
c2.Request = c2.Request.WithContext(ctx)
@ -2108,10 +2138,16 @@ func TestContextWithFallbackDoneFromRequestContext(t *testing.T) {
}
func TestContextWithFallbackErrFromRequestContext(t *testing.T) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
assert.Nil(t, c.Err())
c2 := &Context{}
c2, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c2.engine.ContextWithFallback = true
c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
ctx, cancel := context.WithCancel(context.Background())
c2.Request = c2.Request.WithContext(ctx)
@ -2120,9 +2156,9 @@ func TestContextWithFallbackErrFromRequestContext(t *testing.T) {
assert.EqualError(t, c2.Err(), context.Canceled.Error())
}
type contextKey string
func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
type contextKey string
tests := []struct {
name string
getContextAndKey func() (*Context, any)
@ -2132,7 +2168,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
name: "c with struct context key",
getContextAndKey: func() (*Context, any) {
var key struct{}
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value"))
return c, key
@ -2142,7 +2180,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
{
name: "c with string context key",
getContextAndKey: func() (*Context, any) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
return c, contextKey("key")
@ -2152,7 +2192,10 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
{
name: "c with nil http.Request",
getContextAndKey: func() (*Context, any) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
c.Request = nil
return c, "key"
},
value: nil,
@ -2160,7 +2203,9 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
{
name: "c with nil http.Request.Context()",
getContextAndKey: func() (*Context, any) {
c := &Context{}
c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil)
return c, "key"
},
@ -2175,6 +2220,70 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
}
}
func TestContextCopyShouldNotCancel(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
ensureRequestIsOver := make(chan struct{})
wg := &sync.WaitGroup{}
r := New()
r.GET("/", func(ginctx *Context) {
wg.Add(1)
ginctx = ginctx.Copy()
// start async goroutine for calling srv
go func() {
defer wg.Done()
<-ensureRequestIsOver // ensure request is done
req, err := http.NewRequestWithContext(ginctx, http.MethodGet, srv.URL, nil)
must(err)
res, err := http.DefaultClient.Do(req)
if err != nil {
t.Error(fmt.Errorf("request error: %w", err))
return
}
if res.StatusCode != http.StatusOK {
t.Error(fmt.Errorf("unexpected status code: %s", res.Status))
}
}()
})
l, err := net.Listen("tcp", ":0")
must(err)
go func() {
s := &http.Server{
Handler: r,
}
must(s.Serve(l))
}()
addr := strings.Split(l.Addr().String(), ":")
res, err := http.Get(fmt.Sprintf("http://127.0.0.1:%s/", addr[len(addr)-1]))
if err != nil {
t.Error(fmt.Errorf("request error: %w", err))
return
}
close(ensureRequestIsOver)
if res.StatusCode != http.StatusOK {
t.Error(fmt.Errorf("unexpected status code: %s", res.Status))
return
}
wg.Wait()
}
func TestContextAddParam(t *testing.T) {
c := &Context{}
id := "id"

View File

@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
func debugPrintWARNINGDefault() {
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
debugPrint(`[WARNING] Now Gin requires Go 1.14+.
debugPrint(`[WARNING] Now Gin requires Go 1.15+.
`)
}

View File

@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
})
m, e := getMinVer(runtime.Version())
if e == nil && m <= ginSupportMinGoVer {
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.14+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.15+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
} else {
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
}

5
gin.go
View File

@ -147,6 +147,9 @@ type Engine struct {
// UseH2C enable h2c support.
UseH2C bool
// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
ContextWithFallback bool
delims render.Delims
secureJSONPrefix string
HTMLRender render.HTMLRender
@ -195,7 +198,7 @@ func New() *Engine {
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJSONPrefix: "while(1);",
trustedProxies: []string{"0.0.0.0/0"},
trustedProxies: []string{"0.0.0.0/0", "::/0"},
trustedCIDRs: defaultTrustedCIDRs,
}
engine.RouterGroup.engine = engine

View File

@ -15,6 +15,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"sync"
"testing"
"time"
@ -281,7 +282,16 @@ func TestFileDescriptor(t *testing.T) {
listener, err := net.ListenTCP("tcp", addr)
assert.NoError(t, err)
socketFile, err := listener.File()
if isWindows() {
// not supported by windows, it is unimplemented now
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
if socketFile == nil {
return
}
go func() {
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
@ -547,3 +557,7 @@ func TestTreeRunDynamicRouting(t *testing.T) {
testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found")
testRequest(t, ts.URL+"/something/secondthing/121", "404 Not Found")
}
func isWindows() bool {
return runtime.GOOS == "windows"
}

View File

@ -202,7 +202,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
assert.Equal(t, "Date: 2017/07/01", string(resp))
}
func init() {
@ -320,7 +320,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
assert.Equal(t, "Date: 2017/07/01", string(resp))
}
func TestAddRoute(t *testing.T) {

View File

@ -296,8 +296,8 @@ func TestShouldBindUri(t *testing.T) {
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
var person Person
assert.NoError(t, c.ShouldBindUri(&person))
assert.True(t, "" != person.Name)
assert.True(t, "" != person.ID)
assert.True(t, person.Name != "")
assert.True(t, person.ID != "")
c.String(http.StatusOK, "ShouldBindUri test OK")
})
@ -318,8 +318,8 @@ func TestBindUri(t *testing.T) {
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
var person Person
assert.NoError(t, c.BindUri(&person))
assert.True(t, "" != person.Name)
assert.True(t, "" != person.ID)
assert.True(t, person.Name != "")
assert.True(t, person.ID != "")
c.String(http.StatusOK, "BindUri test OK")
})

9
go.mod
View File

@ -5,13 +5,14 @@ go 1.18
require (
github.com/gin-contrib/sse v0.1.0
github.com/go-playground/validator/v10 v10.10.0
github.com/goccy/go-json v0.9.5
github.com/goccy/go-json v0.9.7
github.com/json-iterator/go v1.1.12
github.com/mattn/go-isatty v0.0.14
github.com/stretchr/testify v1.7.0
github.com/pelletier/go-toml/v2 v2.0.2
github.com/stretchr/testify v1.7.5
github.com/ugorji/go/codec v1.2.7
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
google.golang.org/protobuf v1.27.1
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v2 v2.4.0
)
@ -26,5 +27,5 @@ require (
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

19
go.sum
View File

@ -12,8 +12,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/goccy/go-json v0.9.5 h1:ooSMW526ZjK+EaL5elrSyN2EzIfi/3V0m4+HJEDYLik=
github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -36,6 +36,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -43,10 +45,14 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
@ -67,8 +73,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@ -77,5 +83,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -208,6 +208,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
// set dummy ClientIP
c.Request.Header.Set("X-Forwarded-For", "20.20.20.20")
gotKeys = c.Keys
time.Sleep(time.Millisecond)
})
PerformRequest(router, "GET", "/example?a=100")

View File

@ -5,6 +5,7 @@
package gin
import (
"flag"
"io"
"os"
@ -54,8 +55,12 @@ func init() {
// SetMode sets gin mode according to input string.
func SetMode(value string) {
if value == "" {
if flag.Lookup("test.v") != nil {
value = TestMode
} else {
value = DebugMode
}
}
switch value {
case DebugMode:

View File

@ -5,6 +5,7 @@
package gin
import (
"flag"
"os"
"testing"
@ -21,9 +22,16 @@ func TestSetMode(t *testing.T) {
assert.Equal(t, TestMode, Mode())
os.Unsetenv(EnvGinMode)
SetMode("")
assert.Equal(t, testCode, ginMode)
assert.Equal(t, TestMode, Mode())
tmp := flag.CommandLine
flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError)
SetMode("")
assert.Equal(t, debugCode, ginMode)
assert.Equal(t, DebugMode, Mode())
flag.CommandLine = tmp
SetMode(DebugMode)
assert.Equal(t, debugCode, ginMode)

View File

@ -30,6 +30,7 @@ var (
_ Render = Reader{}
_ Render = AsciiJSON{}
_ Render = ProtoBuf{}
_ Render = TOML{}
)
func writeContentType(w http.ResponseWriter, value []string) {

36
render/toml.go Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package render
import (
"net/http"
"github.com/pelletier/go-toml/v2"
)
// TOML contains the given interface object.
type TOML struct {
Data any
}
var TOMLContentType = []string{"application/toml; charset=utf-8"}
// Render (TOML) marshals the given interface object and writes data with custom ContentType.
func (r TOML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
bytes, err := toml.Marshal(r.Data)
if err != nil {
return err
}
_, err = w.Write(bytes)
return err
}
// WriteContentType (TOML) writes TOML ContentType for response.
func (r TOML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, TOMLContentType)
}

View File

@ -455,7 +455,7 @@ walk: // Outer loop for walking the tree
if !n.wildChild {
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
// the current node needs to roll back to last vaild skippedNode
// the current node needs to roll back to last valid skippedNode
if path != "/" {
for l := len(*skippedNodes); l > 0; {
skippedNode := (*skippedNodes)[l-1]
@ -572,7 +572,7 @@ walk: // Outer loop for walking the tree
if path == prefix {
// If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
// the current node needs to roll back to last vaild skippedNode
// the current node needs to roll back to last valid skippedNode
if n.handlers == nil && path != "/" {
for l := len(*skippedNodes); l > 0; {
skippedNode := (*skippedNodes)[l-1]

View File

@ -5,4 +5,4 @@
package gin
// Version is the current gin framework's version.
const Version = "v1.7.7"
const Version = "v1.8.1"