added cache feature on #memcached
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Paul 2023-08-13 08:42:44 +02:00
parent f79bc57d83
commit 5b0f744a75
75 changed files with 1972 additions and 327 deletions

View File

@ -1,6 +1,8 @@
package main
import (
"os"
"git.paulbsd.com/paulbsd/dip/src/config"
"git.paulbsd.com/paulbsd/dip/src/dip"
"git.paulbsd.com/paulbsd/dip/src/ws"
@ -18,5 +20,11 @@ func main() {
ws := ws.WS{}
ws.Page.Title = "Public IP Address Service"
dip.Init()
memcacheconn, ok := os.LookupEnv("MEMCACHECONN")
if ok {
dip.MemcacheConn = memcacheconn
}
ws.RunServer(config, Templates, Static)
}

13
go.mod
View File

@ -3,14 +3,15 @@ module git.paulbsd.com/paulbsd/dip
go 1.20
require (
github.com/labstack/echo/v4 v4.10.2
github.com/labstack/echo/v4 v4.11.1
github.com/mattn/go-colorable v0.1.13 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
)
require (
github.com/bradfitz/gomemcache v0.0.0-20230611145640-acc696258285
github.com/likexian/whois v1.15.0
github.com/likexian/whois-parser v1.24.8
github.com/oschwald/geoip2-golang v1.9.0
@ -21,9 +22,9 @@ require (
github.com/labstack/gommon v0.4.0 // indirect
github.com/likexian/gokit v0.25.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
)

82
go.sum
View File

@ -1,32 +1,18 @@
github.com/bradfitz/gomemcache v0.0.0-20230611145640-acc696258285 h1:Dr+ezPI5ivhMn/3WOoB86XzMhie146DNaBbhaQWZHMY=
github.com/bradfitz/gomemcache v0.0.0-20230611145640-acc696258285/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/likexian/gokit v0.25.9 h1:rzSQ/dP7Qw+QUzSuWlrLF0AtZS3Di6uO5yWOKhx2Gk4=
github.com/likexian/gokit v0.25.9/go.mod h1:oDDqJUcnnF9uAKuw54F7s6oEG+OJ7eallfDW2dq0A/o=
github.com/likexian/gokit v0.25.11 h1:o5H5JgVtBYBQn0QX30zE5+V8j2rgLTDoZWAO8xLFFfY=
github.com/likexian/gokit v0.25.11/go.mod h1:IKRQFPah2KQ0HL3iVjaOvFbx2apkSdFicGmsA0A4a1c=
github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
github.com/likexian/whois v1.14.4 h1:yNQVugAHfeoylzyhitcs0vaPksDduvzW46HLjqXhpCg=
github.com/likexian/whois v1.14.4/go.mod h1:ijMqjPbtafr5TB49U3Hs3BsJx/93ixhRi/8YnCSK0iY=
github.com/likexian/whois v1.14.6 h1:chNF6xkUShMQHq+5tiovVOQFadb9hqrWcaqbtlvbe3o=
github.com/likexian/whois v1.14.6/go.mod h1:upfX0X3o78fvHyFY2zHv46zooPoydUV1msyovvLZTMU=
github.com/likexian/whois v1.15.0 h1:AYYJ5bNUo8Qy2T1Z5GgMp1oIcIlCcTDfg1buYz6TdAE=
github.com/likexian/whois v1.15.0/go.mod h1:456fUTkh+O8F8v09bGdVl7XxBjRaQ4LvYHyVWX5Bxyg=
github.com/likexian/whois-parser v1.24.2 h1:EaNwcXqWBfQPfLD5F6MxAA0o7FnzmGcN3mg7v7h0zfY=
github.com/likexian/whois-parser v1.24.2/go.mod h1:fT0m/HCa4PXuR4ddA5PIE9V7gTWldeiMG/AHpeAhLtg=
github.com/likexian/whois-parser v1.24.6 h1:Zaz4dEDxtLaz9Ot8jEdni4xUHqYqt/6DAhfrZmsUd9s=
github.com/likexian/whois-parser v1.24.6/go.mod h1:MJN8IUeQ55VSG2OCB01a88T6V6VQm/UYxKLQySSqdSc=
github.com/likexian/whois-parser v1.24.7 h1:UF36WfIXXu4BMwaUmWk6YbzolgpzWTGY0JQ4wZ/kumE=
github.com/likexian/whois-parser v1.24.7/go.mod h1:MJN8IUeQ55VSG2OCB01a88T6V6VQm/UYxKLQySSqdSc=
github.com/likexian/whois-parser v1.24.8 h1:41RCpnRXSvlo9U7vPuLORi187UkTURH+8HXUcGdnhZA=
github.com/likexian/whois-parser v1.24.8/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@ -34,75 +20,35 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -1,11 +1,13 @@
package dip
import (
"encoding/json"
"fmt"
"log"
"net"
"git.paulbsd.com/paulbsd/dip/src/geoip"
"github.com/bradfitz/gomemcache/memcache"
"github.com/labstack/echo/v4"
"github.com/likexian/whois"
whoisparser "github.com/likexian/whois-parser"
@ -13,9 +15,11 @@ import (
)
const defaultLanguage = "en"
const cacheMaxTime = 604800
var citydb *geoip2.Reader
var asndb *geoip2.Reader
var MemcacheConn string = ""
func Init() (err error) {
citydb, asndb, err = geoip.InitGeoIP()
@ -26,13 +30,37 @@ func Init() (err error) {
}
// GetIPInfo returns IP address informations
func (ip *IP) GetIPInfo(c echo.Context) (err error) {
func (ip *IP) GetIPInfo(c echo.Context) (cached bool, err error) {
var mcenabled bool
var mc *memcache.Client
if MemcacheConn != "" {
mc = memcache.New(MemcacheConn)
mcenabled = true
defer mc.Close()
}
if c.Param("ip") != "" {
ip.IP = c.Param("ip")
} else {
ip.IP = c.RealIP()
}
if mcenabled {
item, err := mc.Get(ip.IP)
if err != nil {
log.Println(ip.IP, err)
} else {
cached = true
cachedip := IP{}
err = json.Unmarshal(item.Value, &cachedip)
if err != nil {
log.Println(err)
}
*ip = cachedip
return cached, err
}
}
err = ip.CheckIPAddress()
if err != nil {
ip.IP = c.RealIP()
@ -52,6 +80,14 @@ func (ip *IP) GetIPInfo(c echo.Context) (err error) {
log.Println(err)
}
if mcenabled {
dt, err := json.Marshal(*ip)
err = mc.Set(&memcache.Item{Key: ip.IP, Value: dt, Expiration: cacheMaxTime})
if err != nil {
log.Println(err)
}
}
/*err = ip.GetWhois()
if err != nil {
log.Println(err)

View File

@ -8,9 +8,9 @@ import (
"github.com/labstack/echo/v4"
)
func (p *Page) GetContent(c echo.Context) (err error) {
func (p *Page) GetContent(c echo.Context) (cached bool, err error) {
var ip dip.IP
err = ip.GetIPInfo(c)
cached, err = ip.GetIPInfo(c)
p.IP = &ip
return
@ -18,13 +18,17 @@ func (p *Page) GetContent(c echo.Context) (err error) {
// Process returns main webpage or a JSON
func (p *Page) Process(c echo.Context, querytype string) (err error) {
p.GetContent(c)
cached, err := p.GetContent(c)
c.Response().Header().Set("X-Cached", "false")
if cached {
c.Response().Header().Set("X-Cached", "true")
}
if querytype == "json" {
return c.JSONPretty(http.StatusOK, p.IP, " ")
return c.JSON(http.StatusOK, p.IP)
}
if accept, ok := c.Request().Header["Accept"]; ok {
if accept[0] == "*/*" {
return c.JSONPretty(http.StatusOK, p.IP, " ")
return c.JSON(http.StatusOK, p.IP)
}
}
return c.Render(http.StatusOK, "index.html", p)

202
vendor/github.com/bradfitz/gomemcache/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,769 @@
/*
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package memcache provides a client for the memcached cache server.
package memcache
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
)
// Similar to:
// https://godoc.org/google.golang.org/appengine/memcache
var (
// ErrCacheMiss means that a Get failed because the item wasn't present.
ErrCacheMiss = errors.New("memcache: cache miss")
// ErrCASConflict means that a CompareAndSwap call failed due to the
// cached value being modified between the Get and the CompareAndSwap.
// If the cached value was simply evicted rather than replaced,
// ErrNotStored will be returned instead.
ErrCASConflict = errors.New("memcache: compare-and-swap conflict")
// ErrNotStored means that a conditional write operation (i.e. Add or
// CompareAndSwap) failed because the condition was not satisfied.
ErrNotStored = errors.New("memcache: item not stored")
// ErrServer means that a server error occurred.
ErrServerError = errors.New("memcache: server error")
// ErrNoStats means that no statistics were available.
ErrNoStats = errors.New("memcache: no statistics available")
// ErrMalformedKey is returned when an invalid key is used.
// Keys must be at maximum 250 bytes long and not
// contain whitespace or control characters.
ErrMalformedKey = errors.New("malformed: key is too long or contains invalid characters")
// ErrNoServers is returned when no servers are configured or available.
ErrNoServers = errors.New("memcache: no servers configured or available")
)
const (
// DefaultTimeout is the default socket read/write timeout.
DefaultTimeout = 100 * time.Millisecond
// DefaultMaxIdleConns is the default maximum number of idle connections
// kept for any single address.
DefaultMaxIdleConns = 2
)
const buffered = 8 // arbitrary buffered channel size, for readability
// resumableError returns true if err is only a protocol-level cache error.
// This is used to determine whether or not a server connection should
// be re-used or not. If an error occurs, by default we don't reuse the
// connection, unless it was just a cache error.
func resumableError(err error) bool {
switch err {
case ErrCacheMiss, ErrCASConflict, ErrNotStored, ErrMalformedKey:
return true
}
return false
}
func legalKey(key string) bool {
if len(key) > 250 {
return false
}
for i := 0; i < len(key); i++ {
if key[i] <= ' ' || key[i] == 0x7f {
return false
}
}
return true
}
var (
crlf = []byte("\r\n")
space = []byte(" ")
resultOK = []byte("OK\r\n")
resultStored = []byte("STORED\r\n")
resultNotStored = []byte("NOT_STORED\r\n")
resultExists = []byte("EXISTS\r\n")
resultNotFound = []byte("NOT_FOUND\r\n")
resultDeleted = []byte("DELETED\r\n")
resultEnd = []byte("END\r\n")
resultOk = []byte("OK\r\n")
resultTouched = []byte("TOUCHED\r\n")
resultClientErrorPrefix = []byte("CLIENT_ERROR ")
versionPrefix = []byte("VERSION")
)
// New returns a memcache client using the provided server(s)
// with equal weight. If a server is listed multiple times,
// it gets a proportional amount of weight.
func New(server ...string) *Client {
ss := new(ServerList)
ss.SetServers(server...)
return NewFromSelector(ss)
}
// NewFromSelector returns a new Client using the provided ServerSelector.
func NewFromSelector(ss ServerSelector) *Client {
return &Client{selector: ss}
}
// Client is a memcache client.
// It is safe for unlocked use by multiple concurrent goroutines.
type Client struct {
// DialContext connects to the address on the named network
// using the provided context
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
// Timeout specifies the socket read/write timeout.
// If zero, DefaultTimeout is used.
Timeout time.Duration
// MaxIdleConns specifies the maximum number of idle connections that will
// be maintained per address. If less than one, DefaultMaxIdleConns will be
// used.
//
// Consider your expected traffic rates and latency carefully. This should
// be set to a number higher than your peak parallel requests.
MaxIdleConns int
selector ServerSelector
lk sync.Mutex
freeconn map[string][]*conn
}
// Item is an item to be got or stored in a memcached server.
type Item struct {
// Key is the Item's key (250 bytes maximum).
Key string
// Value is the Item's value.
Value []byte
// Flags are server-opaque flags whose semantics are entirely
// up to the app.
Flags uint32
// Expiration is the cache expiration time, in seconds: either a relative
// time from now (up to 1 month), or an absolute Unix epoch time.
// Zero means the Item has no expiration time.
Expiration int32
// Compare and swap ID.
casid uint64
}
// conn is a connection to a server.
type conn struct {
nc net.Conn
rw *bufio.ReadWriter
addr net.Addr
c *Client
}
// release returns this connection back to the client's free pool
func (cn *conn) release() {
cn.c.putFreeConn(cn.addr, cn)
}
func (cn *conn) extendDeadline() {
cn.nc.SetDeadline(time.Now().Add(cn.c.netTimeout()))
}
// condRelease releases this connection if the error pointed to by err
// is nil (not an error) or is only a protocol level error (e.g. a
// cache miss). The purpose is to not recycle TCP connections that
// are bad.
func (cn *conn) condRelease(err *error) {
if *err == nil || resumableError(*err) {
cn.release()
} else {
cn.nc.Close()
}
}
func (c *Client) putFreeConn(addr net.Addr, cn *conn) {
c.lk.Lock()
defer c.lk.Unlock()
if c.freeconn == nil {
c.freeconn = make(map[string][]*conn)
}
freelist := c.freeconn[addr.String()]
if len(freelist) >= c.maxIdleConns() {
cn.nc.Close()
return
}
c.freeconn[addr.String()] = append(freelist, cn)
}
func (c *Client) getFreeConn(addr net.Addr) (cn *conn, ok bool) {
c.lk.Lock()
defer c.lk.Unlock()
if c.freeconn == nil {
return nil, false
}
freelist, ok := c.freeconn[addr.String()]
if !ok || len(freelist) == 0 {
return nil, false
}
cn = freelist[len(freelist)-1]
c.freeconn[addr.String()] = freelist[:len(freelist)-1]
return cn, true
}
func (c *Client) netTimeout() time.Duration {
if c.Timeout != 0 {
return c.Timeout
}
return DefaultTimeout
}
func (c *Client) maxIdleConns() int {
if c.MaxIdleConns > 0 {
return c.MaxIdleConns
}
return DefaultMaxIdleConns
}
// ConnectTimeoutError is the error type used when it takes
// too long to connect to the desired host. This level of
// detail can generally be ignored.
type ConnectTimeoutError struct {
Addr net.Addr
}
func (cte *ConnectTimeoutError) Error() string {
return "memcache: connect timeout to " + cte.Addr.String()
}
func (c *Client) dial(addr net.Addr) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), c.netTimeout())
defer cancel()
dialerContext := c.DialContext
if dialerContext == nil {
dialer := net.Dialer{
Timeout: c.netTimeout(),
}
dialerContext = dialer.DialContext
}
nc, err := dialerContext(ctx, addr.Network(), addr.String())
if err == nil {
return nc, nil
}
if ne, ok := err.(net.Error); ok && ne.Timeout() {
return nil, &ConnectTimeoutError{addr}
}
return nil, err
}
func (c *Client) getConn(addr net.Addr) (*conn, error) {
cn, ok := c.getFreeConn(addr)
if ok {
cn.extendDeadline()
return cn, nil
}
nc, err := c.dial(addr)
if err != nil {
return nil, err
}
cn = &conn{
nc: nc,
addr: addr,
rw: bufio.NewReadWriter(bufio.NewReader(nc), bufio.NewWriter(nc)),
c: c,
}
cn.extendDeadline()
return cn, nil
}
func (c *Client) onItem(item *Item, fn func(*Client, *bufio.ReadWriter, *Item) error) error {
addr, err := c.selector.PickServer(item.Key)
if err != nil {
return err
}
cn, err := c.getConn(addr)
if err != nil {
return err
}
defer cn.condRelease(&err)
if err = fn(c, cn.rw, item); err != nil {
return err
}
return nil
}
func (c *Client) FlushAll() error {
return c.selector.Each(c.flushAllFromAddr)
}
// Get gets the item for the given key. ErrCacheMiss is returned for a
// memcache cache miss. The key must be at most 250 bytes in length.
func (c *Client) Get(key string) (item *Item, err error) {
err = c.withKeyAddr(key, func(addr net.Addr) error {
return c.getFromAddr(addr, []string{key}, func(it *Item) { item = it })
})
if err == nil && item == nil {
err = ErrCacheMiss
}
return
}
// Touch updates the expiry for the given key. The seconds parameter is either
// a Unix timestamp or, if seconds is less than 1 month, the number of seconds
// into the future at which time the item will expire. Zero means the item has
// no expiration time. ErrCacheMiss is returned if the key is not in the cache.
// The key must be at most 250 bytes in length.
func (c *Client) Touch(key string, seconds int32) (err error) {
return c.withKeyAddr(key, func(addr net.Addr) error {
return c.touchFromAddr(addr, []string{key}, seconds)
})
}
func (c *Client) withKeyAddr(key string, fn func(net.Addr) error) (err error) {
if !legalKey(key) {
return ErrMalformedKey
}
addr, err := c.selector.PickServer(key)
if err != nil {
return err
}
return fn(addr)
}
func (c *Client) withAddrRw(addr net.Addr, fn func(*bufio.ReadWriter) error) (err error) {
cn, err := c.getConn(addr)
if err != nil {
return err
}
defer cn.condRelease(&err)
return fn(cn.rw)
}
func (c *Client) withKeyRw(key string, fn func(*bufio.ReadWriter) error) error {
return c.withKeyAddr(key, func(addr net.Addr) error {
return c.withAddrRw(addr, fn)
})
}
func (c *Client) getFromAddr(addr net.Addr, keys []string, cb func(*Item)) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
if _, err := fmt.Fprintf(rw, "gets %s\r\n", strings.Join(keys, " ")); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
if err := parseGetResponse(rw.Reader, cb); err != nil {
return err
}
return nil
})
}
// flushAllFromAddr send the flush_all command to the given addr
func (c *Client) flushAllFromAddr(addr net.Addr) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
if _, err := fmt.Fprintf(rw, "flush_all\r\n"); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultOk):
break
default:
return fmt.Errorf("memcache: unexpected response line from flush_all: %q", string(line))
}
return nil
})
}
// ping sends the version command to the given addr
func (c *Client) ping(addr net.Addr) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.HasPrefix(line, versionPrefix):
break
default:
return fmt.Errorf("memcache: unexpected response line from ping: %q", string(line))
}
return nil
})
}
func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
for _, key := range keys {
if _, err := fmt.Fprintf(rw, "touch %s %d\r\n", key, expiration); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultTouched):
break
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
default:
return fmt.Errorf("memcache: unexpected response line from touch: %q", string(line))
}
}
return nil
})
}
// GetMulti is a batch version of Get. The returned map from keys to
// items may have fewer elements than the input slice, due to memcache
// cache misses. Each key must be at most 250 bytes in length.
// If no error is returned, the returned map will also be non-nil.
func (c *Client) GetMulti(keys []string) (map[string]*Item, error) {
var lk sync.Mutex
m := make(map[string]*Item)
addItemToMap := func(it *Item) {
lk.Lock()
defer lk.Unlock()
m[it.Key] = it
}
keyMap := make(map[net.Addr][]string)
for _, key := range keys {
if !legalKey(key) {
return nil, ErrMalformedKey
}
addr, err := c.selector.PickServer(key)
if err != nil {
return nil, err
}
keyMap[addr] = append(keyMap[addr], key)
}
ch := make(chan error, buffered)
for addr, keys := range keyMap {
go func(addr net.Addr, keys []string) {
ch <- c.getFromAddr(addr, keys, addItemToMap)
}(addr, keys)
}
var err error
for _ = range keyMap {
if ge := <-ch; ge != nil {
err = ge
}
}
return m, err
}
// parseGetResponse reads a GET response from r and calls cb for each
// read and allocated Item
func parseGetResponse(r *bufio.Reader, cb func(*Item)) error {
for {
line, err := r.ReadSlice('\n')
if err != nil {
return err
}
if bytes.Equal(line, resultEnd) {
return nil
}
it := new(Item)
size, err := scanGetResponseLine(line, it)
if err != nil {
return err
}
it.Value = make([]byte, size+2)
_, err = io.ReadFull(r, it.Value)
if err != nil {
it.Value = nil
return err
}
if !bytes.HasSuffix(it.Value, crlf) {
it.Value = nil
return fmt.Errorf("memcache: corrupt get result read")
}
it.Value = it.Value[:size]
cb(it)
}
}
// scanGetResponseLine populates it and returns the declared size of the item.
// It does not read the bytes of the item.
func scanGetResponseLine(line []byte, it *Item) (size int, err error) {
pattern := "VALUE %s %d %d %d\r\n"
dest := []interface{}{&it.Key, &it.Flags, &size, &it.casid}
if bytes.Count(line, space) == 3 {
pattern = "VALUE %s %d %d\r\n"
dest = dest[:3]
}
n, err := fmt.Sscanf(string(line), pattern, dest...)
if err != nil || n != len(dest) {
return -1, fmt.Errorf("memcache: unexpected line in get response: %q", line)
}
return size, nil
}
// Set writes the given item, unconditionally.
func (c *Client) Set(item *Item) error {
return c.onItem(item, (*Client).set)
}
func (c *Client) set(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "set", item)
}
// Add writes the given item, if no value already exists for its
// key. ErrNotStored is returned if that condition is not met.
func (c *Client) Add(item *Item) error {
return c.onItem(item, (*Client).add)
}
func (c *Client) add(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "add", item)
}
// Replace writes the given item, but only if the server *does*
// already hold data for this key
func (c *Client) Replace(item *Item) error {
return c.onItem(item, (*Client).replace)
}
func (c *Client) replace(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "replace", item)
}
// Append appends the given item to the existing item, if a value already
// exists for its key. ErrNotStored is returned if that condition is not met.
func (c *Client) Append(item *Item) error {
return c.onItem(item, (*Client).append)
}
func (c *Client) append(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "append", item)
}
// Prepend prepends the given item to the existing item, if a value already
// exists for its key. ErrNotStored is returned if that condition is not met.
func (c *Client) Prepend(item *Item) error {
return c.onItem(item, (*Client).prepend)
}
func (c *Client) prepend(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "prepend", item)
}
// CompareAndSwap writes the given item that was previously returned
// by Get, if the value was neither modified or evicted between the
// Get and the CompareAndSwap calls. The item's Key should not change
// between calls but all other item fields may differ. ErrCASConflict
// is returned if the value was modified in between the
// calls. ErrNotStored is returned if the value was evicted in between
// the calls.
func (c *Client) CompareAndSwap(item *Item) error {
return c.onItem(item, (*Client).cas)
}
func (c *Client) cas(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "cas", item)
}
func (c *Client) populateOne(rw *bufio.ReadWriter, verb string, item *Item) error {
if !legalKey(item.Key) {
return ErrMalformedKey
}
var err error
if verb == "cas" {
_, err = fmt.Fprintf(rw, "%s %s %d %d %d %d\r\n",
verb, item.Key, item.Flags, item.Expiration, len(item.Value), item.casid)
} else {
_, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n",
verb, item.Key, item.Flags, item.Expiration, len(item.Value))
}
if err != nil {
return err
}
if _, err = rw.Write(item.Value); err != nil {
return err
}
if _, err := rw.Write(crlf); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultStored):
return nil
case bytes.Equal(line, resultNotStored):
return ErrNotStored
case bytes.Equal(line, resultExists):
return ErrCASConflict
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
}
return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line))
}
func writeReadLine(rw *bufio.ReadWriter, format string, args ...interface{}) ([]byte, error) {
_, err := fmt.Fprintf(rw, format, args...)
if err != nil {
return nil, err
}
if err := rw.Flush(); err != nil {
return nil, err
}
line, err := rw.ReadSlice('\n')
return line, err
}
func writeExpectf(rw *bufio.ReadWriter, expect []byte, format string, args ...interface{}) error {
line, err := writeReadLine(rw, format, args...)
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultOK):
return nil
case bytes.Equal(line, expect):
return nil
case bytes.Equal(line, resultNotStored):
return ErrNotStored
case bytes.Equal(line, resultExists):
return ErrCASConflict
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
}
return fmt.Errorf("memcache: unexpected response line: %q", string(line))
}
// Delete deletes the item with the provided key. The error ErrCacheMiss is
// returned if the item didn't already exist in the cache.
func (c *Client) Delete(key string) error {
return c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
return writeExpectf(rw, resultDeleted, "delete %s\r\n", key)
})
}
// DeleteAll deletes all items in the cache.
func (c *Client) DeleteAll() error {
return c.withKeyRw("", func(rw *bufio.ReadWriter) error {
return writeExpectf(rw, resultDeleted, "flush_all\r\n")
})
}
// Ping checks all instances if they are alive. Returns error if any
// of them is down.
func (c *Client) Ping() error {
return c.selector.Each(c.ping)
}
// Increment atomically increments key by delta. The return value is
// the new value after being incremented or an error. If the value
// didn't exist in memcached the error is ErrCacheMiss. The value in
// memcached must be an decimal number, or an error will be returned.
// On 64-bit overflow, the new value wraps around.
func (c *Client) Increment(key string, delta uint64) (newValue uint64, err error) {
return c.incrDecr("incr", key, delta)
}
// Decrement atomically decrements key by delta. The return value is
// the new value after being decremented or an error. If the value
// didn't exist in memcached the error is ErrCacheMiss. The value in
// memcached must be an decimal number, or an error will be returned.
// On underflow, the new value is capped at zero and does not wrap
// around.
func (c *Client) Decrement(key string, delta uint64) (newValue uint64, err error) {
return c.incrDecr("decr", key, delta)
}
func (c *Client) incrDecr(verb, key string, delta uint64) (uint64, error) {
var val uint64
err := c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
line, err := writeReadLine(rw, "%s %s %d\r\n", verb, key, delta)
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
case bytes.HasPrefix(line, resultClientErrorPrefix):
errMsg := line[len(resultClientErrorPrefix) : len(line)-2]
return errors.New("memcache: client error: " + string(errMsg))
}
val, err = strconv.ParseUint(string(line[:len(line)-2]), 10, 64)
if err != nil {
return err
}
return nil
})
return val, err
}
// Close closes any open connections.
//
// It returns the first error encountered closing connections, but always
// closes all connections.
//
// After Close, the Client may still be used.
func (c *Client) Close() error {
c.lk.Lock()
defer c.lk.Unlock()
var ret error
for _, conns := range c.freeconn {
for _, c := range conns {
if err := c.nc.Close(); err != nil && ret == nil {
ret = err
}
}
}
c.freeconn = nil
return ret
}

View File

@ -0,0 +1,129 @@
/*
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package memcache
import (
"hash/crc32"
"net"
"strings"
"sync"
)
// ServerSelector is the interface that selects a memcache server
// as a function of the item's key.
//
// All ServerSelector implementations must be safe for concurrent use
// by multiple goroutines.
type ServerSelector interface {
// PickServer returns the server address that a given item
// should be shared onto.
PickServer(key string) (net.Addr, error)
Each(func(net.Addr) error) error
}
// ServerList is a simple ServerSelector. Its zero value is usable.
type ServerList struct {
mu sync.RWMutex
addrs []net.Addr
}
// staticAddr caches the Network() and String() values from any net.Addr.
type staticAddr struct {
ntw, str string
}
func newStaticAddr(a net.Addr) net.Addr {
return &staticAddr{
ntw: a.Network(),
str: a.String(),
}
}
func (s *staticAddr) Network() string { return s.ntw }
func (s *staticAddr) String() string { return s.str }
// SetServers changes a ServerList's set of servers at runtime and is
// safe for concurrent use by multiple goroutines.
//
// Each server is given equal weight. A server is given more weight
// if it's listed multiple times.
//
// SetServers returns an error if any of the server names fail to
// resolve. No attempt is made to connect to the server. If any error
// is returned, no changes are made to the ServerList.
func (ss *ServerList) SetServers(servers ...string) error {
naddr := make([]net.Addr, len(servers))
for i, server := range servers {
if strings.Contains(server, "/") {
addr, err := net.ResolveUnixAddr("unix", server)
if err != nil {
return err
}
naddr[i] = newStaticAddr(addr)
} else {
tcpaddr, err := net.ResolveTCPAddr("tcp", server)
if err != nil {
return err
}
naddr[i] = newStaticAddr(tcpaddr)
}
}
ss.mu.Lock()
defer ss.mu.Unlock()
ss.addrs = naddr
return nil
}
// Each iterates over each server calling the given function
func (ss *ServerList) Each(f func(net.Addr) error) error {
ss.mu.RLock()
defer ss.mu.RUnlock()
for _, a := range ss.addrs {
if err := f(a); nil != err {
return err
}
}
return nil
}
// keyBufPool returns []byte buffers for use by PickServer's call to
// crc32.ChecksumIEEE to avoid allocations. (but doesn't avoid the
// copies, which at least are bounded in size and small)
var keyBufPool = sync.Pool{
New: func() interface{} {
b := make([]byte, 256)
return &b
},
}
func (ss *ServerList) PickServer(key string) (net.Addr, error) {
ss.mu.RLock()
defer ss.mu.RUnlock()
if len(ss.addrs) == 0 {
return nil, ErrNoServers
}
if len(ss.addrs) == 1 {
return ss.addrs[0], nil
}
bufp := keyBufPool.Get().(*[]byte)
n := copy(*bufp, key)
cs := crc32.ChecksumIEEE((*bufp)[:n])
keyBufPool.Put(bufp)
return ss.addrs[cs%uint32(len(ss.addrs))], nil
}

View File

@ -1,5 +1,38 @@
# Changelog
## v4.11.1 - 2023-07-16
**Fixes**
* Fix `Gzip` middleware not sending response code for no content responses (404, 301/302 redirects etc) [#2481](https://github.com/labstack/echo/pull/2481)
## v4.11.0 - 2023-07-14
**Fixes**
* Fixes the proxy middleware concurrency issue of calling the Next() proxy target on Round Robin Balancer [#2409](https://github.com/labstack/echo/pull/2409)
* Fix `group.RouteNotFound` not working when group has attached middlewares [#2411](https://github.com/labstack/echo/pull/2411)
* Fix global error handler return error message when message is an error [#2456](https://github.com/labstack/echo/pull/2456)
* Do not use global timeNow variables [#2477](https://github.com/labstack/echo/pull/2477)
**Enhancements**
* Added a optional config variable to disable centralized error handler in recovery middleware [#2410](https://github.com/labstack/echo/pull/2410)
* refactor: use `strings.ReplaceAll` directly [#2424](https://github.com/labstack/echo/pull/2424)
* Add support for Go1.20 `http.rwUnwrapper` to Response struct [#2425](https://github.com/labstack/echo/pull/2425)
* Check whether is nil before invoking centralized error handling [#2429](https://github.com/labstack/echo/pull/2429)
* Proper colon support in `echo.Reverse` method [#2416](https://github.com/labstack/echo/pull/2416)
* Fix misuses of a vs an in documentation comments [#2436](https://github.com/labstack/echo/pull/2436)
* Add link to slog.Handler library for Echo logging into README.md [#2444](https://github.com/labstack/echo/pull/2444)
* In proxy middleware Support retries of failed proxy requests [#2414](https://github.com/labstack/echo/pull/2414)
* gofmt fixes to comments [#2452](https://github.com/labstack/echo/pull/2452)
* gzip response only if it exceeds a minimal length [#2267](https://github.com/labstack/echo/pull/2267)
* Upgrade packages [#2475](https://github.com/labstack/echo/pull/2475)
## v4.10.2 - 2023-02-22
**Security**

View File

@ -110,6 +110,7 @@ of middlewares in this list.
| [github.com/swaggo/echo-swagger](https://github.com/swaggo/echo-swagger) | Automatically generate RESTful API documentation with [Swagger](https://swagger.io/) 2.0. |
| [github.com/ziflex/lecho](https://github.com/ziflex/lecho) | [Zerolog](https://github.com/rs/zerolog) logging library wrapper for Echo logger interface. |
| [github.com/brpaz/echozap](https://github.com/brpaz/echozap) | Uber´s [Zap](https://github.com/uber-go/zap) logging library wrapper for Echo logger interface. |
| [github.com/samber/slog-echo](https://github.com/samber/slog-echo) | Go [slog](https://pkg.go.dev/golang.org/x/exp/slog) logging library wrapper for Echo logger interface. |
| [github.com/darkweak/souin/plugins/echo](https://github.com/darkweak/souin/tree/master/plugins/echo) | HTTP cache system based on [Souin](https://github.com/darkweak/souin) to automatically get your endpoints cached. It supports some distributed and non-distributed storage systems depending your needs. |
| [github.com/mikestefanello/pagoda](https://github.com/mikestefanello/pagoda) | Rapid, easy full-stack web development starter kit built with Echo. |
| [github.com/go-woo/protoc-gen-echo](https://github.com/go-woo/protoc-gen-echo) | ProtoBuf generate Echo server side code |

View File

@ -114,7 +114,7 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
// Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
// For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
// The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670)
method := c.Request().Method
method := c.Request().Method
if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
if err = b.BindQueryParams(c, i); err != nil {
return err

View File

@ -1236,7 +1236,7 @@ func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]tim
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Second)
}
@ -1247,7 +1247,7 @@ func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Second)
}
@ -1257,7 +1257,7 @@ func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBi
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Millisecond)
}
@ -1268,7 +1268,7 @@ func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueB
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Millisecond)
}
@ -1280,8 +1280,8 @@ func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *Va
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Nanosecond)
}
@ -1294,8 +1294,8 @@ func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBi
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
//
// Note:
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
func (b *ValueBinder) MustUnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Nanosecond)
}

View File

@ -100,8 +100,8 @@ type (
// Set saves data in the context.
Set(key string, val interface{})
// Bind binds the request body into provided type `i`. The default binder
// does it based on Content-Type header.
// Bind binds path params, query params and the request body into provided type `i`. The default binder
// binds body based on Content-Type header.
Bind(i interface{}) error
// Validate validates provided `i`. It is usually called after `Context#Bind()`.

View File

@ -39,6 +39,7 @@ package echo
import (
stdContext "context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
@ -258,7 +259,7 @@ const (
const (
// Version of Echo
Version = "4.10.2"
Version = "4.11.1"
website = "https://echo.labstack.com"
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
banner = `
@ -438,12 +439,18 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
// Issue #1426
code := he.Code
message := he.Message
if m, ok := he.Message.(string); ok {
switch m := he.Message.(type) {
case string:
if e.Debug {
message = Map{"message": m, "error": err.Error()}
} else {
message = Map{"message": m}
}
case json.Marshaler:
// do nothing - this type knows how to format itself to JSON
case error:
message = Map{"message": m.Error()}
}
// Send response
@ -614,7 +621,7 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
return e.URI(h, params...)
}
// Reverse generates an URL from route name and provided parameters.
// Reverse generates a URL from route name and provided parameters.
func (e *Echo) Reverse(name string, params ...interface{}) string {
return e.router.Reverse(name, params...)
}

View File

@ -23,10 +23,12 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
if len(g.middleware) == 0 {
return
}
// Allow all requests to reach the group as they might get dropped if router
// doesn't find a match, making none of the group middleware process.
g.Any("", NotFoundHandler)
g.Any("/*", NotFoundHandler)
// group level middlewares are different from Echo `Pre` and `Use` middlewares (those are global). Group level middlewares
// are only executed if they are added to the Router with route.
// So we register catch all route (404 is a safe way to emulate route match) for this group and now during routing the
// Router would find route to match our request path and therefore guarantee the middleware(s) will get executed.
g.RouteNotFound("", NotFoundHandler)
g.RouteNotFound("/*", NotFoundHandler)
}
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.

View File

@ -2,9 +2,9 @@ package middleware
import (
"encoding/base64"
"net/http"
"strconv"
"strings"
"net/http"
"github.com/labstack/echo/v4"
)

View File

@ -2,6 +2,7 @@ package middleware
import (
"bufio"
"bytes"
"compress/gzip"
"io"
"net"
@ -21,12 +22,30 @@ type (
// Gzip compression level.
// Optional. Default value -1.
Level int `yaml:"level"`
// Length threshold before gzip compression is applied.
// Optional. Default value 0.
//
// Most of the time you will not need to change the default. Compressing
// a short response might increase the transmitted data because of the
// gzip format overhead. Compressing the response will also consume CPU
// and time on the server and the client (for decompressing). Depending on
// your use case such a threshold might be useful.
//
// See also:
// https://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits
MinLength int
}
gzipResponseWriter struct {
io.Writer
http.ResponseWriter
wroteBody bool
wroteHeader bool
wroteBody bool
minLength int
minLengthExceeded bool
buffer *bytes.Buffer
code int
}
)
@ -37,8 +56,9 @@ const (
var (
// DefaultGzipConfig is the default Gzip middleware config.
DefaultGzipConfig = GzipConfig{
Skipper: DefaultSkipper,
Level: -1,
Skipper: DefaultSkipper,
Level: -1,
MinLength: 0,
}
)
@ -58,8 +78,12 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
if config.Level == 0 {
config.Level = DefaultGzipConfig.Level
}
if config.MinLength < 0 {
config.MinLength = DefaultGzipConfig.MinLength
}
pool := gzipCompressPool(config)
bpool := bufferPool()
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
@ -70,7 +94,6 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
res := c.Response()
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
i := pool.Get()
w, ok := i.(*gzip.Writer)
if !ok {
@ -78,19 +101,38 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
}
rw := res.Writer
w.Reset(rw)
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
buf := bpool.Get().(*bytes.Buffer)
buf.Reset()
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw, minLength: config.MinLength, buffer: buf}
defer func() {
// There are different reasons for cases when we have not yet written response to the client and now need to do so.
// a) handler response had only response code and no response body (ala 404 or redirects etc). Response code need to be written now.
// b) body is shorter than our minimum length threshold and being buffered currently and needs to be written
if !grw.wroteBody {
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
res.Header().Del(echo.HeaderContentEncoding)
}
if grw.wroteHeader {
rw.WriteHeader(grw.code)
}
// We have to reset response to it's pristine state when
// nothing is written to body or error is returned.
// See issue #424, #407.
res.Writer = rw
w.Reset(io.Discard)
} else if !grw.minLengthExceeded {
// Write uncompressed response
res.Writer = rw
if grw.wroteHeader {
grw.ResponseWriter.WriteHeader(grw.code)
}
grw.buffer.WriteTo(rw)
w.Reset(io.Discard)
}
w.Close()
bpool.Put(buf)
pool.Put(w)
}()
res.Writer = grw
@ -102,7 +144,11 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
func (w *gzipResponseWriter) WriteHeader(code int) {
w.Header().Del(echo.HeaderContentLength) // Issue #444
w.ResponseWriter.WriteHeader(code)
w.wroteHeader = true
// Delay writing of the header until we know if we'll actually compress the response
w.code = code
}
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
@ -110,10 +156,40 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) {
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
}
w.wroteBody = true
if !w.minLengthExceeded {
n, err := w.buffer.Write(b)
if w.buffer.Len() >= w.minLength {
w.minLengthExceeded = true
// The minimum length is exceeded, add Content-Encoding header and write the header
w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
if w.wroteHeader {
w.ResponseWriter.WriteHeader(w.code)
}
return w.Writer.Write(w.buffer.Bytes())
}
return n, err
}
return w.Writer.Write(b)
}
func (w *gzipResponseWriter) Flush() {
if !w.minLengthExceeded {
// Enforce compression because we will not know how much more data will come
w.minLengthExceeded = true
w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
if w.wroteHeader {
w.ResponseWriter.WriteHeader(w.code)
}
w.Writer.Write(w.buffer.Bytes())
}
w.Writer.(*gzip.Writer).Flush()
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
@ -142,3 +218,12 @@ func gzipCompressPool(config GzipConfig) sync.Pool {
},
}
}
func bufferPool() sync.Pool {
return sync.Pool{
New: func() interface{} {
b := &bytes.Buffer{}
return b
},
}
}

View File

@ -150,8 +150,8 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
allowOriginPatterns := []string{}
for _, origin := range config.AllowOrigins {
pattern := regexp.QuoteMeta(origin)
pattern = strings.Replace(pattern, "\\*", ".*", -1)
pattern = strings.Replace(pattern, "\\?", ".", -1)
pattern = strings.ReplaceAll(pattern, "\\*", ".*")
pattern = strings.ReplaceAll(pattern, "\\?", ".")
pattern = "^" + pattern + "$"
allowOriginPatterns = append(allowOriginPatterns, pattern)
}

View File

@ -20,7 +20,7 @@ type (
}
)
//GZIPEncoding content-encoding header if set to "gzip", decompress body contents.
// GZIPEncoding content-encoding header if set to "gzip", decompress body contents.
const GZIPEncoding string = "gzip"
// Decompressor is used to get the sync.Pool used by the middleware to get Gzip readers
@ -44,12 +44,12 @@ func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
}
//Decompress decompresses request body based if content encoding type is set to "gzip" with default config
// Decompress decompresses request body based if content encoding type is set to "gzip" with default config
func Decompress() echo.MiddlewareFunc {
return DecompressWithConfig(DefaultDecompressConfig)
}
//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
// DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
// Defaults
if config.Skipper == nil {

View File

@ -38,9 +38,9 @@ func rewriteRulesRegex(rewrite map[string]string) map[*regexp.Regexp]string {
rulesRegex := map[*regexp.Regexp]string{}
for k, v := range rewrite {
k = regexp.QuoteMeta(k)
k = strings.Replace(k, `\*`, "(.*?)", -1)
k = strings.ReplaceAll(k, `\*`, "(.*?)")
if strings.HasPrefix(k, `\^`) {
k = strings.Replace(k, `\^`, "^", -1)
k = strings.ReplaceAll(k, `\^`, "^")
}
k = k + "$"
rulesRegex[regexp.MustCompile(k)] = v

View File

@ -12,7 +12,6 @@ import (
"regexp"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/labstack/echo/v4"
@ -30,6 +29,33 @@ type (
// Required.
Balancer ProxyBalancer
// RetryCount defines the number of times a failed proxied request should be retried
// using the next available ProxyTarget. Defaults to 0, meaning requests are never retried.
RetryCount int
// RetryFilter defines a function used to determine if a failed request to a
// ProxyTarget should be retried. The RetryFilter will only be called when the number
// of previous retries is less than RetryCount. If the function returns true, the
// request will be retried. The provided error indicates the reason for the request
// failure. When the ProxyTarget is unavailable, the error will be an instance of
// echo.HTTPError with a Code of http.StatusBadGateway. In all other cases, the error
// will indicate an internal error in the Proxy middleware. When a RetryFilter is not
// specified, all requests that fail with http.StatusBadGateway will be retried. A custom
// RetryFilter can be provided to only retry specific requests. Note that RetryFilter is
// only called when the request to the target fails, or an internal error in the Proxy
// middleware has occurred. Successful requests that return a non-200 response code cannot
// be retried.
RetryFilter func(c echo.Context, e error) bool
// ErrorHandler defines a function which can be used to return custom errors from
// the Proxy middleware. ErrorHandler is only invoked when there has been
// either an internal error in the Proxy middleware or the ProxyTarget is
// unavailable. Due to the way requests are proxied, ErrorHandler is not invoked
// when a ProxyTarget returns a non-200 response. In these cases, the response
// is already written so errors cannot be modified. ErrorHandler is only
// invoked after all retry attempts have been exhausted.
ErrorHandler func(c echo.Context, err error) error
// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
// retrieved by index e.g. $1, $2 and so on.
// Examples:
@ -72,26 +98,28 @@ type (
Next(echo.Context) *ProxyTarget
}
// TargetProvider defines an interface that gives the opportunity for balancer to return custom errors when selecting target.
// TargetProvider defines an interface that gives the opportunity for balancer
// to return custom errors when selecting target.
TargetProvider interface {
NextTarget(echo.Context) (*ProxyTarget, error)
}
commonBalancer struct {
targets []*ProxyTarget
mutex sync.RWMutex
mutex sync.Mutex
}
// RandomBalancer implements a random load balancing technique.
randomBalancer struct {
*commonBalancer
commonBalancer
random *rand.Rand
}
// RoundRobinBalancer implements a round-robin load balancing technique.
roundRobinBalancer struct {
*commonBalancer
i uint32
commonBalancer
// tracking the index on `targets` slice for the next `*ProxyTarget` to be used
i int
}
)
@ -107,14 +135,14 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
in, _, err := c.Response().Hijack()
if err != nil {
c.Set("_error", fmt.Sprintf("proxy raw, hijack error=%v, url=%s", t.URL, err))
c.Set("_error", fmt.Errorf("proxy raw, hijack error=%w, url=%s", err, t.URL))
return
}
defer in.Close()
out, err := net.Dial("tcp", t.URL.Host)
if err != nil {
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err)))
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", err, t.URL)))
return
}
defer out.Close()
@ -122,7 +150,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
// Write header
err = r.Write(out)
if err != nil {
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err)))
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", err, t.URL)))
return
}
@ -136,39 +164,44 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
go cp(in, out)
err = <-errCh
if err != nil && err != io.EOF {
c.Set("_error", fmt.Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err))
c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err, t.URL))
}
})
}
// NewRandomBalancer returns a random proxy balancer.
func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
b := &randomBalancer{commonBalancer: new(commonBalancer)}
b := randomBalancer{}
b.targets = targets
return b
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
return &b
}
// NewRoundRobinBalancer returns a round-robin proxy balancer.
func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
b := &roundRobinBalancer{commonBalancer: new(commonBalancer)}
b := roundRobinBalancer{}
b.targets = targets
return b
return &b
}
// AddTarget adds an upstream target to the list.
// AddTarget adds an upstream target to the list and returns `true`.
//
// However, if a target with the same name already exists then the operation is aborted returning `false`.
func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
b.mutex.Lock()
defer b.mutex.Unlock()
for _, t := range b.targets {
if t.Name == target.Name {
return false
}
}
b.mutex.Lock()
defer b.mutex.Unlock()
b.targets = append(b.targets, target)
return true
}
// RemoveTarget removes an upstream target from the list.
// RemoveTarget removes an upstream target from the list by name.
//
// Returns `true` on success, `false` if no target with the name is found.
func (b *commonBalancer) RemoveTarget(name string) bool {
b.mutex.Lock()
defer b.mutex.Unlock()
@ -182,21 +215,58 @@ func (b *commonBalancer) RemoveTarget(name string) bool {
}
// Next randomly returns an upstream target.
//
// Note: `nil` is returned in case upstream target list is empty.
func (b *randomBalancer) Next(c echo.Context) *ProxyTarget {
if b.random == nil {
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
b.mutex.Lock()
defer b.mutex.Unlock()
if len(b.targets) == 0 {
return nil
} else if len(b.targets) == 1 {
return b.targets[0]
}
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.targets[b.random.Intn(len(b.targets))]
}
// Next returns an upstream target using round-robin technique.
// Next returns an upstream target using round-robin technique. In the case
// where a previously failed request is being retried, the round-robin
// balancer will attempt to use the next target relative to the original
// request. If the list of targets held by the balancer is modified while a
// failed request is being retried, it is possible that the balancer will
// return the original failed target.
//
// Note: `nil` is returned in case upstream target list is empty.
func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget {
b.i = b.i % uint32(len(b.targets))
t := b.targets[b.i]
atomic.AddUint32(&b.i, 1)
return t
b.mutex.Lock()
defer b.mutex.Unlock()
if len(b.targets) == 0 {
return nil
} else if len(b.targets) == 1 {
return b.targets[0]
}
var i int
const lastIdxKey = "_round_robin_last_index"
// This request is a retry, start from the index of the previous
// target to ensure we don't attempt to retry the request with
// the same failed target
if c.Get(lastIdxKey) != nil {
i = c.Get(lastIdxKey).(int)
i++
if i >= len(b.targets) {
i = 0
}
} else {
// This is a first time request, use the global index
if b.i >= len(b.targets) {
b.i = 0
}
i = b.i
b.i++
}
c.Set(lastIdxKey, i)
return b.targets[i]
}
// Proxy returns a Proxy middleware.
@ -211,14 +281,26 @@ func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
// ProxyWithConfig returns a Proxy middleware with config.
// See: `Proxy()`
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
if config.Balancer == nil {
panic("echo: proxy middleware requires balancer")
}
// Defaults
if config.Skipper == nil {
config.Skipper = DefaultProxyConfig.Skipper
}
if config.Balancer == nil {
panic("echo: proxy middleware requires balancer")
if config.RetryFilter == nil {
config.RetryFilter = func(c echo.Context, e error) bool {
if httpErr, ok := e.(*echo.HTTPError); ok {
return httpErr.Code == http.StatusBadGateway
}
return false
}
}
if config.ErrorHandler == nil {
config.ErrorHandler = func(c echo.Context, err error) error {
return err
}
}
if config.Rewrite != nil {
if config.RegexRewrite == nil {
config.RegexRewrite = make(map[*regexp.Regexp]string)
@ -229,28 +311,17 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
}
provider, isTargetProvider := config.Balancer.(TargetProvider)
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
return func(c echo.Context) error {
if config.Skipper(c) {
return next(c)
}
req := c.Request()
res := c.Response()
var tgt *ProxyTarget
if isTargetProvider {
tgt, err = provider.NextTarget(c)
if err != nil {
return err
}
} else {
tgt = config.Balancer.Next(c)
}
c.Set(config.ContextKey, tgt)
if err := rewriteURL(config.RegexRewrite, req); err != nil {
return err
return config.ErrorHandler(c, err)
}
// Fix header
@ -266,19 +337,49 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
}
// Proxy
switch {
case c.IsWebSocket():
proxyRaw(tgt, c).ServeHTTP(res, req)
case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
default:
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
}
if e, ok := c.Get("_error").(error); ok {
err = e
}
retries := config.RetryCount
for {
var tgt *ProxyTarget
var err error
if isTargetProvider {
tgt, err = provider.NextTarget(c)
if err != nil {
return config.ErrorHandler(c, err)
}
} else {
tgt = config.Balancer.Next(c)
}
return
c.Set(config.ContextKey, tgt)
//If retrying a failed request, clear any previous errors from
//context here so that balancers have the option to check for
//errors that occurred using previous target
if retries < config.RetryCount {
c.Set("_error", nil)
}
// Proxy
switch {
case c.IsWebSocket():
proxyRaw(tgt, c).ServeHTTP(res, req)
case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
default:
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
}
err, hasError := c.Get("_error").(error)
if !hasError {
return nil
}
retry := retries > 0 && config.RetryFilter(c, err)
if !retry {
return config.ErrorHandler(c, err)
}
retries--
}
}
}
}

View File

@ -160,6 +160,8 @@ type (
burst int
expiresIn time.Duration
lastCleanup time.Time
timeNow func() time.Time
}
// Visitor signifies a unique user's limiter details
Visitor struct {
@ -219,7 +221,8 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s
store.burst = int(config.Rate)
}
store.visitors = make(map[string]*Visitor)
store.lastCleanup = now()
store.timeNow = time.Now
store.lastCleanup = store.timeNow()
return
}
@ -244,12 +247,13 @@ func (store *RateLimiterMemoryStore) Allow(identifier string) (bool, error) {
limiter.Limiter = rate.NewLimiter(store.rate, store.burst)
store.visitors[identifier] = limiter
}
limiter.lastSeen = now()
if now().Sub(store.lastCleanup) > store.expiresIn {
now := store.timeNow()
limiter.lastSeen = now
if now.Sub(store.lastCleanup) > store.expiresIn {
store.cleanupStaleVisitors()
}
store.mutex.Unlock()
return limiter.AllowN(now(), 1), nil
return limiter.AllowN(store.timeNow(), 1), nil
}
/*
@ -258,14 +262,9 @@ of users who haven't visited again after the configured expiry time has elapsed
*/
func (store *RateLimiterMemoryStore) cleanupStaleVisitors() {
for id, visitor := range store.visitors {
if now().Sub(visitor.lastSeen) > store.expiresIn {
if store.timeNow().Sub(visitor.lastSeen) > store.expiresIn {
delete(store.visitors, id)
}
}
store.lastCleanup = now()
store.lastCleanup = store.timeNow()
}
/*
actual time method which is mocked in test file
*/
var now = time.Now

View File

@ -37,19 +37,26 @@ type (
// LogErrorFunc defines a function for custom logging in the middleware.
// If it's set you don't need to provide LogLevel for config.
// If this function returns nil, the centralized HTTPErrorHandler will not be called.
LogErrorFunc LogErrorFunc
// DisableErrorHandler disables the call to centralized HTTPErrorHandler.
// The recovered error is then passed back to upstream middleware, instead of swallowing the error.
// Optional. Default value false.
DisableErrorHandler bool `yaml:"disable_error_handler"`
}
)
var (
// DefaultRecoverConfig is the default Recover middleware config.
DefaultRecoverConfig = RecoverConfig{
Skipper: DefaultSkipper,
StackSize: 4 << 10, // 4 KB
DisableStackAll: false,
DisablePrintStack: false,
LogLevel: 0,
LogErrorFunc: nil,
Skipper: DefaultSkipper,
StackSize: 4 << 10, // 4 KB
DisableStackAll: false,
DisablePrintStack: false,
LogLevel: 0,
LogErrorFunc: nil,
DisableErrorHandler: false,
}
)
@ -71,7 +78,7 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
return func(c echo.Context) (returnErr error) {
if config.Skipper(c) {
return next(c)
}
@ -113,7 +120,12 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
c.Logger().Print(msg)
}
}
c.Error(err)
if err != nil && !config.DisableErrorHandler {
c.Error(err)
} else {
returnErr = err
}
}
}()
return next(c)

View File

@ -225,7 +225,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
if config.Skipper == nil {
config.Skipper = DefaultSkipper
}
now = time.Now
now := time.Now
if config.timeNow != nil {
now = config.timeNow
}
@ -257,7 +257,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
config.BeforeNextFunc(c)
}
err := next(c)
if config.HandleError {
if err != nil && config.HandleError {
c.Error(err)
}

View File

@ -94,6 +94,13 @@ func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return r.Writer.(http.Hijacker).Hijack()
}
// Unwrap returns the original http.ResponseWriter.
// ResponseController can be used to access the original http.ResponseWriter.
// See [https://go.dev/blog/go1.20]
func (r *Response) Unwrap() http.ResponseWriter {
return r.Writer
}
func (r *Response) reset(w http.ResponseWriter) {
r.beforeFuncs = nil
r.afterFuncs = nil

View File

@ -151,7 +151,7 @@ func (r *Router) Routes() []*Route {
return routes
}
// Reverse generates an URL from route name and provided parameters.
// Reverse generates a URL from route name and provided parameters.
func (r *Router) Reverse(name string, params ...interface{}) string {
uri := new(bytes.Buffer)
ln := len(params)
@ -159,7 +159,12 @@ func (r *Router) Reverse(name string, params ...interface{}) string {
for _, route := range r.routes {
if route.Name == name {
for i, l := 0, len(route.Path); i < l; i++ {
if (route.Path[i] == ':' || route.Path[i] == '*') && n < ln {
hasBackslash := route.Path[i] == '\\'
if hasBackslash && i+1 < l && route.Path[i+1] == ':' {
i++ // backslash before colon escapes that colon. in that case skip backslash
}
if n < ln && (route.Path[i] == '*' || (!hasBackslash && route.Path[i] == ':')) {
// in case of `*` wildcard or `:` (unescaped colon) param we replace everything till next slash or end of path
for ; i < l && route.Path[i] != '/'; i++ {
}
uri.WriteString(fmt.Sprintf("%v", params[n]))

View File

@ -596,18 +596,20 @@ func (d *decoder) decodeMap(
mapType := result.Type()
keyValue := reflect.New(mapType.Key()).Elem()
elemType := mapType.Elem()
elemKind := elemType.Kind()
var elemValue reflect.Value
for i := uint(0); i < size; i++ {
var key []byte
var err error
key, offset, err = d.decodeKey(offset)
if err != nil {
return 0, err
}
if !elemValue.IsValid() || elemKind == reflect.Interface {
if elemValue.IsValid() {
// After 1.20 is the minimum supported version, this can just be
// elemValue.SetZero()
reflectSetZero(elemValue)
} else {
elemValue = reflect.New(elemType).Elem()
}

View File

@ -1,5 +1,5 @@
//go:build !windows && !appengine && !plan9 && !js && !wasip1
// +build !windows,!appengine,!plan9,!js,!wasip1
//go:build !windows && !appengine && !plan9 && !js && !wasip1 && !wasi
// +build !windows,!appengine,!plan9,!js,!wasip1,!wasi
package maxminddb

View File

@ -1,5 +1,5 @@
//go:build appengine || plan9 || js || wasip1
// +build appengine plan9 js wasip1
//go:build appengine || plan9 || js || wasip1 || wasi
// +build appengine plan9 js wasip1 wasi
package maxminddb

View File

@ -1,5 +1,5 @@
//go:build !appengine && !plan9 && !js && !wasip1
// +build !appengine,!plan9,!js,!wasip1
//go:build !appengine && !plan9 && !js && !wasip1 && !wasi
// +build !appengine,!plan9,!js,!wasip1,!wasi
package maxminddb

View File

@ -0,0 +1,10 @@
//go:build go1.20
// +build go1.20
package maxminddb
import "reflect"
func reflectSetZero(v reflect.Value) {
v.SetZero()
}

View File

@ -0,0 +1,10 @@
//go:build !go1.20
// +build !go1.20
package maxminddb
import "reflect"
func reflectSetZero(v reflect.Value) {
v.Set(reflect.Zero(v.Type()))
}

View File

@ -19,6 +19,7 @@ import (
"io/fs"
"log"
"math"
"math/bits"
mathrand "math/rand"
"net"
"net/http"
@ -518,11 +519,14 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
func authorityAddr(scheme string, authority string) (addr string) {
host, port, err := net.SplitHostPort(authority)
if err != nil { // authority didn't have a port
host = authority
port = ""
}
if port == "" { // authority's port was empty
port = "443"
if scheme == "http" {
port = "80"
}
host = authority
}
if a, err := idna.ToASCII(host); err == nil {
host = a
@ -1677,7 +1681,27 @@ func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {
return int(n) // doesn't truncate; max is 512K
}
var bufPool sync.Pool // of *[]byte
// Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running
// streaming requests using small frame sizes occupy large buffers initially allocated for prior
// requests needing big buffers. The size ranges are as follows:
// {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB],
// {256 KB, 512 KB], {512 KB, infinity}
// In practice, the maximum scratch buffer size should not exceed 512 KB due to
// frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used.
// It exists mainly as a safety measure, for potential future increases in max buffer size.
var bufPools [7]sync.Pool // of *[]byte
func bufPoolIndex(size int) int {
if size <= 16384 {
return 0
}
size -= 1
bits := bits.Len(uint(size))
index := bits - 14
if index >= len(bufPools) {
return len(bufPools) - 1
}
return index
}
func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
cc := cs.cc
@ -1695,12 +1719,13 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
// Scratch buffer for reading into & writing from.
scratchLen := cs.frameScratchBufferLen(maxFrameSize)
var buf []byte
if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen {
defer bufPool.Put(bp)
index := bufPoolIndex(scratchLen)
if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen {
defer bufPools[index].Put(bp)
buf = *bp
} else {
buf = make([]byte, scratchLen)
defer bufPool.Put(&buf)
defer bufPools[index].Put(&buf)
}
var sawEOF bool

View File

@ -624,7 +624,7 @@ ccflags="$@"
$2 ~ /^MEM/ ||
$2 ~ /^WG/ ||
$2 ~ /^FIB_RULE_/ ||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE|IOMIN$|IOOPT$|ALIGNOFF$|DISCARD|ROTATIONAL$|ZEROOUT$|GETDISKSEQ$)/ {printf("\t%s = C.%s\n", $2, $2)}
$2 ~ /^__WCOREFLAG$/ {next}
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}

14
vendor/golang.org/x/sys/unix/mmap_nomremap.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris
// +build aix darwin dragonfly freebsd openbsd solaris
package unix
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux
// +build linux
//go:build linux || netbsd
// +build linux netbsd
package unix
@ -14,8 +14,17 @@ type mremapMmapper struct {
mremap func(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
}
var mapper = &mremapMmapper{
mmapper: mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
},
mremap: mremap,
}
func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
if newLength <= 0 || len(oldData) == 0 || len(oldData) != cap(oldData) || flags&MREMAP_FIXED != 0 {
if newLength <= 0 || len(oldData) == 0 || len(oldData) != cap(oldData) || flags&mremapFixed != 0 {
return nil, EINVAL
}
@ -32,9 +41,13 @@ func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data [
}
bNew := unsafe.Slice((*byte)(unsafe.Pointer(newAddr)), newLength)
pNew := &bNew[cap(bNew)-1]
if flags&MREMAP_DONTUNMAP == 0 {
if flags&mremapDontunmap == 0 {
delete(m.active, pOld)
}
m.active[pNew] = bNew
return bNew, nil
}
func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
return mapper.Mremap(oldData, newLength, flags)
}

View File

@ -535,21 +535,6 @@ func Fsync(fd int) error {
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = nsendmsg
//sys munmap(addr uintptr, length uintptr) (err error)
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
//sys Madvise(b []byte, advice int) (err error)
//sys Mprotect(b []byte, prot int) (err error)
//sys Mlock(b []byte) (err error)

View File

@ -601,20 +601,6 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
// Gethostuuid(uuid *byte, timeout *Timespec) (err error)
// Ptrace(req int, pid int, addr uintptr, data int) (ret uintptr, err error)
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
//sys Madvise(b []byte, behav int) (err error)
//sys Mlock(b []byte) (err error)
//sys Mlockall(flags int) (err error)

View File

@ -510,30 +510,36 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
return nil, err
}
// Find size.
n := uintptr(0)
if err := sysctl(mib, nil, &n, nil, 0); err != nil {
return nil, err
}
if n == 0 {
return nil, nil
}
if n%SizeofKinfoProc != 0 {
return nil, fmt.Errorf("sysctl() returned a size of %d, which is not a multiple of %d", n, SizeofKinfoProc)
}
for {
// Find size.
n := uintptr(0)
if err := sysctl(mib, nil, &n, nil, 0); err != nil {
return nil, err
}
if n == 0 {
return nil, nil
}
if n%SizeofKinfoProc != 0 {
return nil, fmt.Errorf("sysctl() returned a size of %d, which is not a multiple of %d", n, SizeofKinfoProc)
}
// Read into buffer of that size.
buf := make([]KinfoProc, n/SizeofKinfoProc)
if err := sysctl(mib, (*byte)(unsafe.Pointer(&buf[0])), &n, nil, 0); err != nil {
return nil, err
}
if n%SizeofKinfoProc != 0 {
return nil, fmt.Errorf("sysctl() returned a size of %d, which is not a multiple of %d", n, SizeofKinfoProc)
}
// Read into buffer of that size.
buf := make([]KinfoProc, n/SizeofKinfoProc)
if err := sysctl(mib, (*byte)(unsafe.Pointer(&buf[0])), &n, nil, 0); err != nil {
if err == ENOMEM {
// Process table grew. Try again.
continue
}
return nil, err
}
if n%SizeofKinfoProc != 0 {
return nil, fmt.Errorf("sysctl() returned a size of %d, which is not a multiple of %d", n, SizeofKinfoProc)
}
// The actual call may return less than the original reported required
// size so ensure we deal with that.
return buf[:n/SizeofKinfoProc], nil
// The actual call may return less than the original reported required
// size so ensure we deal with that.
return buf[:n/SizeofKinfoProc], nil
}
}
//sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)

View File

@ -1885,7 +1885,7 @@ func Getpgrp() (pid int) {
//sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6
//sys pselect6(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_argpack) (n int, err error)
//sys read(fd int, p []byte) (n int, err error)
//sys Removexattr(path string, attr string) (err error)
//sys Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error)
@ -2125,28 +2125,6 @@ func writevRacedetect(iovecs []Iovec, n int) {
// mmap varies by architecture; see syscall_linux_*.go.
//sys munmap(addr uintptr, length uintptr) (err error)
//sys mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
var mapper = &mremapMmapper{
mmapper: mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
},
mremap: mremap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
return mapper.Mremap(oldData, newLength, flags)
}
//sys Madvise(b []byte, advice int) (err error)
//sys Mprotect(b []byte, prot int) (err error)
//sys Mlock(b []byte) (err error)
@ -2155,6 +2133,12 @@ func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
//sys Munlock(b []byte) (err error)
//sys Munlockall() (err error)
const (
mremapFixed = MREMAP_FIXED
mremapDontunmap = MREMAP_DONTUNMAP
mremapMaymove = MREMAP_MAYMOVE
)
// Vmsplice splices user pages from a slice of Iovecs into a pipe specified by fd,
// using the specified flags.
func Vmsplice(fd int, iovs []Iovec, flags int) (int, error) {
@ -2454,6 +2438,39 @@ func Getresgid() (rgid, egid, sgid int) {
return int(r), int(e), int(s)
}
// Pselect is a wrapper around the Linux pselect6 system call.
// This version does not modify the timeout argument.
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
// Per https://man7.org/linux/man-pages/man2/select.2.html#NOTES,
// The Linux pselect6() system call modifies its timeout argument.
// [Not modifying the argument] is the behavior required by POSIX.1-2001.
var mutableTimeout *Timespec
if timeout != nil {
mutableTimeout = new(Timespec)
*mutableTimeout = *timeout
}
// The final argument of the pselect6() system call is not a
// sigset_t * pointer, but is instead a structure
var kernelMask *sigset_argpack
if sigmask != nil {
wordBits := 32 << (^uintptr(0) >> 63) // see math.intSize
// A sigset stores one bit per signal,
// offset by 1 (because signal 0 does not exist).
// So the number of words needed is ⌈__C_NSIG - 1 / wordBits⌉.
sigsetWords := (_C__NSIG - 1 + wordBits - 1) / (wordBits)
sigsetBytes := uintptr(sigsetWords * (wordBits / 8))
kernelMask = &sigset_argpack{
ss: sigmask,
ssLen: sigsetBytes,
}
}
return pselect6(nfd, r, w, e, mutableTimeout, kernelMask)
}
/*
* Unimplemented
*/

View File

@ -40,7 +40,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
if timeout != nil {
ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
}
return Pselect(nfd, r, w, e, ts, nil)
return pselect6(nfd, r, w, e, ts, nil)
}
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View File

@ -33,7 +33,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
if timeout != nil {
ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
}
return Pselect(nfd, r, w, e, ts, nil)
return pselect6(nfd, r, w, e, ts, nil)
}
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View File

@ -28,7 +28,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
if timeout != nil {
ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
}
return Pselect(nfd, r, w, e, ts, nil)
return pselect6(nfd, r, w, e, ts, nil)
}
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View File

@ -31,7 +31,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
if timeout != nil {
ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
}
return Pselect(nfd, r, w, e, ts, nil)
return pselect6(nfd, r, w, e, ts, nil)
}
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View File

@ -32,7 +32,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
if timeout != nil {
ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
}
return Pselect(nfd, r, w, e, ts, nil)
return pselect6(nfd, r, w, e, ts, nil)
}
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
@ -177,3 +177,14 @@ func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error
}
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
}
//sys riscvHWProbe(pairs []RISCVHWProbePairs, cpuCount uintptr, cpus *CPUSet, flags uint) (err error)
func RISCVHWProbe(pairs []RISCVHWProbePairs, set *CPUSet, flags uint) (err error) {
var setSize uintptr
if set != nil {
setSize = uintptr(unsafe.Sizeof(*set))
}
return riscvHWProbe(pairs, setSize, set, flags)
}

View File

@ -360,6 +360,18 @@ func Statvfs(path string, buf *Statvfs_t) (err error) {
//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
const (
mremapFixed = MAP_FIXED
mremapDontunmap = 0
mremapMaymove = 0
)
//sys mremapNetBSD(oldp uintptr, oldsize uintptr, newp uintptr, newsize uintptr, flags int) (xaddr uintptr, err error) = SYS_MREMAP
func mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (uintptr, error) {
return mremapNetBSD(oldaddr, oldlength, newaddr, newlength, flags)
}
/*
* Unimplemented
*/
@ -564,7 +576,6 @@ func Statvfs(path string, buf *Statvfs_t) (err error) {
// mq_timedreceive
// mq_timedsend
// mq_unlink
// mremap
// msgget
// msgrcv
// msgsnd

View File

@ -716,20 +716,6 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
return
}
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
// Event Ports
type fileObjCookie struct {

View File

@ -147,6 +147,14 @@ func (m *mmapper) Munmap(data []byte) (err error) {
return nil
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
func Read(fd int, p []byte) (n int, err error) {
n, err = read(fd, p)
if raceenabled {

View File

@ -285,25 +285,11 @@ func Close(fd int) (err error) {
return
}
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
// Dummy function: there are no semantics for Madvise on z/OS
func Madvise(b []byte, advice int) (err error) {
return
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
//sys Gethostname(buf []byte) (err error) = SYS___GETHOSTNAME_A
//sysnb Getegid() (egid int)
//sysnb Geteuid() (uid int)

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80041270
BLKBSZSET = 0x40041271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80041272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80081270
BLKBSZSET = 0x40081271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80081272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80041270
BLKBSZSET = 0x40041271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80041272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80081270
BLKBSZSET = 0x40081271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80081272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80081270
BLKBSZSET = 0x40081271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80081272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40041270
BLKBSZSET = 0x80041271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40041272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40081270
BLKBSZSET = 0x80081271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40081272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40081270
BLKBSZSET = 0x80081271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40081272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40041270
BLKBSZSET = 0x80041271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40041272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x10
B576000 = 0x15
B921600 = 0x16
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40041270
BLKBSZSET = 0x80041271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40041272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1f
BS1 = 0x8000
BSDLY = 0x8000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x10
B576000 = 0x15
B921600 = 0x16
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40081270
BLKBSZSET = 0x80081271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40081272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1f
BS1 = 0x8000
BSDLY = 0x8000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x10
B576000 = 0x15
B921600 = 0x16
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40081270
BLKBSZSET = 0x80081271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40081272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1f
BS1 = 0x8000
BSDLY = 0x8000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80081270
BLKBSZSET = 0x40081271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80081272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -27,22 +27,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x127a
BLKBSZGET = 0x80081270
BLKBSZSET = 0x40081271
BLKDISCARD = 0x1277
BLKDISCARDZEROES = 0x127c
BLKFLSBUF = 0x1261
BLKFRAGET = 0x1265
BLKFRASET = 0x1264
BLKGETDISKSEQ = 0x80081280
BLKGETSIZE = 0x1260
BLKGETSIZE64 = 0x80081272
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
BLKPBSZGET = 0x127b
BLKRAGET = 0x1263
BLKRASET = 0x1262
BLKROGET = 0x125e
BLKROSET = 0x125d
BLKROTATIONAL = 0x127e
BLKRRPART = 0x125f
BLKSECDISCARD = 0x127d
BLKSECTGET = 0x1267
BLKSECTSET = 0x1266
BLKSSZGET = 0x1268
BLKZEROOUT = 0x127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -30,22 +30,31 @@ const (
B57600 = 0x1001
B576000 = 0x1006
B921600 = 0x1007
BLKALIGNOFF = 0x2000127a
BLKBSZGET = 0x40081270
BLKBSZSET = 0x80081271
BLKDISCARD = 0x20001277
BLKDISCARDZEROES = 0x2000127c
BLKFLSBUF = 0x20001261
BLKFRAGET = 0x20001265
BLKFRASET = 0x20001264
BLKGETDISKSEQ = 0x40081280
BLKGETSIZE = 0x20001260
BLKGETSIZE64 = 0x40081272
BLKIOMIN = 0x20001278
BLKIOOPT = 0x20001279
BLKPBSZGET = 0x2000127b
BLKRAGET = 0x20001263
BLKRASET = 0x20001262
BLKROGET = 0x2000125e
BLKROSET = 0x2000125d
BLKROTATIONAL = 0x2000127e
BLKRRPART = 0x2000125f
BLKSECDISCARD = 0x2000127d
BLKSECTGET = 0x20001267
BLKSECTSET = 0x20001266
BLKSSZGET = 0x20001268
BLKZEROOUT = 0x2000127f
BOTHER = 0x1000
BS1 = 0x2000
BSDLY = 0x2000

View File

@ -1356,7 +1356,7 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
func pselect6(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_argpack) (n int, err error) {
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
n = int(r0)
if e1 != 0 {

View File

@ -531,3 +531,19 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func riscvHWProbe(pairs []RISCVHWProbePairs, cpuCount uintptr, cpus *CPUSet, flags uint) (err error) {
var _p0 unsafe.Pointer
if len(pairs) > 0 {
_p0 = unsafe.Pointer(&pairs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
_, _, e1 := Syscall6(SYS_RISCV_HWPROBE, uintptr(_p0), uintptr(len(pairs)), uintptr(cpuCount), uintptr(unsafe.Pointer(cpus)), uintptr(flags), 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1858,3 +1858,14 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mremapNetBSD(oldp uintptr, oldsize uintptr, newp uintptr, newsize uintptr, flags int) (xaddr uintptr, err error) {
r0, _, e1 := Syscall6(SYS_MREMAP, uintptr(oldp), uintptr(oldsize), uintptr(newp), uintptr(newsize), uintptr(flags), 0)
xaddr = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1858,3 +1858,14 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mremapNetBSD(oldp uintptr, oldsize uintptr, newp uintptr, newsize uintptr, flags int) (xaddr uintptr, err error) {
r0, _, e1 := Syscall6(SYS_MREMAP, uintptr(oldp), uintptr(oldsize), uintptr(newp), uintptr(newsize), uintptr(flags), 0)
xaddr = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1858,3 +1858,14 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mremapNetBSD(oldp uintptr, oldsize uintptr, newp uintptr, newsize uintptr, flags int) (xaddr uintptr, err error) {
r0, _, e1 := Syscall6(SYS_MREMAP, uintptr(oldp), uintptr(oldsize), uintptr(newp), uintptr(newsize), uintptr(flags), 0)
xaddr = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -1858,3 +1858,14 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mremapNetBSD(oldp uintptr, oldsize uintptr, newp uintptr, newsize uintptr, flags int) (xaddr uintptr, err error) {
r0, _, e1 := Syscall6(SYS_MREMAP, uintptr(oldp), uintptr(oldsize), uintptr(newp), uintptr(newsize), uintptr(flags), 0)
xaddr = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}

View File

@ -251,6 +251,8 @@ const (
SYS_ACCEPT4 = 242
SYS_RECVMMSG = 243
SYS_ARCH_SPECIFIC_SYSCALL = 244
SYS_RISCV_HWPROBE = 258
SYS_RISCV_FLUSH_ICACHE = 259
SYS_WAIT4 = 260
SYS_PRLIMIT64 = 261
SYS_FANOTIFY_INIT = 262

View File

@ -866,6 +866,11 @@ const (
POLLNVAL = 0x20
)
type sigset_argpack struct {
ss *Sigset_t
ssLen uintptr
}
type SignalfdSiginfo struct {
Signo uint32
Errno int32

View File

@ -718,3 +718,26 @@ type SysvShmDesc struct {
_ uint64
_ uint64
}
type RISCVHWProbePairs struct {
Key int64
Value uint64
}
const (
RISCV_HWPROBE_KEY_MVENDORID = 0x0
RISCV_HWPROBE_KEY_MARCHID = 0x1
RISCV_HWPROBE_KEY_MIMPID = 0x2
RISCV_HWPROBE_KEY_BASE_BEHAVIOR = 0x3
RISCV_HWPROBE_BASE_BEHAVIOR_IMA = 0x1
RISCV_HWPROBE_KEY_IMA_EXT_0 = 0x4
RISCV_HWPROBE_IMA_FD = 0x1
RISCV_HWPROBE_IMA_C = 0x2
RISCV_HWPROBE_KEY_CPUPERF_0 = 0x5
RISCV_HWPROBE_MISALIGNED_UNKNOWN = 0x0
RISCV_HWPROBE_MISALIGNED_EMULATED = 0x1
RISCV_HWPROBE_MISALIGNED_SLOW = 0x2
RISCV_HWPROBE_MISALIGNED_FAST = 0x3
RISCV_HWPROBE_MISALIGNED_UNSUPPORTED = 0x4
RISCV_HWPROBE_MISALIGNED_MASK = 0x7
)

View File

@ -135,14 +135,14 @@ func Getpagesize() int { return 4096 }
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
// This is useful when interoperating with Windows code requiring callbacks.
// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
// The argument is expected to be a function with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
func NewCallback(fn interface{}) uintptr {
return syscall.NewCallback(fn)
}
// NewCallbackCDecl converts a Go function to a function pointer conforming to the cdecl calling convention.
// This is useful when interoperating with Windows code requiring callbacks.
// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
// The argument is expected to be a function with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
func NewCallbackCDecl(fn interface{}) uintptr {
return syscall.NewCallbackCDecl(fn)
}

15
vendor/modules.txt vendored
View File

@ -1,7 +1,10 @@
# github.com/bradfitz/gomemcache v0.0.0-20230611145640-acc696258285
## explicit; go 1.12
github.com/bradfitz/gomemcache/memcache
# github.com/golang-jwt/jwt v3.2.2+incompatible
## explicit
github.com/golang-jwt/jwt
# github.com/labstack/echo/v4 v4.10.2
# github.com/labstack/echo/v4 v4.11.1
## explicit; go 1.17
github.com/labstack/echo/v4
github.com/labstack/echo/v4/middleware
@ -31,7 +34,7 @@ github.com/mattn/go-isatty
# github.com/oschwald/geoip2-golang v1.9.0
## explicit; go 1.19
github.com/oschwald/geoip2-golang
# github.com/oschwald/maxminddb-golang v1.11.0
# github.com/oschwald/maxminddb-golang v1.12.0
## explicit; go 1.19
github.com/oschwald/maxminddb-golang
# github.com/valyala/bytebufferpool v1.0.0
@ -40,11 +43,11 @@ github.com/valyala/bytebufferpool
# github.com/valyala/fasttemplate v1.2.2
## explicit; go 1.12
github.com/valyala/fasttemplate
# golang.org/x/crypto v0.11.0
# golang.org/x/crypto v0.12.0
## explicit; go 1.17
golang.org/x/crypto/acme
golang.org/x/crypto/acme/autocert
# golang.org/x/net v0.12.0
# golang.org/x/net v0.14.0
## explicit; go 1.17
golang.org/x/net/http/httpguts
golang.org/x/net/http2
@ -53,12 +56,12 @@ golang.org/x/net/http2/hpack
golang.org/x/net/idna
golang.org/x/net/internal/socks
golang.org/x/net/proxy
# golang.org/x/sys v0.10.0
# golang.org/x/sys v0.11.0
## explicit; go 1.17
golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/text v0.11.0
# golang.org/x/text v0.12.0
## explicit; go 1.17
golang.org/x/text/secure/bidirule
golang.org/x/text/transform