updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Paul 2023-03-17 13:25:01 +01:00
parent 8f0c230005
commit f9ae5221c6
200 changed files with 7020 additions and 1998 deletions

21
go.mod
View File

@ -1,36 +1,35 @@
module git.paulbsd.com/paulbsd/qrz module git.paulbsd.com/paulbsd/qrz
go 1.19 go 1.20
require ( require (
github.com/antchfx/htmlquery v1.2.5 github.com/antchfx/htmlquery v1.3.0
github.com/antchfx/xpath v1.2.1 // indirect github.com/antchfx/xpath v1.2.4 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/labstack/echo/v4 v4.9.1 github.com/labstack/echo/v4 v4.10.2
github.com/lib/pq v1.10.7 github.com/lib/pq v1.10.7
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.23.0 // indirect github.com/onsi/gomega v1.23.0 // indirect
github.com/robfig/cron v1.2.0 github.com/robfig/cron v1.2.0
golang.org/x/crypto v0.1.0 // indirect golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.1.0 golang.org/x/net v0.8.0
golang.org/x/sys v0.1.0 // indirect golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.4.0 // indirect golang.org/x/text v0.8.0 // indirect
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
xorm.io/builder v0.3.12 // indirect xorm.io/builder v0.3.12 // indirect
xorm.io/xorm v1.3.2 xorm.io/xorm v1.3.2
) )
require ( require (
github.com/goccy/go-json v0.9.11 // indirect github.com/goccy/go-json v0.10.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/labstack/gommon v0.4.0 // indirect github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/stretchr/testify v1.8.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect

62
go.sum
View File

@ -14,10 +14,11 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antchfx/htmlquery v1.2.5 h1:1lXnx46/1wtv1E/kzmH8vrfMuUKYgkdDBA9pIdMJnk4= github.com/antchfx/htmlquery v1.3.0 h1:5I5yNFOVI+egyia5F2s/5Do2nFWxJz41Tr3DyfKD25E=
github.com/antchfx/htmlquery v1.2.5/go.mod h1:2MCVBzYVafPBmKbrmwB9F5xdd+IEgRY61ci2oOsOQVw= github.com/antchfx/htmlquery v1.3.0/go.mod h1:zKPDVTMhfOmcwxheXUsx4rKJy8KEY/PU6eXr/2SebQ8=
github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8= github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY=
github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -80,8 +81,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
@ -92,7 +93,6 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -221,8 +221,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -250,8 +250,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
@ -367,15 +368,12 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -388,6 +386,7 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@ -421,8 +420,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -432,7 +432,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -449,12 +450,13 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -464,6 +466,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -499,18 +502,25 @@ golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/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-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -531,7 +541,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -584,7 +595,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -5,11 +5,11 @@ package htmlquery
import ( import (
"bufio" "bufio"
"bytes"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
"strings"
"github.com/antchfx/xpath" "github.com/antchfx/xpath"
"golang.org/x/net/html" "golang.org/x/net/html"
@ -138,23 +138,23 @@ func Parse(r io.Reader) (*html.Node, error) {
// InnerText returns the text between the start and end tags of the object. // InnerText returns the text between the start and end tags of the object.
func InnerText(n *html.Node) string { func InnerText(n *html.Node) string {
var output func(*bytes.Buffer, *html.Node) var output func(*strings.Builder, *html.Node)
output = func(buf *bytes.Buffer, n *html.Node) { output = func(b *strings.Builder, n *html.Node) {
switch n.Type { switch n.Type {
case html.TextNode: case html.TextNode:
buf.WriteString(n.Data) b.WriteString(n.Data)
return return
case html.CommentNode: case html.CommentNode:
return return
} }
for child := n.FirstChild; child != nil; child = child.NextSibling { for child := n.FirstChild; child != nil; child = child.NextSibling {
output(buf, child) output(b, child)
} }
} }
var buf bytes.Buffer var b strings.Builder
output(&buf, n) output(&b, n)
return buf.String() return b.String()
} }
// SelectAttr returns the attribute value with the specified name. // SelectAttr returns the attribute value with the specified name.
@ -189,15 +189,15 @@ func ExistsAttr(n *html.Node, name string) bool {
// OutputHTML returns the text including tags name. // OutputHTML returns the text including tags name.
func OutputHTML(n *html.Node, self bool) string { func OutputHTML(n *html.Node, self bool) string {
var buf bytes.Buffer var b strings.Builder
if self { if self {
html.Render(&buf, n) html.Render(&b, n)
} else { } else {
for n := n.FirstChild; n != nil; n = n.NextSibling { for n := n.FirstChild; n != nil; n = n.NextSibling {
html.Render(&buf, n) html.Render(&b, n)
} }
} }
return buf.String() return b.String()
} }
type NodeNavigator struct { type NodeNavigator struct {

View File

@ -44,6 +44,12 @@ func axisPredicate(root *axisNode) func(NodeNavigator) bool {
predicate := func(n NodeNavigator) bool { predicate := func(n NodeNavigator) bool {
if typ == n.NodeType() || typ == allNode { if typ == n.NodeType() || typ == allNode {
if nametest { if nametest {
type namespaceURL interface {
NamespaceURL() string
}
if ns, ok := n.(namespaceURL); ok && root.hasNamespaceURI {
return root.LocalName == n.LocalName() && root.namespaceURI == ns.NamespaceURL()
}
if root.LocalName == n.LocalName() && root.Prefix == n.Prefix() { if root.LocalName == n.LocalName() && root.Prefix == n.Prefix() {
return true return true
} }
@ -88,7 +94,10 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
} }
return v return v
} }
qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: filter, Self: true} // fix `//*[contains(@id,"food")]//*[contains(@id,"food")]`, see https://github.com/antchfx/htmlquery/issues/52
// Skip the current node(Self:false) for the next descendants nodes.
_, ok := qyGrandInput.(*contextQuery)
qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: filter, Self: ok}
return qyOutput, nil return qyOutput, nil
} }
} }
@ -347,7 +356,15 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
}, },
} }
case "last": case "last":
switch typ := b.firstInput.(type) {
case *groupQuery, *filterQuery:
// https://github.com/antchfx/xpath/issues/76
// https://github.com/antchfx/xpath/issues/78
qyOutput = &lastQuery{Input: typ}
default:
qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc} qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc}
}
case "position": case "position":
qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc} qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc}
case "boolean", "number", "string": case "boolean", "number", "string":
@ -511,6 +528,7 @@ func (b *builder) processNode(root node) (q query, err error) {
b.firstInput = q b.firstInput = q
case nodeFilter: case nodeFilter:
q, err = b.processFilterNode(root.(*filterNode)) q, err = b.processFilterNode(root.(*filterNode))
b.firstInput = q
case nodeFunction: case nodeFunction:
q, err = b.processFunctionNode(root.(*functionNode)) q, err = b.processFunctionNode(root.(*functionNode))
case nodeOperator: case nodeOperator:
@ -521,12 +539,13 @@ func (b *builder) processNode(root node) (q query, err error) {
return return
} }
q = &groupQuery{Input: q} q = &groupQuery{Input: q}
b.firstInput = q
} }
return return
} }
// build builds a specified XPath expressions expr. // build builds a specified XPath expressions expr.
func build(expr string) (q query, err error) { func build(expr string, namespaces map[string]string) (q query, err error) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
switch x := e.(type) { switch x := e.(type) {
@ -539,7 +558,7 @@ func build(expr string) (q query, err error) {
} }
} }
}() }()
root := parse(expr) root := parse(expr, namespaces)
b := &builder{} b := &builder{}
return b.processNode(root) return b.processNode(root)
} }

View File

@ -71,6 +71,7 @@ const (
type parser struct { type parser struct {
r *scanner r *scanner
d int d int
namespaces map[string]string
} }
// newOperatorNode returns new operator node OperatorNode. // newOperatorNode returns new operator node OperatorNode.
@ -84,8 +85,8 @@ func newOperandNode(v interface{}) node {
} }
// newAxisNode returns new axis node AxisNode. // newAxisNode returns new axis node AxisNode.
func newAxisNode(axeTyp, localName, prefix, prop string, n node) node { func newAxisNode(axeTyp, localName, prefix, prop string, n node, opts ...func(p *axisNode)) node {
return &axisNode{ a := axisNode{
nodeType: nodeAxis, nodeType: nodeAxis,
LocalName: localName, LocalName: localName,
Prefix: prefix, Prefix: prefix,
@ -93,6 +94,10 @@ func newAxisNode(axeTyp, localName, prefix, prop string, n node) node {
Prop: prop, Prop: prop,
Input: n, Input: n,
} }
for _, o := range opts {
o(&a)
}
return &a
} }
// newVariableNode returns new variable node VariableNode. // newVariableNode returns new variable node VariableNode.
@ -469,7 +474,16 @@ func (p *parser) parseNodeTest(n node, axeTyp string) (opnd node) {
if p.r.name == "*" { if p.r.name == "*" {
name = "" name = ""
} }
opnd = newAxisNode(axeTyp, name, prefix, "", n) opnd = newAxisNode(axeTyp, name, prefix, "", n, func(a *axisNode) {
if prefix != "" && p.namespaces != nil {
if ns, ok := p.namespaces[prefix]; ok {
a.hasNamespaceURI = true
a.namespaceURI = ns
} else {
panic(fmt.Sprintf("prefix %s not defined.", prefix))
}
}
})
} }
case itemStar: case itemStar:
opnd = newAxisNode(axeTyp, "", "", "", n) opnd = newAxisNode(axeTyp, "", "", "", n)
@ -531,11 +545,11 @@ func (p *parser) parseMethod(n node) node {
} }
// Parse parsing the XPath express string expr and returns a tree node. // Parse parsing the XPath express string expr and returns a tree node.
func parse(expr string) node { func parse(expr string, namespaces map[string]string) node {
r := &scanner{text: expr} r := &scanner{text: expr}
r.nextChar() r.nextChar()
r.nextItem() r.nextItem()
p := &parser{r: r} p := &parser{r: r, namespaces: namespaces}
return p.parseExpression(nil) return p.parseExpression(nil)
} }
@ -568,6 +582,8 @@ type axisNode struct {
AxeType string // name of the axes.[attribute|ancestor|child|....] AxeType string // name of the axes.[attribute|ancestor|child|....]
LocalName string // local part name of node. LocalName string // local part name of node.
Prefix string // prefix name of node. Prefix string // prefix name of node.
namespaceURI string // namespace URI of node
hasNamespaceURI bool // if namespace URI is set (can be "")
} }
func (a *axisNode) String() string { func (a *axisNode) String() string {

View File

@ -56,7 +56,7 @@ func (c *contextQuery) Evaluate(iterator) interface{} {
} }
func (c *contextQuery) Clone() query { func (c *contextQuery) Clone() query {
return &contextQuery{count: 0, Root: c.Root} return &contextQuery{Root: c.Root}
} }
// ancestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*) // ancestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*)
@ -558,8 +558,8 @@ func (f *filterQuery) do(t iterator) bool {
pt := getNodePosition(f.Input) pt := getNodePosition(f.Input)
return int(val.Float()) == pt return int(val.Float()) == pt
default: default:
if q, ok := f.Predicate.(query); ok { if f.Predicate != nil {
return q.Select(t) != nil return f.Predicate.Select(t) != nil
} }
} }
return false return false
@ -577,7 +577,7 @@ func (f *filterQuery) Select(t iterator) NodeNavigator {
node := f.Input.Select(t) node := f.Input.Select(t)
if node == nil { if node == nil {
return node return nil
} }
node = node.Copy() node = node.Copy()
@ -676,14 +676,12 @@ type groupQuery struct {
} }
func (g *groupQuery) Select(t iterator) NodeNavigator { func (g *groupQuery) Select(t iterator) NodeNavigator {
for {
node := g.Input.Select(t) node := g.Input.Select(t)
if node == nil { if node == nil {
return nil return nil
} }
g.posit++ g.posit++
return node.Copy() return node
}
} }
func (g *groupQuery) Evaluate(t iterator) interface{} { func (g *groupQuery) Evaluate(t iterator) interface{} {
@ -691,7 +689,7 @@ func (g *groupQuery) Evaluate(t iterator) interface{} {
} }
func (g *groupQuery) Clone() query { func (g *groupQuery) Clone() query {
return &groupQuery{Input: g.Input} return &groupQuery{Input: g.Input.Clone()}
} }
func (g *groupQuery) position() int { func (g *groupQuery) position() int {
@ -896,6 +894,35 @@ func (u *unionQuery) Clone() query {
return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()} return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()}
} }
type lastQuery struct {
buffer []NodeNavigator
counted bool
Input query
}
func (q *lastQuery) Select(t iterator) NodeNavigator {
return nil
}
func (q *lastQuery) Evaluate(t iterator) interface{} {
if !q.counted {
for {
node := q.Input.Select(t)
if node == nil {
break
}
q.buffer = append(q.buffer, node.Copy())
}
q.counted = true
}
return float64(len(q.buffer))
}
func (q *lastQuery) Clone() query {
return &lastQuery{Input: q.Input.Clone()}
}
func getHashCode(n NodeNavigator) uint64 { func getHashCode(n NodeNavigator) uint64 {
var sb bytes.Buffer var sb bytes.Buffer
switch n.NodeType() { switch n.NodeType() {

View File

@ -141,7 +141,7 @@ func Compile(expr string) (*Expr, error) {
if expr == "" { if expr == "" {
return nil, errors.New("expr expression is nil") return nil, errors.New("expr expression is nil")
} }
qy, err := build(expr) qy, err := build(expr, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,3 +159,18 @@ func MustCompile(expr string) *Expr {
} }
return exp return exp
} }
// CompileWithNS compiles an XPath expression string, using given namespaces map.
func CompileWithNS(expr string, namespaces map[string]string) (*Expr, error) {
if expr == "" {
return nil, errors.New("expr expression is nil")
}
qy, err := build(expr, namespaces)
if err != nil {
return nil, err
}
if qy == nil {
return nil, fmt.Errorf(fmt.Sprintf("undeclared variable in XPath expression: %s", expr))
}
return &Expr{s: expr, q: qy}, nil
}

View File

@ -1,3 +1,25 @@
# v0.10.1 - 2023/03/13
### Fix bugs
* Fix checkptr error for array decoder ( #415 )
* Fix added buffer size check when decoding key ( #430 )
* Fix handling of anonymous fields other than struct ( #431 )
* Fix to not optimize when lower conversion can't handle byte-by-byte ( #432 )
* Fix a problem that MarshalIndent does not work when UnorderedMap is specified ( #435 )
* Fix mapDecoder.DecodeStream() for empty objects containing whitespace ( #425 )
* Fix an issue that could not set the correct NextField for fields in the embedded structure ( #438 )
# v0.10.0 - 2022/11/29
### New features
* Support JSON Path ( #250 )
### Fix bugs
* Fix marshaler for map's key ( #409 )
# v0.9.11 - 2022/08/18 # v0.9.11 - 2022/08/18
### Fix bugs ### Fix bugs

View File

@ -22,7 +22,7 @@ cover-html: cover
.PHONY: lint .PHONY: lint
lint: golangci-lint lint: golangci-lint
golangci-lint run $(BIN_DIR)/golangci-lint run
golangci-lint: | $(BIN_DIR) golangci-lint: | $(BIN_DIR)
@{ \ @{ \
@ -30,7 +30,7 @@ golangci-lint: | $(BIN_DIR)
GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \ GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \
cd $$GOLANGCI_LINT_TMP_DIR; \ cd $$GOLANGCI_LINT_TMP_DIR; \
go mod init tmp; \ go mod init tmp; \
GOBIN=$(BIN_DIR) go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0; \ GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0; \
rm -rf $$GOLANGCI_LINT_TMP_DIR; \ rm -rf $$GOLANGCI_LINT_TMP_DIR; \
} }

View File

@ -184,7 +184,7 @@ func Marshal(v interface{}) ([]byte, error) {
`json.Marshal` and `json.Unmarshal` receive `interface{}` value and they perform type determination dynamically to process. `json.Marshal` and `json.Unmarshal` receive `interface{}` value and they perform type determination dynamically to process.
In normal case, you need to use the `reflect` library to determine the type dynamically, but since `reflect.Type` is defined as `interface`, when you call the method of `reflect.Type`, The reflect's argument is escaped. In normal case, you need to use the `reflect` library to determine the type dynamically, but since `reflect.Type` is defined as `interface`, when you call the method of `reflect.Type`, The reflect's argument is escaped.
Therefore, the arguments for `Marshal` and `Unmarshal` are always escape to the heap. Therefore, the arguments for `Marshal` and `Unmarshal` are always escaped to the heap.
However, `go-json` can use the feature of `reflect.Type` while avoiding escaping. However, `go-json` can use the feature of `reflect.Type` while avoiding escaping.
`reflect.Type` is defined as `interface`, but in reality `reflect.Type` is implemented only by the structure `rtype` defined in the `reflect` package. `reflect.Type` is defined as `interface`, but in reality `reflect.Type` is implemented only by the structure `rtype` defined in the `reflect` package.

View File

@ -83,6 +83,37 @@ func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs
return validateEndBuf(src, cursor) return validateEndBuf(src, cursor)
} }
var (
pathDecoder = decoder.NewPathDecoder()
)
func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
if path.path.RootSelectorOnly {
return [][]byte{data}, nil
}
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
ctx := decoder.TakeRuntimeContext()
ctx.Buf = src
ctx.Option.Flags = 0
ctx.Option.Flags |= decoder.PathOption
ctx.Option.Path = path.path
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
paths, cursor, err := pathDecoder.DecodePath(ctx, 0, 0)
if err != nil {
decoder.ReleaseRuntimeContext(ctx)
return nil, err
}
decoder.ReleaseRuntimeContext(ctx)
if err := validateEndBuf(src, cursor); err != nil {
return nil, err
}
return paths, nil
}
func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data) copy(src, data)

View File

@ -37,3 +37,5 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
type UnsupportedTypeError = errors.UnsupportedTypeError type UnsupportedTypeError = errors.UnsupportedTypeError
type UnsupportedValueError = errors.UnsupportedValueError type UnsupportedValueError = errors.UnsupportedValueError
type PathError = errors.PathError

View File

@ -35,3 +35,7 @@ func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
p = *(*unsafe.Pointer)(p) p = *(*unsafe.Pointer)(p)
return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset)) return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
} }
func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return d.dec.DecodePath(ctx, cursor, depth)
}

View File

@ -1,6 +1,7 @@
package decoder package decoder
import ( import (
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
@ -18,7 +19,9 @@ type arrayDecoder struct {
} }
func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder { func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder {
zeroValue := *(*unsafe.Pointer)(unsafe_New(elemType)) // workaround to avoid checkptr errors. cannot use `*(*unsafe.Pointer)(unsafe_New(elemType))` directly.
zeroValuePtr := unsafe_New(elemType)
zeroValue := **(**unsafe.Pointer)(unsafe.Pointer(&zeroValuePtr))
return &arrayDecoder{ return &arrayDecoder{
valueDecoder: dec, valueDecoder: dec,
elemType: elemType, elemType: elemType,
@ -167,3 +170,7 @@ func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
} }
} }
} }
func (d *arrayDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: array decoder does not support decode path")
}

View File

@ -0,0 +1,438 @@
package decoder
import (
"fmt"
"reflect"
"strconv"
)
var (
nilValue = reflect.ValueOf(nil)
)
func AssignValue(src, dst reflect.Value) error {
if dst.Type().Kind() != reflect.Ptr {
return fmt.Errorf("invalid dst type. required pointer type: %T", dst.Type())
}
casted, err := castValue(dst.Elem().Type(), src)
if err != nil {
return err
}
dst.Elem().Set(casted)
return nil
}
func castValue(t reflect.Type, v reflect.Value) (reflect.Value, error) {
switch t.Kind() {
case reflect.Int:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int(vv.Int())), nil
case reflect.Int8:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int8(vv.Int())), nil
case reflect.Int16:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int16(vv.Int())), nil
case reflect.Int32:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int32(vv.Int())), nil
case reflect.Int64:
return castInt(v)
case reflect.Uint:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint(vv.Uint())), nil
case reflect.Uint8:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint8(vv.Uint())), nil
case reflect.Uint16:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint16(vv.Uint())), nil
case reflect.Uint32:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint32(vv.Uint())), nil
case reflect.Uint64:
return castUint(v)
case reflect.Uintptr:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uintptr(vv.Uint())), nil
case reflect.String:
return castString(v)
case reflect.Bool:
return castBool(v)
case reflect.Float32:
vv, err := castFloat(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(float32(vv.Float())), nil
case reflect.Float64:
return castFloat(v)
case reflect.Array:
return castArray(t, v)
case reflect.Slice:
return castSlice(t, v)
case reflect.Map:
return castMap(t, v)
case reflect.Struct:
return castStruct(t, v)
}
return v, nil
}
func castInt(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(int64(v.Uint())), nil
case reflect.String:
i64, err := strconv.ParseInt(v.String(), 10, 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(i64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(int64(1)), nil
}
return reflect.ValueOf(int64(0)), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(int64(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castInt(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to int64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castInt(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to int64 from empty slice")
case reflect.Interface:
return castInt(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to int64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to int64 from struct")
case reflect.Ptr:
return castInt(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to int64 from %s", v.Type().Kind())
}
func castUint(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(uint64(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v, nil
case reflect.String:
u64, err := strconv.ParseUint(v.String(), 10, 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(u64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(uint64(1)), nil
}
return reflect.ValueOf(uint64(0)), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(uint64(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castUint(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to uint64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castUint(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to uint64 from empty slice")
case reflect.Interface:
return castUint(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to uint64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to uint64 from struct")
case reflect.Ptr:
return castUint(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to uint64 from %s", v.Type().Kind())
}
func castString(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(fmt.Sprint(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(fmt.Sprint(v.Uint())), nil
case reflect.String:
return v, nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf("true"), nil
}
return reflect.ValueOf("false"), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(fmt.Sprint(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castString(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castString(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
case reflect.Interface:
return castString(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to string from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to string from struct")
case reflect.Ptr:
return castString(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to string from %s", v.Type().Kind())
}
func castBool(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch v.Int() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch v.Uint() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Uint())
case reflect.String:
b, err := strconv.ParseBool(v.String())
if err != nil {
return nilValue, err
}
return reflect.ValueOf(b), nil
case reflect.Bool:
return v, nil
case reflect.Float32, reflect.Float64:
switch v.Float() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %f", v.Float())
case reflect.Array:
if v.Len() > 0 {
return castBool(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castBool(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
case reflect.Interface:
return castBool(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to string from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to string from struct")
case reflect.Ptr:
return castBool(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to bool from %s", v.Type().Kind())
}
func castFloat(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(float64(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(float64(v.Uint())), nil
case reflect.String:
f64, err := strconv.ParseFloat(v.String(), 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(f64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(float64(1)), nil
}
return reflect.ValueOf(float64(0)), nil
case reflect.Float32, reflect.Float64:
return v, nil
case reflect.Array:
if v.Len() > 0 {
return castFloat(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to float64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castFloat(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to float64 from empty slice")
case reflect.Interface:
return castFloat(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to float64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to float64 from struct")
case reflect.Ptr:
return castFloat(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to float64 from %s", v.Type().Kind())
}
func castArray(t reflect.Type, v reflect.Value) (reflect.Value, error) {
kind := v.Type().Kind()
if kind == reflect.Interface {
return castArray(t, reflect.ValueOf(v.Interface()))
}
if kind != reflect.Slice && kind != reflect.Array {
return nilValue, fmt.Errorf("failed to cast to array from %s", kind)
}
if t.Elem() == v.Type().Elem() {
return v, nil
}
if t.Len() != v.Len() {
return nilValue, fmt.Errorf("failed to cast [%d]array from slice of %d length", t.Len(), v.Len())
}
ret := reflect.New(t).Elem()
for i := 0; i < v.Len(); i++ {
vv, err := castValue(t.Elem(), v.Index(i))
if err != nil {
return nilValue, err
}
ret.Index(i).Set(vv)
}
return ret, nil
}
func castSlice(t reflect.Type, v reflect.Value) (reflect.Value, error) {
kind := v.Type().Kind()
if kind == reflect.Interface {
return castSlice(t, reflect.ValueOf(v.Interface()))
}
if kind != reflect.Slice && kind != reflect.Array {
return nilValue, fmt.Errorf("failed to cast to slice from %s", kind)
}
if t.Elem() == v.Type().Elem() {
return v, nil
}
ret := reflect.MakeSlice(t, v.Len(), v.Len())
for i := 0; i < v.Len(); i++ {
vv, err := castValue(t.Elem(), v.Index(i))
if err != nil {
return nilValue, err
}
ret.Index(i).Set(vv)
}
return ret, nil
}
func castMap(t reflect.Type, v reflect.Value) (reflect.Value, error) {
ret := reflect.MakeMap(t)
switch v.Type().Kind() {
case reflect.Map:
iter := v.MapRange()
for iter.Next() {
key, err := castValue(t.Key(), iter.Key())
if err != nil {
return nilValue, err
}
value, err := castValue(t.Elem(), iter.Value())
if err != nil {
return nilValue, err
}
ret.SetMapIndex(key, value)
}
return ret, nil
case reflect.Interface:
return castMap(t, reflect.ValueOf(v.Interface()))
case reflect.Slice:
if v.Len() > 0 {
return castMap(t, v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to map from empty slice")
}
return nilValue, fmt.Errorf("failed to cast to map from %s", v.Type().Kind())
}
func castStruct(t reflect.Type, v reflect.Value) (reflect.Value, error) {
ret := reflect.New(t).Elem()
switch v.Type().Kind() {
case reflect.Map:
iter := v.MapRange()
for iter.Next() {
key := iter.Key()
k, err := castString(key)
if err != nil {
return nilValue, err
}
fieldName := k.String()
field, ok := t.FieldByName(fieldName)
if ok {
value, err := castValue(field.Type, iter.Value())
if err != nil {
return nilValue, err
}
ret.FieldByName(fieldName).Set(value)
}
}
return ret, nil
case reflect.Struct:
for i := 0; i < v.Type().NumField(); i++ {
name := v.Type().Field(i).Name
ret.FieldByName(name).Set(v.FieldByName(name))
}
return ret, nil
case reflect.Interface:
return castStruct(t, reflect.ValueOf(v.Interface()))
case reflect.Slice:
if v.Len() > 0 {
return castStruct(t, v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to struct from empty slice")
default:
return nilValue, fmt.Errorf("failed to cast to struct from %s", v.Type().Kind())
}
}

View File

@ -1,6 +1,7 @@
package decoder package decoder
import ( import (
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
@ -76,3 +77,7 @@ func (d *boolDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
} }
return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor) return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor)
} }
func (d *boolDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: bool decoder does not support decode path")
}

View File

@ -2,6 +2,7 @@ package decoder
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
@ -78,6 +79,10 @@ func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
return cursor, nil return cursor, nil
} }
func (d *bytesDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: []byte decoder does not support decode path")
}
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) { func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
c := s.skipWhiteSpace() c := s.skipWhiteSpace()
if c == '[' { if c == '[' {

View File

@ -156,3 +156,15 @@ func (d *floatDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
d.op(p, f64) d.op(p, f64)
return cursor, nil return cursor, nil
} }
func (d *floatDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
bytes, c, err := d.decodeByte(buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}

View File

@ -2,6 +2,7 @@ package decoder
import ( import (
"bytes" "bytes"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
@ -139,3 +140,7 @@ func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
} }
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor) return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
} }
func (d *funcDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: func decoder does not support decode path")
}

View File

@ -240,3 +240,7 @@ func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
d.op(p, i64) d.op(p, i64)
return cursor, nil return cursor, nil
} }
func (d *intDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: int decoder does not support decode path")
}

View File

@ -94,6 +94,7 @@ func (d *interfaceDecoder) numDecoder(s *Stream) Decoder {
var ( var (
emptyInterfaceType = runtime.Type2RType(reflect.TypeOf((*interface{})(nil)).Elem()) emptyInterfaceType = runtime.Type2RType(reflect.TypeOf((*interface{})(nil)).Elem())
EmptyInterfaceType = emptyInterfaceType
interfaceMapType = runtime.Type2RType( interfaceMapType = runtime.Type2RType(
reflect.TypeOf((*map[string]interface{})(nil)).Elem(), reflect.TypeOf((*map[string]interface{})(nil)).Elem(),
) )
@ -456,3 +457,72 @@ func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, dep
} }
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor) return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
} }
func NewPathDecoder() Decoder {
ifaceDecoder := &interfaceDecoder{
typ: emptyInterfaceType,
structName: "",
fieldName: "",
floatDecoder: newFloatDecoder("", "", func(p unsafe.Pointer, v float64) {
*(*interface{})(p) = v
}),
numberDecoder: newNumberDecoder("", "", func(p unsafe.Pointer, v json.Number) {
*(*interface{})(p) = v
}),
stringDecoder: newStringDecoder("", ""),
}
ifaceDecoder.sliceDecoder = newSliceDecoder(
ifaceDecoder,
emptyInterfaceType,
emptyInterfaceType.Size(),
"", "",
)
ifaceDecoder.mapDecoder = newMapDecoder(
interfaceMapType,
stringType,
ifaceDecoder.stringDecoder,
interfaceMapType.Elem(),
ifaceDecoder,
"", "",
)
return ifaceDecoder
}
var (
truebytes = []byte("true")
falsebytes = []byte("false")
)
func (d *interfaceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case '{':
return d.mapDecoder.DecodePath(ctx, cursor, depth)
case '[':
return d.sliceDecoder.DecodePath(ctx, cursor, depth)
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return d.floatDecoder.DecodePath(ctx, cursor, depth)
case '"':
return d.stringDecoder.DecodePath(ctx, cursor, depth)
case 't':
if err := validateTrue(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{truebytes}, cursor, nil
case 'f':
if err := validateFalse(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 5
return [][]byte{falsebytes}, cursor, nil
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
}
return nil, cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}

View File

@ -43,3 +43,13 @@ func (d *invalidDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsa
Field: d.fieldName, Field: d.fieldName,
} }
} }
func (d *invalidDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: cursor,
Struct: d.structName,
Field: d.fieldName,
}
}

View File

@ -88,7 +88,7 @@ func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro
mapValue = makemap(d.mapType, 0) mapValue = makemap(d.mapType, 0)
} }
s.cursor++ s.cursor++
if s.equalChar('}') { if s.skipWhiteSpace() == '}' {
*(*unsafe.Pointer)(p) = mapValue *(*unsafe.Pointer)(p) = mapValue
s.cursor++ s.cursor++
return nil return nil
@ -185,3 +185,96 @@ func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
cursor++ cursor++
} }
} }
func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
cursor = skipWhiteSpace(buf, cursor)
buflen := int64(len(buf))
if buflen < 2 {
return nil, 0, errors.ErrExpected("{} for map", cursor)
}
switch buf[cursor] {
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
case '{':
default:
return nil, 0, errors.ErrExpected("{ character for map value", cursor)
}
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return nil, cursor, nil
}
keyDecoder, ok := d.keyDecoder.(*stringDecoder)
if !ok {
return nil, 0, &errors.UnmarshalTypeError{
Value: "string",
Type: reflect.TypeOf(""),
Offset: cursor,
Struct: d.structName,
Field: d.fieldName,
}
}
ret := [][]byte{}
for {
key, keyCursor, err := keyDecoder.decodeByte(buf, cursor)
if err != nil {
return nil, 0, err
}
cursor = skipWhiteSpace(buf, keyCursor)
if buf[cursor] != ':' {
return nil, 0, errors.ErrExpected("colon after object key", cursor)
}
cursor++
child, found, err := ctx.Option.Path.Field(string(key))
if err != nil {
return nil, 0, err
}
if found {
if child != nil {
oldPath := ctx.Option.Path.node
ctx.Option.Path.node = child
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
if err != nil {
return nil, 0, err
}
ctx.Option.Path.node = oldPath
ret = append(ret, paths...)
cursor = c
} else {
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
ret = append(ret, buf[start:end])
cursor = end
}
} else {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
cursor = c
}
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return ret, cursor, nil
}
if buf[cursor] != ',' {
return nil, 0, errors.ErrExpected("comma after object value", cursor)
}
cursor++
}
}

View File

@ -51,6 +51,17 @@ func (d *numberDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
return cursor, nil return cursor, nil
} }
func (d *numberDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}
func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) { func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
start := s.cursor start := s.cursor
for { for {

View File

@ -7,9 +7,11 @@ type OptionFlags uint8
const ( const (
FirstWinOption OptionFlags = 1 << iota FirstWinOption OptionFlags = 1 << iota
ContextOption ContextOption
PathOption
) )
type Option struct { type Option struct {
Flags OptionFlags Flags OptionFlags
Context context.Context Context context.Context
Path *Path
} }

View File

@ -0,0 +1,670 @@
package decoder
import (
"fmt"
"reflect"
"strconv"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type PathString string
func (s PathString) Build() (*Path, error) {
builder := new(PathBuilder)
return builder.Build([]rune(s))
}
type PathBuilder struct {
root PathNode
node PathNode
singleQuotePathSelector bool
doubleQuotePathSelector bool
}
func (b *PathBuilder) Build(buf []rune) (*Path, error) {
node, err := b.build(buf)
if err != nil {
return nil, err
}
return &Path{
node: node,
RootSelectorOnly: node == nil,
SingleQuotePathSelector: b.singleQuotePathSelector,
DoubleQuotePathSelector: b.doubleQuotePathSelector,
}, nil
}
func (b *PathBuilder) build(buf []rune) (PathNode, error) {
if len(buf) == 0 {
return nil, errors.ErrEmptyPath()
}
if buf[0] != '$' {
return nil, errors.ErrInvalidPath("JSON Path must start with a $ character")
}
if len(buf) == 1 {
return nil, nil
}
buf = buf[1:]
offset, err := b.buildNext(buf)
if err != nil {
return nil, err
}
if len(buf) > offset {
return nil, errors.ErrInvalidPath("remain invalid path %q", buf[offset:])
}
return b.root, nil
}
func (b *PathBuilder) buildNextCharIfExists(buf []rune, cursor int) (int, error) {
if len(buf) > cursor {
offset, err := b.buildNext(buf[cursor:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
return cursor, nil
}
func (b *PathBuilder) buildNext(buf []rune) (int, error) {
switch buf[0] {
case '.':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
offset, err := b.buildSelector(buf[1:])
if err != nil {
return 0, err
}
return offset + 1, nil
case '[':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
offset, err := b.buildIndex(buf[1:])
if err != nil {
return 0, err
}
return offset + 1, nil
default:
return 0, errors.ErrInvalidPath("expect dot or left bracket character. but found %c character", buf[0])
}
}
func (b *PathBuilder) buildSelector(buf []rune) (int, error) {
switch buf[0] {
case '.':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with double dot character")
}
offset, err := b.buildPathRecursive(buf[1:])
if err != nil {
return 0, err
}
return 1 + offset, nil
case '[', ']', '$', '*':
return 0, errors.ErrInvalidPath("found invalid path character %c after dot", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '$', '*', ']':
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
case '.':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
offset, err := b.buildSelector(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '[':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
offset, err := b.buildIndex(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '"':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with double quote character")
}
offset, err := b.buildQuoteSelector(buf[cursor+1:], DoubleQuotePathSelector)
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
}
b.addSelectorNode(string(buf))
return len(buf), nil
}
func (b *PathBuilder) buildQuoteSelector(buf []rune, sel QuotePathSelector) (int, error) {
switch buf[0] {
case '[', ']', '$', '.', '*', '\'', '"':
return 0, errors.ErrInvalidPath("found invalid path character %c after quote", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '\'':
if sel != SingleQuotePathSelector {
return 0, errors.ErrInvalidPath("found double quote character in field selector with single quote context")
}
if len(buf) <= cursor+1 {
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character in field selector context")
}
if buf[cursor+1] != ']' {
return 0, errors.ErrInvalidPath("expect right bracket for field selector with single quote but found %c", buf[cursor+1])
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
b.singleQuotePathSelector = true
return b.buildNextCharIfExists(buf, cursor+2)
case '"':
if sel != DoubleQuotePathSelector {
return 0, errors.ErrInvalidPath("found single quote character in field selector with double quote context")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
b.doubleQuotePathSelector = true
return b.buildNextCharIfExists(buf, cursor+1)
}
}
return 0, errors.ErrInvalidPath("couldn't find quote character in selector quote path context")
}
func (b *PathBuilder) buildPathRecursive(buf []rune) (int, error) {
switch buf[0] {
case '.', '[', ']', '$', '*':
return 0, errors.ErrInvalidPath("found invalid path character %c after double dot", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '$', '*', ']':
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
case '.':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
selector := buf[:cursor]
b.addRecursiveNode(string(selector))
offset, err := b.buildSelector(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '[':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
selector := buf[:cursor]
b.addRecursiveNode(string(selector))
offset, err := b.buildIndex(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
}
b.addRecursiveNode(string(buf))
return len(buf), nil
}
func (b *PathBuilder) buildIndex(buf []rune) (int, error) {
switch buf[0] {
case '.', '[', ']', '$':
return 0, errors.ErrInvalidPath("found invalid path character %c after left bracket", buf[0])
case '\'':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character")
}
offset, err := b.buildQuoteSelector(buf[1:], SingleQuotePathSelector)
if err != nil {
return 0, err
}
return 1 + offset, nil
case '*':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with star character")
}
if buf[1] != ']' {
return 0, errors.ErrInvalidPath("expect right bracket character for index all path but found %c character", buf[1])
}
b.addIndexAllNode()
offset := len("*]")
if len(buf) > 2 {
buildOffset, err := b.buildNext(buf[2:])
if err != nil {
return 0, err
}
return offset + buildOffset, nil
}
return offset, nil
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case ']':
index, err := strconv.ParseInt(string(buf[:cursor]), 10, 64)
if err != nil {
return 0, errors.ErrInvalidPath("%q is unexpected index path", buf[:cursor])
}
b.addIndexNode(int(index))
return b.buildNextCharIfExists(buf, cursor+1)
}
}
return 0, errors.ErrInvalidPath("couldn't find right bracket character in index path context")
}
func (b *PathBuilder) addIndexAllNode() {
node := newPathIndexAllNode()
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addRecursiveNode(selector string) {
node := newPathRecursiveNode(selector)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addSelectorNode(name string) {
node := newPathSelectorNode(name)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addIndexNode(idx int) {
node := newPathIndexNode(idx)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
type QuotePathSelector int
const (
SingleQuotePathSelector QuotePathSelector = 1
DoubleQuotePathSelector QuotePathSelector = 2
)
type Path struct {
node PathNode
RootSelectorOnly bool
SingleQuotePathSelector bool
DoubleQuotePathSelector bool
}
func (p *Path) Field(sel string) (PathNode, bool, error) {
if p.node == nil {
return nil, false, nil
}
return p.node.Field(sel)
}
func (p *Path) Get(src, dst reflect.Value) error {
if p.node == nil {
return nil
}
return p.node.Get(src, dst)
}
func (p *Path) String() string {
if p.node == nil {
return "$"
}
return p.node.String()
}
type PathNode interface {
fmt.Stringer
Index(idx int) (PathNode, bool, error)
Field(fieldName string) (PathNode, bool, error)
Get(src, dst reflect.Value) error
chain(PathNode) PathNode
target() bool
single() bool
}
type BasePathNode struct {
child PathNode
}
func (n *BasePathNode) chain(node PathNode) PathNode {
n.child = node
return node
}
func (n *BasePathNode) target() bool {
return n.child == nil
}
func (n *BasePathNode) single() bool {
return true
}
type PathSelectorNode struct {
*BasePathNode
selector string
}
func newPathSelectorNode(selector string) *PathSelectorNode {
return &PathSelectorNode{
BasePathNode: &BasePathNode{},
selector: selector,
}
}
func (n *PathSelectorNode) Index(idx int) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathSelectorNode) Field(fieldName string) (PathNode, bool, error) {
if n.selector == fieldName {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathSelectorNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Map:
iter := src.MapRange()
for iter.Next() {
key, ok := iter.Key().Interface().(string)
if !ok {
return fmt.Errorf("invalid map key type %T", src.Type().Key())
}
child, found, err := n.Field(key)
if err != nil {
return err
}
if found {
if child != nil {
return child.Get(iter.Value(), dst)
}
return AssignValue(iter.Value(), dst)
}
}
case reflect.Struct:
typ := src.Type()
for i := 0; i < typ.Len(); i++ {
tag := runtime.StructTagFromField(typ.Field(i))
child, found, err := n.Field(tag.Key)
if err != nil {
return err
}
if found {
if child != nil {
return child.Get(src.Field(i), dst)
}
return AssignValue(src.Field(i), dst)
}
}
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
case reflect.Float64, reflect.String, reflect.Bool:
return AssignValue(src, dst)
}
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
}
func (n *PathSelectorNode) String() string {
s := fmt.Sprintf(".%s", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}
type PathIndexNode struct {
*BasePathNode
selector int
}
func newPathIndexNode(selector int) *PathIndexNode {
return &PathIndexNode{
BasePathNode: &BasePathNode{},
selector: selector,
}
}
func (n *PathIndexNode) Index(idx int) (PathNode, bool, error) {
if n.selector == idx {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathIndexNode) Field(fieldName string) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathIndexNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Array, reflect.Slice:
if src.Len() > n.selector {
if n.child != nil {
return n.child.Get(src.Index(n.selector), dst)
}
return AssignValue(src.Index(n.selector), dst)
}
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get [%d] value from %s", n.selector, src.Type())
}
func (n *PathIndexNode) String() string {
s := fmt.Sprintf("[%d]", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}
type PathIndexAllNode struct {
*BasePathNode
}
func newPathIndexAllNode() *PathIndexAllNode {
return &PathIndexAllNode{
BasePathNode: &BasePathNode{},
}
}
func (n *PathIndexAllNode) Index(idx int) (PathNode, bool, error) {
return n.child, true, nil
}
func (n *PathIndexAllNode) Field(fieldName string) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathIndexAllNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Array, reflect.Slice:
var arr []interface{}
for i := 0; i < src.Len(); i++ {
var v interface{}
rv := reflect.ValueOf(&v)
if n.child != nil {
if err := n.child.Get(src.Index(i), rv); err != nil {
return err
}
} else {
if err := AssignValue(src.Index(i), rv); err != nil {
return err
}
}
arr = append(arr, v)
}
if err := AssignValue(reflect.ValueOf(arr), dst); err != nil {
return err
}
return nil
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get all value from %s", src.Type())
}
func (n *PathIndexAllNode) String() string {
s := "[*]"
if n.child != nil {
s += n.child.String()
}
return s
}
type PathRecursiveNode struct {
*BasePathNode
selector string
}
func newPathRecursiveNode(selector string) *PathRecursiveNode {
node := newPathSelectorNode(selector)
return &PathRecursiveNode{
BasePathNode: &BasePathNode{
child: node,
},
selector: selector,
}
}
func (n *PathRecursiveNode) Field(fieldName string) (PathNode, bool, error) {
if n.selector == fieldName {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathRecursiveNode) Index(_ int) (PathNode, bool, error) {
return n, true, nil
}
func valueToSliceValue(v interface{}) []interface{} {
rv := reflect.ValueOf(v)
ret := []interface{}{}
if rv.Type().Kind() == reflect.Slice || rv.Type().Kind() == reflect.Array {
for i := 0; i < rv.Len(); i++ {
ret = append(ret, rv.Index(i).Interface())
}
return ret
}
return []interface{}{v}
}
func (n *PathRecursiveNode) Get(src, dst reflect.Value) error {
if n.child == nil {
return fmt.Errorf("failed to get by recursive path ..%s", n.selector)
}
var arr []interface{}
switch src.Type().Kind() {
case reflect.Map:
iter := src.MapRange()
for iter.Next() {
key, ok := iter.Key().Interface().(string)
if !ok {
return fmt.Errorf("invalid map key type %T", src.Type().Key())
}
child, found, err := n.Field(key)
if err != nil {
return err
}
if found {
var v interface{}
rv := reflect.ValueOf(&v)
_ = child.Get(iter.Value(), rv)
arr = append(arr, valueToSliceValue(v)...)
} else {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(iter.Value(), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Struct:
typ := src.Type()
for i := 0; i < typ.Len(); i++ {
tag := runtime.StructTagFromField(typ.Field(i))
child, found, err := n.Field(tag.Key)
if err != nil {
return err
}
if found {
var v interface{}
rv := reflect.ValueOf(&v)
_ = child.Get(src.Field(i), rv)
arr = append(arr, valueToSliceValue(v)...)
} else {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(src.Field(i), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Array, reflect.Slice:
for i := 0; i < src.Len(); i++ {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(src.Index(i), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
}
func (n *PathRecursiveNode) String() string {
s := fmt.Sprintf("..%s", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}

View File

@ -1,6 +1,7 @@
package decoder package decoder
import ( import (
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
@ -34,6 +35,10 @@ func (d *ptrDecoder) contentDecoder() Decoder {
//go:linkname unsafe_New reflect.unsafe_New //go:linkname unsafe_New reflect.unsafe_New
func unsafe_New(*runtime.Type) unsafe.Pointer func unsafe_New(*runtime.Type) unsafe.Pointer
func UnsafeNew(t *runtime.Type) unsafe.Pointer {
return unsafe_New(t)
}
func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
if s.skipWhiteSpace() == nul { if s.skipWhiteSpace() == nul {
s.read() s.read()
@ -85,3 +90,7 @@ func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
cursor = c cursor = c
return cursor, nil return cursor, nil
} }
func (d *ptrDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: ptr decoder does not support decode path")
}

View File

@ -299,3 +299,82 @@ func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
} }
} }
} }
func (d *sliceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
ret := [][]byte{}
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
case '[':
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == ']' {
cursor++
return ret, cursor, nil
}
idx := 0
for {
child, found, err := ctx.Option.Path.node.Index(idx)
if err != nil {
return nil, 0, err
}
if found {
if child != nil {
oldPath := ctx.Option.Path.node
ctx.Option.Path.node = child
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
if err != nil {
return nil, 0, err
}
ctx.Option.Path.node = oldPath
ret = append(ret, paths...)
cursor = c
} else {
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
ret = append(ret, buf[start:end])
cursor = end
}
} else {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
cursor = c
}
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case ']':
cursor++
return ret, cursor, nil
case ',':
idx++
default:
return nil, 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
}
cursor++
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return nil, 0, d.errNumber(cursor)
default:
return nil, 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
}
}
}

View File

@ -60,6 +60,17 @@ func (d *stringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
return cursor, nil return cursor, nil
} }
func (d *stringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}
var ( var (
hexToInt = [256]int{ hexToInt = [256]int{
'0': 0, '0': 0,

View File

@ -51,6 +51,14 @@ func init() {
} }
} }
func toASCIILower(s string) string {
b := []byte(s)
for i := range b {
b[i] = largeToSmallTable[b[i]]
}
return string(b)
}
func newStructDecoder(structName, fieldName string, fieldMap map[string]*structFieldSet) *structDecoder { func newStructDecoder(structName, fieldName string, fieldMap map[string]*structFieldSet) *structDecoder {
return &structDecoder{ return &structDecoder{
fieldMap: fieldMap, fieldMap: fieldMap,
@ -91,6 +99,10 @@ func (d *structDecoder) tryOptimize() {
for k, v := range d.fieldMap { for k, v := range d.fieldMap {
key := strings.ToLower(k) key := strings.ToLower(k)
if key != k { if key != k {
if key != toASCIILower(k) {
d.isTriedOptimize = true
return
}
// already exists same key (e.g. Hello and HELLO has same lower case key // already exists same key (e.g. Hello and HELLO has same lower case key
if _, exists := conflicted[key]; exists { if _, exists := conflicted[key]; exists {
d.isTriedOptimize = true d.isTriedOptimize = true
@ -158,49 +170,53 @@ func (d *structDecoder) tryOptimize() {
} }
// decode from '\uXXXX' // decode from '\uXXXX'
func decodeKeyCharByUnicodeRune(buf []byte, cursor int64) ([]byte, int64) { func decodeKeyCharByUnicodeRune(buf []byte, cursor int64) ([]byte, int64, error) {
const defaultOffset = 4 const defaultOffset = 4
const surrogateOffset = 6 const surrogateOffset = 6
if cursor+defaultOffset >= int64(len(buf)) {
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
r := unicodeToRune(buf[cursor : cursor+defaultOffset]) r := unicodeToRune(buf[cursor : cursor+defaultOffset])
if utf16.IsSurrogate(r) { if utf16.IsSurrogate(r) {
cursor += defaultOffset cursor += defaultOffset
if cursor+surrogateOffset >= int64(len(buf)) || buf[cursor] != '\\' || buf[cursor+1] != 'u' { if cursor+surrogateOffset >= int64(len(buf)) || buf[cursor] != '\\' || buf[cursor+1] != 'u' {
return []byte(string(unicode.ReplacementChar)), cursor + defaultOffset - 1 return []byte(string(unicode.ReplacementChar)), cursor + defaultOffset - 1, nil
} }
cursor += 2 cursor += 2
r2 := unicodeToRune(buf[cursor : cursor+defaultOffset]) r2 := unicodeToRune(buf[cursor : cursor+defaultOffset])
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar { if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
return []byte(string(r)), cursor + defaultOffset - 1 return []byte(string(r)), cursor + defaultOffset - 1, nil
} }
} }
return []byte(string(r)), cursor + defaultOffset - 1 return []byte(string(r)), cursor + defaultOffset - 1, nil
} }
func decodeKeyCharByEscapedChar(buf []byte, cursor int64) ([]byte, int64) { func decodeKeyCharByEscapedChar(buf []byte, cursor int64) ([]byte, int64, error) {
c := buf[cursor] c := buf[cursor]
cursor++ cursor++
switch c { switch c {
case '"': case '"':
return []byte{'"'}, cursor return []byte{'"'}, cursor, nil
case '\\': case '\\':
return []byte{'\\'}, cursor return []byte{'\\'}, cursor, nil
case '/': case '/':
return []byte{'/'}, cursor return []byte{'/'}, cursor, nil
case 'b': case 'b':
return []byte{'\b'}, cursor return []byte{'\b'}, cursor, nil
case 'f': case 'f':
return []byte{'\f'}, cursor return []byte{'\f'}, cursor, nil
case 'n': case 'n':
return []byte{'\n'}, cursor return []byte{'\n'}, cursor, nil
case 'r': case 'r':
return []byte{'\r'}, cursor return []byte{'\r'}, cursor, nil
case 't': case 't':
return []byte{'\t'}, cursor return []byte{'\t'}, cursor, nil
case 'u': case 'u':
return decodeKeyCharByUnicodeRune(buf, cursor) return decodeKeyCharByUnicodeRune(buf, cursor)
} }
return nil, cursor return nil, cursor, nil
} }
func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) { func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
@ -242,7 +258,10 @@ func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64,
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor) return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
case '\\': case '\\':
cursor++ cursor++
chars, nextCursor := decodeKeyCharByEscapedChar(buf, cursor) chars, nextCursor, err := decodeKeyCharByEscapedChar(buf, cursor)
if err != nil {
return 0, nil, err
}
for _, c := range chars { for _, c := range chars {
curBit &= bitmap[keyIdx][largeToSmallTable[c]] curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 { if curBit == 0 {
@ -305,7 +324,10 @@ func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64,
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor) return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
case '\\': case '\\':
cursor++ cursor++
chars, nextCursor := decodeKeyCharByEscapedChar(buf, cursor) chars, nextCursor, err := decodeKeyCharByEscapedChar(buf, cursor)
if err != nil {
return 0, nil, err
}
for _, c := range chars { for _, c := range chars {
curBit &= bitmap[keyIdx][largeToSmallTable[c]] curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 { if curBit == 0 {
@ -817,3 +839,7 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
cursor++ cursor++
} }
} }
func (d *structDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: struct decoder does not support decode path")
}

View File

@ -10,6 +10,7 @@ import (
type Decoder interface { type Decoder interface {
Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error) Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error)
DecodePath(*RuntimeContext, int64, int64) ([][]byte, int64, error)
DecodeStream(*Stream, int64, unsafe.Pointer) error DecodeStream(*Stream, int64, unsafe.Pointer) error
} }

View File

@ -188,3 +188,7 @@ func (d *uintDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
d.op(p, u64) d.op(p, u64)
return cursor, nil return cursor, nil
} }
func (d *uintDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: uint decoder does not support decode path")
}

View File

@ -3,6 +3,7 @@ package decoder
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
@ -97,3 +98,7 @@ func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
} }
return end, nil return end, nil
} }
func (d *unmarshalJSONDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: unmarshal json decoder does not support decode path")
}

View File

@ -3,6 +3,7 @@ package decoder
import ( import (
"bytes" "bytes"
"encoding" "encoding"
"fmt"
"unicode" "unicode"
"unicode/utf16" "unicode/utf16"
"unicode/utf8" "unicode/utf8"
@ -142,6 +143,10 @@ func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
return end, nil return end, nil
} }
func (d *unmarshalTextDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: unmarshal text decoder does not support decode path")
}
func unquoteBytes(s []byte) (t []byte, ok bool) { func unquoteBytes(s []byte) (t []byte, ok bool) {
length := len(s) length := len(s)
if length < 2 || s[0] != '"' || s[length-1] != '"' { if length < 2 || s[0] != '"' || s[length-1] != '"' {

View File

@ -1,6 +1,7 @@
package decoder package decoder
import ( import (
"fmt"
"reflect" "reflect"
"unsafe" "unsafe"
@ -66,3 +67,7 @@ func (d *wrappedStringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
ctx.Buf = oldBuf ctx.Buf = oldBuf
return c, nil return c, nil
} }
func (d *wrappedStringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: wrapped string decoder does not support decode path")
}

View File

@ -397,7 +397,10 @@ func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *
func (c *StructCode) lastAnonymousFieldCode(firstField *Opcode) *Opcode { func (c *StructCode) lastAnonymousFieldCode(firstField *Opcode) *Opcode {
// firstField is special StructHead operation for anonymous structure. // firstField is special StructHead operation for anonymous structure.
// So, StructHead's next operation is truly struct head operation. // So, StructHead's next operation is truly struct head operation.
lastField := firstField.Next for firstField.Op == OpStructHead {
firstField = firstField.Next
}
lastField := firstField
for lastField.NextField != nil { for lastField.NextField != nil {
lastField = lastField.NextField lastField = lastField.NextField
} }
@ -437,11 +440,6 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
} }
if isEndField { if isEndField {
endField := fieldCodes.Last() endField := fieldCodes.Last()
if isEmbeddedStruct(field) {
firstField.End = endField
lastField := c.lastAnonymousFieldCode(firstField)
lastField.NextField = endField
}
if len(codes) > 0 { if len(codes) > 0 {
codes.First().End = endField codes.First().End = endField
} else { } else {
@ -698,7 +696,15 @@ func (c *StructFieldCode) addStructEndCode(ctx *compileContext, codes Opcodes) O
Indent: ctx.indent, Indent: ctx.indent,
} }
codes.Last().Next = end codes.Last().Next = end
codes.First().NextField = end code := codes.First()
for code.Op == OpStructField || code.Op == OpStructHead {
code = code.Next
}
for code.NextField != nil {
code = code.NextField
}
code.NextField = end
codes = codes.Add(end) codes = codes.Add(end)
ctx.incOpcodeIndex() ctx.incOpcodeIndex()
return codes return codes

View File

@ -506,8 +506,6 @@ func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) { func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
switch { switch {
case c.implementsMarshalJSON(typ):
return c.marshalJSONCode(typ)
case c.implementsMarshalText(typ): case c.implementsMarshalText(typ):
return c.marshalTextCode(typ) return c.marshalTextCode(typ)
} }
@ -619,6 +617,13 @@ func (c *Compiler) structCode(typ *runtime.Type, isPtr bool) (*StructCode, error
return code, nil return code, nil
} }
func toElemType(t *runtime.Type) *runtime.Type {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTag, isPtr, isOnlyOneFirstField bool) (*StructFieldCode, error) { func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTag, isPtr, isOnlyOneFirstField bool) (*StructFieldCode, error) {
field := tag.Field field := tag.Field
fieldType := runtime.Type2RType(field.Type) fieldType := runtime.Type2RType(field.Type)
@ -628,7 +633,7 @@ func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTa
key: tag.Key, key: tag.Key,
tag: tag, tag: tag,
offset: field.Offset, offset: field.Offset,
isAnonymous: field.Anonymous && !tag.IsTaggedKey, isAnonymous: field.Anonymous && !tag.IsTaggedKey && toElemType(fieldType).Kind() == reflect.Struct,
isTaggedKey: tag.IsTaggedKey, isTaggedKey: tag.IsTaggedKey,
isNilableType: c.isNilableType(fieldType), isNilableType: c.isNilableType(fieldType),
isNilCheck: true, isNilCheck: true,

View File

@ -189,7 +189,7 @@ func appendNullComma(ctx *encoder.RuntimeContext, b []byte) []byte {
} }
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte { func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ') return append(b[:len(b)-2], ':', ' ')
} }
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte { func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
@ -229,8 +229,9 @@ func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1 last := len(b) - 1
b[last] = '\n' // replace comma to newline
b = appendIndent(ctx, b, code.Indent-1) b[last-1] = '\n'
b = appendIndent(ctx, b[:last], code.Indent)
return append(b, '}', ',', '\n') return append(b, '}', ',', '\n')
} }

View File

@ -133,7 +133,7 @@ func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte {
} }
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte { func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ') return append(b[:len(b)-2], ':', ' ')
} }
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte { func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
@ -173,8 +173,9 @@ func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1 last := len(b) - 1
b[last] = '\n' // replace comma to newline
b = appendIndent(ctx, b, code.Indent-1) b[last-1] = '\n'
b = appendIndent(ctx, b[:last], code.Indent)
return append(b, '}', ',', '\n') return append(b, '}', ',', '\n')
} }

View File

@ -162,3 +162,22 @@ func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError {
Offset: cursor, Offset: cursor,
} }
} }
type PathError struct {
msg string
}
func (e *PathError) Error() string {
return fmt.Sprintf("json: invalid path format: %s", e.msg)
}
func ErrInvalidPath(msg string, args ...interface{}) *PathError {
if len(args) != 0 {
return &PathError{msg: fmt.Sprintf(msg, args...)}
}
return &PathError{msg: msg}
}
func ErrEmptyPath() *PathError {
return &PathError{msg: "path is empty"}
}

84
vendor/github.com/goccy/go-json/path.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
package json
import (
"reflect"
"github.com/goccy/go-json/internal/decoder"
)
// CreatePath creates JSON Path.
//
// JSON Path rule
// $ : root object or element. The JSON Path format must start with this operator, which refers to the outermost level of the JSON-formatted string.
// . : child operator. You can identify child values using dot-notation.
// .. : recursive descent.
// [] : subscript operator. If the JSON object is an array, you can use brackets to specify the array index.
// [*] : all objects/elements for array.
//
// Reserved words must be properly escaped when included in Path.
//
// Escape Rule
// single quote style escape: e.g.) `$['a.b'].c`
// double quote style escape: e.g.) `$."a.b".c`
func CreatePath(p string) (*Path, error) {
path, err := decoder.PathString(p).Build()
if err != nil {
return nil, err
}
return &Path{path: path}, nil
}
// Path represents JSON Path.
type Path struct {
path *decoder.Path
}
// RootSelectorOnly whether only the root selector ($) is used.
func (p *Path) RootSelectorOnly() bool {
return p.path.RootSelectorOnly
}
// UsedSingleQuotePathSelector whether single quote-based escaping was done when building the JSON Path.
func (p *Path) UsedSingleQuotePathSelector() bool {
return p.path.SingleQuotePathSelector
}
// UsedSingleQuotePathSelector whether double quote-based escaping was done when building the JSON Path.
func (p *Path) UsedDoubleQuotePathSelector() bool {
return p.path.DoubleQuotePathSelector
}
// Extract extracts a specific JSON string.
func (p *Path) Extract(data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
return extractFromPath(p, data, optFuncs...)
}
// PathString returns original JSON Path string.
func (p *Path) PathString() string {
return p.path.String()
}
// Unmarshal extract and decode the value of the part corresponding to JSON Path from the input data.
func (p *Path) Unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
contents, err := extractFromPath(p, data, optFuncs...)
if err != nil {
return err
}
results := make([]interface{}, 0, len(contents))
for _, content := range contents {
var result interface{}
if err := Unmarshal(content, &result); err != nil {
return err
}
results = append(results, result)
}
if err := decoder.AssignValue(reflect.ValueOf(results), reflect.ValueOf(v)); err != nil {
return err
}
return nil
}
// Get extract and substitute the value of the part corresponding to JSON Path from the input value.
func (p *Path) Get(src, dst interface{}) error {
return p.path.Get(reflect.ValueOf(src), reflect.ValueOf(dst))
}

View File

@ -1,21 +0,0 @@
arch:
- amd64
- ppc64le
language: go
go:
- 1.14.x
- 1.15.x
- tip
env:
- GO111MODULE=on
install:
- go get -v golang.org/x/lint/golint
script:
- golint -set_exit_status ./...
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
matrix:
allow_failures:
- go: tip

View File

@ -1,5 +1,69 @@
# Changelog # Changelog
## v4.10.2 - 2023-02-22
**Security**
* `filepath.Clean` behaviour has changed in Go 1.20 - adapt to it [#2406](https://github.com/labstack/echo/pull/2406)
* Add `middleware.CORSConfig.UnsafeWildcardOriginWithAllowCredentials` to make UNSAFE usages of wildcard origin + allow cretentials less likely [#2405](https://github.com/labstack/echo/pull/2405)
**Enhancements**
* Add more HTTP error values [#2277](https://github.com/labstack/echo/pull/2277)
## v4.10.1 - 2023-02-19
**Security**
* Upgrade deps due to the latest golang.org/x/net vulnerability [#2402](https://github.com/labstack/echo/pull/2402)
**Enhancements**
* Add new JWT repository to the README [#2377](https://github.com/labstack/echo/pull/2377)
* Return an empty string for ctx.path if there is no registered path [#2385](https://github.com/labstack/echo/pull/2385)
* Add context timeout middleware [#2380](https://github.com/labstack/echo/pull/2380)
* Update link to jaegertracing [#2394](https://github.com/labstack/echo/pull/2394)
## v4.10.0 - 2022-12-27
**Security**
* We are deprecating JWT middleware in this repository. Please use https://github.com/labstack/echo-jwt instead.
JWT middleware is moved to separate repository to allow us to bump/upgrade version of JWT implementation (`github.com/golang-jwt/jwt`) we are using
which we can not do in Echo core because this would break backwards compatibility guarantees we try to maintain.
* This minor version bumps minimum Go version to 1.17 (from 1.16) due `golang.org/x/` packages we depend on. There are
several vulnerabilities fixed in these libraries.
Echo still tries to support last 4 Go versions but there are occasions we can not guarantee this promise.
**Enhancements**
* Bump x/text to 0.3.8 [#2305](https://github.com/labstack/echo/pull/2305)
* Bump dependencies and add notes about Go releases we support [#2336](https://github.com/labstack/echo/pull/2336)
* Add helper interface for ProxyBalancer interface [#2316](https://github.com/labstack/echo/pull/2316)
* Expose `middleware.CreateExtractors` function so we can use it from echo-contrib repository [#2338](https://github.com/labstack/echo/pull/2338)
* Refactor func(Context) error to HandlerFunc [#2315](https://github.com/labstack/echo/pull/2315)
* Improve function comments [#2329](https://github.com/labstack/echo/pull/2329)
* Add new method HTTPError.WithInternal [#2340](https://github.com/labstack/echo/pull/2340)
* Replace io/ioutil package usages [#2342](https://github.com/labstack/echo/pull/2342)
* Add staticcheck to CI flow [#2343](https://github.com/labstack/echo/pull/2343)
* Replace relative path determination from proprietary to std [#2345](https://github.com/labstack/echo/pull/2345)
* Remove square brackets from ipv6 addresses in XFF (X-Forwarded-For header) [#2182](https://github.com/labstack/echo/pull/2182)
* Add testcases for some BodyLimit middleware configuration options [#2350](https://github.com/labstack/echo/pull/2350)
* Additional configuration options for RequestLogger and Logger middleware [#2341](https://github.com/labstack/echo/pull/2341)
* Add route to request log [#2162](https://github.com/labstack/echo/pull/2162)
* GitHub Workflows security hardening [#2358](https://github.com/labstack/echo/pull/2358)
* Add govulncheck to CI and bump dependencies [#2362](https://github.com/labstack/echo/pull/2362)
* Fix rate limiter docs [#2366](https://github.com/labstack/echo/pull/2366)
* Refactor how `e.Routes()` work and introduce `e.OnAddRouteHandler` callback [#2337](https://github.com/labstack/echo/pull/2337)
## v4.9.1 - 2022-10-12 ## v4.9.1 - 2022-10-12
**Fixes** **Fixes**

View File

@ -10,8 +10,10 @@ check: lint vet race ## Check project
init: init:
@go install golang.org/x/lint/golint@latest @go install golang.org/x/lint/golint@latest
@go install honnef.co/go/tools/cmd/staticcheck@latest
lint: ## Lint the files lint: ## Lint the files
@staticcheck ${PKG_LIST}
@golint -set_exit_status ${PKG_LIST} @golint -set_exit_status ${PKG_LIST}
vet: ## Vet the files vet: ## Vet the files
@ -29,6 +31,6 @@ benchmark: ## Run benchmarks
help: ## Display this help screen help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
goversion ?= "1.16" goversion ?= "1.17"
test_version: ## Run tests inside Docker with given version (defaults to 1.15 oldest supported). Example: make test_version goversion=1.16 test_version: ## Run tests inside Docker with given version (defaults to 1.17 oldest supported). Example: make test_version goversion=1.17
@docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check" @docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check"

View File

@ -11,12 +11,12 @@
## Supported Go versions ## Supported Go versions
Latest version of Echo supports last four Go major [releases](https://go.dev/doc/devel/release) and might work with older versions. Latest version of Echo supports last four Go major [releases](https://go.dev/doc/devel/release) and might work with
older versions.
As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules). As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules).
Therefore a Go version capable of understanding /vN suffixed imports is required: Therefore a Go version capable of understanding /vN suffixed imports is required:
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
way of using Echo going forward. way of using Echo going forward.
@ -90,11 +90,22 @@ func hello(c echo.Context) error {
} }
``` ```
# Third-party middlewares # Official middleware repositories
Following list of middleware is maintained by Echo team.
| Repository | Description | | Repository | Description |
|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [github.com/labstack/echo-contrib](https://github.com/labstack/echo-contrib) | (by Echo team) [casbin](https://github.com/casbin/casbin), [gorilla/sessions](https://github.com/gorilla/sessions), [jaegertracing](github.com/uber/jaeger-client-go), [prometheus](https://github.com/prometheus/client_golang/), [pprof](https://pkg.go.dev/net/http/pprof), [zipkin](https://github.com/openzipkin/zipkin-go) middlewares | | [github.com/labstack/echo-jwt](https://github.com/labstack/echo-jwt) | [JWT](https://github.com/golang-jwt/jwt) middleware |
| [github.com/labstack/echo-contrib](https://github.com/labstack/echo-contrib) | [casbin](https://github.com/casbin/casbin), [gorilla/sessions](https://github.com/gorilla/sessions), [jaegertracing](https://github.com/uber/jaeger-client-go), [prometheus](https://github.com/prometheus/client_golang/), [pprof](https://pkg.go.dev/net/http/pprof), [zipkin](https://github.com/openzipkin/zipkin-go) middlewares |
# Third-party middleware repositories
Be careful when adding 3rd party middleware. Echo teams does not have time or manpower to guarantee safety and quality
of middlewares in this list.
| Repository | Description |
|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen) | Automatically generate RESTful API documentation with [OpenAPI](https://swagger.io/specification/) Client and Server Code Generator | | [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen) | Automatically generate RESTful API documentation with [OpenAPI](https://swagger.io/specification/) Client and Server Code Generator |
| [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/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/ziflex/lecho](https://github.com/ziflex/lecho) | [Zerolog](https://github.com/rs/zerolog) logging library wrapper for Echo logger interface. |

View File

@ -169,7 +169,11 @@ type (
// Redirect redirects the request to a provided URL with status code. // Redirect redirects the request to a provided URL with status code.
Redirect(code int, url string) error Redirect(code int, url string) error
// Error invokes the registered HTTP error handler. Generally used by middleware. // Error invokes the registered global HTTP error handler. Generally used by middleware.
// A side-effect of calling global error handler is that now Response has been committed (sent to the client) and
// middlewares up in chain can not change Response status code or Response body anymore.
//
// Avoid using this method in handlers as no middleware will be able to effectively handle errors after that.
Error(err error) Error(err error)
// Handler returns the matched handler by router. // Handler returns the matched handler by router.
@ -282,11 +286,16 @@ func (c *context) RealIP() string {
if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" {
i := strings.IndexAny(ip, ",") i := strings.IndexAny(ip, ",")
if i > 0 { if i > 0 {
return strings.TrimSpace(ip[:i]) xffip := strings.TrimSpace(ip[:i])
xffip = strings.TrimPrefix(xffip, "[")
xffip = strings.TrimSuffix(xffip, "]")
return xffip
} }
return ip return ip
} }
if ip := c.request.Header.Get(HeaderXRealIP); ip != "" { if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
ip = strings.TrimPrefix(ip, "[")
ip = strings.TrimSuffix(ip, "]")
return ip return ip
} }
ra, _, _ := net.SplitHostPort(c.request.RemoteAddr) ra, _, _ := net.SplitHostPort(c.request.RemoteAddr)

View File

@ -37,16 +37,15 @@ Learn more at https://echo.labstack.com
package echo package echo
import ( import (
"bytes"
stdContext "context" stdContext "context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
stdLog "log" stdLog "log"
"net" "net"
"net/http" "net/http"
"os"
"reflect" "reflect"
"runtime" "runtime"
"sync" "sync"
@ -62,20 +61,28 @@ import (
type ( type (
// Echo is the top-level framework instance. // Echo is the top-level framework instance.
//
// Goroutine safety: Do not mutate Echo instance fields after server has started. Accessing these
// fields from handlers/middlewares and changing field values at the same time leads to data-races.
// Adding new routes after the server has been started is also not safe!
Echo struct { Echo struct {
filesystem filesystem
common common
// startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get // startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
// listener address info (on which interface/port was listener binded) without having data races. // listener address info (on which interface/port was listener binded) without having data races.
startupMutex sync.RWMutex startupMutex sync.RWMutex
StdLogger *stdLog.Logger
colorer *color.Color colorer *color.Color
// premiddleware are middlewares that are run before routing is done. In case a pre-middleware returns
// an error the router is not executed and the request will end up in the global error handler.
premiddleware []MiddlewareFunc premiddleware []MiddlewareFunc
middleware []MiddlewareFunc middleware []MiddlewareFunc
maxParam *int maxParam *int
router *Router router *Router
routers map[string]*Router routers map[string]*Router
pool sync.Pool pool sync.Pool
StdLogger *stdLog.Logger
Server *http.Server Server *http.Server
TLSServer *http.Server TLSServer *http.Server
Listener net.Listener Listener net.Listener
@ -93,6 +100,9 @@ type (
Logger Logger Logger Logger
IPExtractor IPExtractor IPExtractor IPExtractor
ListenerNetwork string ListenerNetwork string
// OnAddRouteHandler is called when Echo adds new route to specific host router.
OnAddRouteHandler func(host string, route Route, handler HandlerFunc, middleware []MiddlewareFunc)
} }
// Route contains a handler and information for matching against requests. // Route contains a handler and information for matching against requests.
@ -116,7 +126,7 @@ type (
HandlerFunc func(c Context) error HandlerFunc func(c Context) error
// HTTPErrorHandler is a centralized HTTP error handler. // HTTPErrorHandler is a centralized HTTP error handler.
HTTPErrorHandler func(error, Context) HTTPErrorHandler func(err error, c Context)
// Validator is the interface that wraps the Validate function. // Validator is the interface that wraps the Validate function.
Validator interface { Validator interface {
@ -248,7 +258,7 @@ const (
const ( const (
// Version of Echo // Version of Echo
Version = "4.9.0" Version = "4.10.2"
website = "https://echo.labstack.com" website = "https://echo.labstack.com"
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
banner = ` banner = `
@ -281,18 +291,47 @@ var (
// Errors // Errors
var ( var (
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType) ErrBadRequest = NewHTTPError(http.StatusBadRequest) // HTTP 400 Bad Request
ErrNotFound = NewHTTPError(http.StatusNotFound) ErrUnauthorized = NewHTTPError(http.StatusUnauthorized) // HTTP 401 Unauthorized
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized) ErrPaymentRequired = NewHTTPError(http.StatusPaymentRequired) // HTTP 402 Payment Required
ErrForbidden = NewHTTPError(http.StatusForbidden) ErrForbidden = NewHTTPError(http.StatusForbidden) // HTTP 403 Forbidden
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) ErrNotFound = NewHTTPError(http.StatusNotFound) // HTTP 404 Not Found
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) // HTTP 405 Method Not Allowed
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests) ErrNotAcceptable = NewHTTPError(http.StatusNotAcceptable) // HTTP 406 Not Acceptable
ErrBadRequest = NewHTTPError(http.StatusBadRequest) ErrProxyAuthRequired = NewHTTPError(http.StatusProxyAuthRequired) // HTTP 407 Proxy AuthRequired
ErrBadGateway = NewHTTPError(http.StatusBadGateway) ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout) // HTTP 408 Request Timeout
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) ErrConflict = NewHTTPError(http.StatusConflict) // HTTP 409 Conflict
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout) ErrGone = NewHTTPError(http.StatusGone) // HTTP 410 Gone
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable) ErrLengthRequired = NewHTTPError(http.StatusLengthRequired) // HTTP 411 Length Required
ErrPreconditionFailed = NewHTTPError(http.StatusPreconditionFailed) // HTTP 412 Precondition Failed
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // HTTP 413 Payload Too Large
ErrRequestURITooLong = NewHTTPError(http.StatusRequestURITooLong) // HTTP 414 URI Too Long
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType) // HTTP 415 Unsupported Media Type
ErrRequestedRangeNotSatisfiable = NewHTTPError(http.StatusRequestedRangeNotSatisfiable) // HTTP 416 Range Not Satisfiable
ErrExpectationFailed = NewHTTPError(http.StatusExpectationFailed) // HTTP 417 Expectation Failed
ErrTeapot = NewHTTPError(http.StatusTeapot) // HTTP 418 I'm a teapot
ErrMisdirectedRequest = NewHTTPError(http.StatusMisdirectedRequest) // HTTP 421 Misdirected Request
ErrUnprocessableEntity = NewHTTPError(http.StatusUnprocessableEntity) // HTTP 422 Unprocessable Entity
ErrLocked = NewHTTPError(http.StatusLocked) // HTTP 423 Locked
ErrFailedDependency = NewHTTPError(http.StatusFailedDependency) // HTTP 424 Failed Dependency
ErrTooEarly = NewHTTPError(http.StatusTooEarly) // HTTP 425 Too Early
ErrUpgradeRequired = NewHTTPError(http.StatusUpgradeRequired) // HTTP 426 Upgrade Required
ErrPreconditionRequired = NewHTTPError(http.StatusPreconditionRequired) // HTTP 428 Precondition Required
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests) // HTTP 429 Too Many Requests
ErrRequestHeaderFieldsTooLarge = NewHTTPError(http.StatusRequestHeaderFieldsTooLarge) // HTTP 431 Request Header Fields Too Large
ErrUnavailableForLegalReasons = NewHTTPError(http.StatusUnavailableForLegalReasons) // HTTP 451 Unavailable For Legal Reasons
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // HTTP 500 Internal Server Error
ErrNotImplemented = NewHTTPError(http.StatusNotImplemented) // HTTP 501 Not Implemented
ErrBadGateway = NewHTTPError(http.StatusBadGateway) // HTTP 502 Bad Gateway
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable) // HTTP 503 Service Unavailable
ErrGatewayTimeout = NewHTTPError(http.StatusGatewayTimeout) // HTTP 504 Gateway Timeout
ErrHTTPVersionNotSupported = NewHTTPError(http.StatusHTTPVersionNotSupported) // HTTP 505 HTTP Version Not Supported
ErrVariantAlsoNegotiates = NewHTTPError(http.StatusVariantAlsoNegotiates) // HTTP 506 Variant Also Negotiates
ErrInsufficientStorage = NewHTTPError(http.StatusInsufficientStorage) // HTTP 507 Insufficient Storage
ErrLoopDetected = NewHTTPError(http.StatusLoopDetected) // HTTP 508 Loop Detected
ErrNotExtended = NewHTTPError(http.StatusNotExtended) // HTTP 510 Not Extended
ErrNetworkAuthenticationRequired = NewHTTPError(http.StatusNetworkAuthenticationRequired) // HTTP 511 Network Authentication Required
ErrValidatorNotRegistered = errors.New("validator not registered") ErrValidatorNotRegistered = errors.New("validator not registered")
ErrRendererNotRegistered = errors.New("renderer not registered") ErrRendererNotRegistered = errors.New("renderer not registered")
ErrInvalidRedirectCode = errors.New("invalid redirect status code") ErrInvalidRedirectCode = errors.New("invalid redirect status code")
@ -527,21 +566,20 @@ func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {
return e.file(path, file, e.GET, m...) return e.file(path, file, e.GET, m...)
} }
func (e *Echo) add(host, method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { func (e *Echo) add(host, method, path string, handler HandlerFunc, middlewares ...MiddlewareFunc) *Route {
name := handlerName(handler)
router := e.findRouter(host) router := e.findRouter(host)
//FIXME: when handler+middleware are both nil ... make it behave like handler removal //FIXME: when handler+middleware are both nil ... make it behave like handler removal
router.Add(method, path, func(c Context) error { name := handlerName(handler)
h := applyMiddleware(handler, middleware...) route := router.add(method, path, name, func(c Context) error {
h := applyMiddleware(handler, middlewares...)
return h(c) return h(c)
}) })
r := &Route{
Method: method, if e.OnAddRouteHandler != nil {
Path: path, e.OnAddRouteHandler(host, *route, handler, middlewares)
Name: name,
} }
e.router.routes[method+path] = r
return r return route
} }
// Add registers a new route for an HTTP method and path with matching handler // Add registers a new route for an HTTP method and path with matching handler
@ -565,7 +603,7 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
return return
} }
// URI generates a URI from handler. // URI generates an URI from handler.
func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
name := handlerName(handler) name := handlerName(handler)
return e.Reverse(name, params...) return e.Reverse(name, params...)
@ -578,35 +616,13 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
// Reverse generates an URL from route name and provided parameters. // Reverse generates an URL from route name and provided parameters.
func (e *Echo) Reverse(name string, params ...interface{}) string { func (e *Echo) Reverse(name string, params ...interface{}) string {
uri := new(bytes.Buffer) return e.router.Reverse(name, params...)
ln := len(params)
n := 0
for _, r := range e.router.routes {
if r.Name == name {
for i, l := 0, len(r.Path); i < l; i++ {
if (r.Path[i] == ':' || r.Path[i] == '*') && n < ln {
for ; i < l && r.Path[i] != '/'; i++ {
}
uri.WriteString(fmt.Sprintf("%v", params[n]))
n++
}
if i < l {
uri.WriteByte(r.Path[i])
}
}
break
}
}
return uri.String()
} }
// Routes returns the registered routes. // Routes returns the registered routes for default router.
// In case when Echo serves multiple hosts/domains use `e.Routers()["domain2.site"].Routes()` to get specific host routes.
func (e *Echo) Routes() []*Route { func (e *Echo) Routes() []*Route {
routes := make([]*Route, 0, len(e.router.routes)) return e.router.Routes()
for _, v := range e.router.routes {
routes = append(routes, v)
}
return routes
} }
// AcquireContext returns an empty `Context` instance from the pool. // AcquireContext returns an empty `Context` instance from the pool.
@ -626,7 +642,7 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Acquire context // Acquire context
c := e.pool.Get().(*context) c := e.pool.Get().(*context)
c.Reset(r, w) c.Reset(r, w)
var h func(Context) error var h HandlerFunc
if e.premiddleware == nil { if e.premiddleware == nil {
e.findRouter(r.Host).Find(r.Method, GetPath(r), c) e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
@ -700,7 +716,7 @@ func (e *Echo) StartTLS(address string, certFile, keyFile interface{}) (err erro
func filepathOrContent(fileOrContent interface{}) (content []byte, err error) { func filepathOrContent(fileOrContent interface{}) (content []byte, err error) {
switch v := fileOrContent.(type) { switch v := fileOrContent.(type) {
case string: case string:
return ioutil.ReadFile(v) return os.ReadFile(v)
case []byte: case []byte:
return v, nil return v, nil
default: default:
@ -884,6 +900,15 @@ func (he *HTTPError) SetInternal(err error) *HTTPError {
return he return he
} }
// WithInternal returns clone of HTTPError with err set to HTTPError.Internal field
func (he *HTTPError) WithInternal(err error) *HTTPError {
return &HTTPError{
Code: he.Code,
Message: he.Message,
Internal: err,
}
}
// Unwrap satisfies the Go 1.13 error wrapper interface. // Unwrap satisfies the Go 1.13 error wrapper interface.
func (he *HTTPError) Unwrap() error { func (he *HTTPError) Unwrap() error {
return he.Internal return he.Internal
@ -913,8 +938,8 @@ func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
// GetPath returns RawPath, if it's empty returns Path from URL // GetPath returns RawPath, if it's empty returns Path from URL
// Difference between RawPath and Path is: // Difference between RawPath and Path is:
// * Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/. // - Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/.
// * RawPath is an optional field which only gets set if the default encoding is different from Path. // - RawPath is an optional field which only gets set if the default encoding is different from Path.
func GetPath(r *http.Request) string { func GetPath(r *http.Request) string {
path := r.URL.RawPath path := r.URL.RawPath
if path == "" { if path == "" {

View File

@ -7,7 +7,6 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
) )
@ -125,7 +124,7 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) {
// we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS. // we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS.
// fs.Fs.Open does not like relative paths ("./", "../") and absolute paths at all but prior echo.Filesystem we // fs.Fs.Open does not like relative paths ("./", "../") and absolute paths at all but prior echo.Filesystem we
// were able to use paths like `./myfile.log`, `/etc/hosts` and these would work fine with `os.Open` but not with fs.Fs // were able to use paths like `./myfile.log`, `/etc/hosts` and these would work fine with `os.Open` but not with fs.Fs
if isRelativePath(root) { if !filepath.IsAbs(root) {
root = filepath.Join(dFS.prefix, root) root = filepath.Join(dFS.prefix, root)
} }
return &defaultFS{ return &defaultFS{
@ -136,21 +135,6 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) {
return fs.Sub(currentFs, root) return fs.Sub(currentFs, root)
} }
func isRelativePath(path string) bool {
if path == "" {
return true
}
if path[0] == '/' {
return false
}
if runtime.GOOS == "windows" && strings.IndexByte(path, ':') != -1 {
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#file_and_directory_names
// https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
return false
}
return true
}
// MustSubFS creates sub FS from current filesystem or panic on failure. // MustSubFS creates sub FS from current filesystem or panic on failure.
// Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules. // Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules.
// //

View File

@ -227,6 +227,8 @@ func ExtractIPFromRealIPHeader(options ...TrustOption) IPExtractor {
return func(req *http.Request) string { return func(req *http.Request) string {
realIP := req.Header.Get(HeaderXRealIP) realIP := req.Header.Get(HeaderXRealIP)
if realIP != "" { if realIP != "" {
realIP = strings.TrimPrefix(realIP, "[")
realIP = strings.TrimSuffix(realIP, "]")
if ip := net.ParseIP(realIP); ip != nil && checker.trust(ip) { if ip := net.ParseIP(realIP); ip != nil && checker.trust(ip) {
return realIP return realIP
} }
@ -248,7 +250,10 @@ func ExtractIPFromXFFHeader(options ...TrustOption) IPExtractor {
} }
ips := append(strings.Split(strings.Join(xffs, ","), ","), directIP) ips := append(strings.Split(strings.Join(xffs, ","), ","), directIP)
for i := len(ips) - 1; i >= 0; i-- { for i := len(ips) - 1; i >= 0; i-- {
ip := net.ParseIP(strings.TrimSpace(ips[i])) ips[i] = strings.TrimSpace(ips[i])
ips[i] = strings.TrimPrefix(ips[i], "[")
ips[i] = strings.TrimSuffix(ips[i], "]")
ip := net.ParseIP(ips[i])
if ip == nil { if ip == nil {
// Unable to parse IP; cannot trust entire records // Unable to parse IP; cannot trust entire records
return directIP return directIP

View File

@ -2,6 +2,7 @@ package echo
import ( import (
"bytes" "bytes"
"fmt"
"net/http" "net/http"
) )
@ -141,6 +142,51 @@ func NewRouter(e *Echo) *Router {
} }
} }
// Routes returns the registered routes.
func (r *Router) Routes() []*Route {
routes := make([]*Route, 0, len(r.routes))
for _, v := range r.routes {
routes = append(routes, v)
}
return routes
}
// Reverse generates an URL from route name and provided parameters.
func (r *Router) Reverse(name string, params ...interface{}) string {
uri := new(bytes.Buffer)
ln := len(params)
n := 0
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 {
for ; i < l && route.Path[i] != '/'; i++ {
}
uri.WriteString(fmt.Sprintf("%v", params[n]))
n++
}
if i < l {
uri.WriteByte(route.Path[i])
}
}
break
}
}
return uri.String()
}
func (r *Router) add(method, path, name string, h HandlerFunc) *Route {
r.Add(method, path, h)
route := &Route{
Method: method,
Path: path,
Name: name,
}
r.routes[method+path] = route
return route
}
// Add registers a new route for method and path with matching handler. // Add registers a new route for method and path with matching handler.
func (r *Router) Add(method, path string, h HandlerFunc) { func (r *Router) Add(method, path string, h HandlerFunc) {
// Validate path // Validate path
@ -478,7 +524,6 @@ func optionsMethodHandler(allowMethods string) func(c Context) error {
// - Return it `Echo#ReleaseContext()`. // - Return it `Echo#ReleaseContext()`.
func (r *Router) Find(method, path string, c Context) { func (r *Router) Find(method, path string, c Context) {
ctx := c.(*context) ctx := c.(*context)
ctx.path = path
currentNode := r.tree // Current node as root currentNode := r.tree // Current node as root
var ( var (

View File

@ -1,5 +1,5 @@
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine //go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine
// +build darwin freebsd openbsd netbsd dragonfly // +build darwin freebsd openbsd netbsd dragonfly hurd
// +build !appengine // +build !appengine
package isatty package isatty

View File

@ -117,7 +117,7 @@ func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error)
return responseAccount(res) return responseAccount(res)
} }
// getGegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555. // getRegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
// It expects c.Discover to have already been called. // It expects c.Discover to have already been called.
func (c *Client) getRegRFC(ctx context.Context) (*Account, error) { func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
req := json.RawMessage(`{"onlyReturnExisting": true}`) req := json.RawMessage(`{"onlyReturnExisting": true}`)

15
vendor/golang.org/x/net/html/doc.go generated vendored
View File

@ -92,6 +92,21 @@ example, to process each anchor node in depth-first order:
The relevant specifications include: The relevant specifications include:
https://html.spec.whatwg.org/multipage/syntax.html and https://html.spec.whatwg.org/multipage/syntax.html and
https://html.spec.whatwg.org/multipage/syntax.html#tokenization https://html.spec.whatwg.org/multipage/syntax.html#tokenization
# Security Considerations
Care should be taken when parsing and interpreting HTML, whether full documents
or fragments, within the framework of the HTML specification, especially with
regard to untrusted inputs.
This package provides both a tokenizer and a parser. Only the parser constructs
a DOM according to the HTML specification, resolving malformed and misplaced
tags where appropriate. The tokenizer simply tokenizes the HTML presented to it,
and as such does not resolve issues that may exist in the processed HTML,
producing a literal interpretation of the input.
If your use case requires semantically well-formed HTML, as defined by the
WHATWG specifiction, the parser should be used rather than the tokenizer.
*/ */
package html // import "golang.org/x/net/html" package html // import "golang.org/x/net/html"

View File

@ -193,6 +193,87 @@ func lower(b []byte) []byte {
return b return b
} }
// escapeComment is like func escape but escapes its input bytes less often.
// Per https://github.com/golang/go/issues/58246 some HTML comments are (1)
// meaningful and (2) contain angle brackets that we'd like to avoid escaping
// unless we have to.
//
// "We have to" includes the '&' byte, since that introduces other escapes.
//
// It also includes those bytes (not including EOF) that would otherwise end
// the comment. Per the summary table at the bottom of comment_test.go, this is
// the '>' byte that, per above, we'd like to avoid escaping unless we have to.
//
// Studying the summary table (and T actions in its '>' column) closely, we
// only need to escape in states 43, 44, 49, 51 and 52. State 43 is at the
// start of the comment data. State 52 is after a '!'. The other three states
// are after a '-'.
//
// Our algorithm is thus to escape every '&' and to escape '>' if and only if:
// - The '>' is after a '!' or '-' (in the unescaped data) or
// - The '>' is at the start of the comment data (after the opening "<!--").
func escapeComment(w writer, s string) error {
// When modifying this function, consider manually increasing the
// maxSuffixLen constant in func TestComments, from 6 to e.g. 9 or more.
// That increase should only be temporary, not committed, as it
// exponentially affects the test running time.
if len(s) == 0 {
return nil
}
// Loop:
// - Grow j such that s[i:j] does not need escaping.
// - If s[j] does need escaping, output s[i:j] and an escaped s[j],
// resetting i and j to point past that s[j] byte.
i := 0
for j := 0; j < len(s); j++ {
escaped := ""
switch s[j] {
case '&':
escaped = "&amp;"
case '>':
if j > 0 {
if prev := s[j-1]; (prev != '!') && (prev != '-') {
continue
}
}
escaped = "&gt;"
default:
continue
}
if i < j {
if _, err := w.WriteString(s[i:j]); err != nil {
return err
}
}
if _, err := w.WriteString(escaped); err != nil {
return err
}
i = j + 1
}
if i < len(s) {
if _, err := w.WriteString(s[i:]); err != nil {
return err
}
}
return nil
}
// escapeCommentString is to EscapeString as escapeComment is to escape.
func escapeCommentString(s string) string {
if strings.IndexAny(s, "&>") == -1 {
return s
}
var buf bytes.Buffer
escapeComment(&buf, s)
return buf.String()
}
const escapedChars = "&'<>\"\r" const escapedChars = "&'<>\"\r"
func escape(w writer, s string) error { func escape(w writer, s string) error {

View File

@ -184,7 +184,7 @@ func (p *parser) clearStackToContext(s scope) {
} }
} }
// parseGenericRawTextElements implements the generic raw text element parsing // parseGenericRawTextElement implements the generic raw text element parsing
// algorithm defined in 12.2.6.2. // algorithm defined in 12.2.6.2.
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text // https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text
// TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part // TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part

View File

@ -85,7 +85,7 @@ func render1(w writer, n *Node) error {
if _, err := w.WriteString("<!--"); err != nil { if _, err := w.WriteString("<!--"); err != nil {
return err return err
} }
if err := escape(w, n.Data); err != nil { if err := escapeComment(w, n.Data); err != nil {
return err return err
} }
if _, err := w.WriteString("-->"); err != nil { if _, err := w.WriteString("-->"); err != nil {

View File

@ -110,7 +110,7 @@ func (t Token) String() string {
case SelfClosingTagToken: case SelfClosingTagToken:
return "<" + t.tagString() + "/>" return "<" + t.tagString() + "/>"
case CommentToken: case CommentToken:
return "<!--" + EscapeString(t.Data) + "-->" return "<!--" + escapeCommentString(t.Data) + "-->"
case DoctypeToken: case DoctypeToken:
return "<!DOCTYPE " + EscapeString(t.Data) + ">" return "<!DOCTYPE " + EscapeString(t.Data) + ">"
} }
@ -598,6 +598,11 @@ scriptDataDoubleEscapeEnd:
// readComment reads the next comment token starting with "<!--". The opening // readComment reads the next comment token starting with "<!--". The opening
// "<!--" has already been consumed. // "<!--" has already been consumed.
func (z *Tokenizer) readComment() { func (z *Tokenizer) readComment() {
// When modifying this function, consider manually increasing the
// maxSuffixLen constant in func TestComments, from 6 to e.g. 9 or more.
// That increase should only be temporary, not committed, as it
// exponentially affects the test running time.
z.data.start = z.raw.end z.data.start = z.raw.end
defer func() { defer func() {
if z.data.end < z.data.start { if z.data.end < z.data.start {
@ -605,14 +610,13 @@ func (z *Tokenizer) readComment() {
z.data.end = z.data.start z.data.end = z.data.start
} }
}() }()
for dashCount := 2; ; {
var dashCount int
beginning := true
for {
c := z.readByte() c := z.readByte()
if z.err != nil { if z.err != nil {
// Ignore up to two dashes at EOF. z.data.end = z.calculateAbruptCommentDataEnd()
if dashCount > 2 {
dashCount = 2
}
z.data.end = z.raw.end - dashCount
return return
} }
switch c { switch c {
@ -620,7 +624,7 @@ func (z *Tokenizer) readComment() {
dashCount++ dashCount++
continue continue
case '>': case '>':
if dashCount >= 2 { if dashCount >= 2 || beginning {
z.data.end = z.raw.end - len("-->") z.data.end = z.raw.end - len("-->")
return return
} }
@ -628,19 +632,52 @@ func (z *Tokenizer) readComment() {
if dashCount >= 2 { if dashCount >= 2 {
c = z.readByte() c = z.readByte()
if z.err != nil { if z.err != nil {
z.data.end = z.raw.end z.data.end = z.calculateAbruptCommentDataEnd()
return return
} } else if c == '>' {
if c == '>' {
z.data.end = z.raw.end - len("--!>") z.data.end = z.raw.end - len("--!>")
return return
} else if c == '-' {
dashCount = 1
beginning = false
continue
} }
} }
} }
dashCount = 0 dashCount = 0
beginning = false
} }
} }
func (z *Tokenizer) calculateAbruptCommentDataEnd() int {
raw := z.Raw()
const prefixLen = len("<!--")
if len(raw) >= prefixLen {
raw = raw[prefixLen:]
if hasSuffix(raw, "--!") {
return z.raw.end - 3
} else if hasSuffix(raw, "--") {
return z.raw.end - 2
} else if hasSuffix(raw, "-") {
return z.raw.end - 1
}
}
return z.raw.end
}
func hasSuffix(b []byte, suffix string) bool {
if len(b) < len(suffix) {
return false
}
b = b[len(b)-len(suffix):]
for i := range b {
if b[i] != suffix[i] {
return false
}
}
return true
}
// readUntilCloseAngle reads until the next ">". // readUntilCloseAngle reads until the next ">".
func (z *Tokenizer) readUntilCloseAngle() { func (z *Tokenizer) readUntilCloseAngle() {
z.data.start = z.raw.end z.data.start = z.raw.end

View File

@ -6,23 +6,91 @@
package http2 package http2
// flow is the flow control window's size. // inflowMinRefresh is the minimum number of bytes we'll send for a
type flow struct { // flow control window update.
const inflowMinRefresh = 4 << 10
// inflow accounts for an inbound flow control window.
// It tracks both the latest window sent to the peer (used for enforcement)
// and the accumulated unsent window.
type inflow struct {
avail int32
unsent int32
}
// init sets the initial window.
func (f *inflow) init(n int32) {
f.avail = n
}
// add adds n bytes to the window, with a maximum window size of max,
// indicating that the peer can now send us more data.
// For example, the user read from a {Request,Response} body and consumed
// some of the buffered data, so the peer can now send more.
// It returns the number of bytes to send in a WINDOW_UPDATE frame to the peer.
// Window updates are accumulated and sent when the unsent capacity
// is at least inflowMinRefresh or will at least double the peer's available window.
func (f *inflow) add(n int) (connAdd int32) {
if n < 0 {
panic("negative update")
}
unsent := int64(f.unsent) + int64(n)
// "A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets."
// RFC 7540 Section 6.9.1.
const maxWindow = 1<<31 - 1
if unsent+int64(f.avail) > maxWindow {
panic("flow control update exceeds maximum window size")
}
f.unsent = int32(unsent)
if f.unsent < inflowMinRefresh && f.unsent < f.avail {
// If there aren't at least inflowMinRefresh bytes of window to send,
// and this update won't at least double the window, buffer the update for later.
return 0
}
f.avail += f.unsent
f.unsent = 0
return int32(unsent)
}
// take attempts to take n bytes from the peer's flow control window.
// It reports whether the window has available capacity.
func (f *inflow) take(n uint32) bool {
if n > uint32(f.avail) {
return false
}
f.avail -= int32(n)
return true
}
// takeInflows attempts to take n bytes from two inflows,
// typically connection-level and stream-level flows.
// It reports whether both windows have available capacity.
func takeInflows(f1, f2 *inflow, n uint32) bool {
if n > uint32(f1.avail) || n > uint32(f2.avail) {
return false
}
f1.avail -= int32(n)
f2.avail -= int32(n)
return true
}
// outflow is the outbound flow control window's size.
type outflow struct {
_ incomparable _ incomparable
// n is the number of DATA bytes we're allowed to send. // n is the number of DATA bytes we're allowed to send.
// A flow is kept both on a conn and a per-stream. // An outflow is kept both on a conn and a per-stream.
n int32 n int32
// conn points to the shared connection-level flow that is // conn points to the shared connection-level outflow that is
// shared by all streams on that conn. It is nil for the flow // shared by all streams on that conn. It is nil for the outflow
// that's on the conn directly. // that's on the conn directly.
conn *flow conn *outflow
} }
func (f *flow) setConnFlow(cf *flow) { f.conn = cf } func (f *outflow) setConnFlow(cf *outflow) { f.conn = cf }
func (f *flow) available() int32 { func (f *outflow) available() int32 {
n := f.n n := f.n
if f.conn != nil && f.conn.n < n { if f.conn != nil && f.conn.n < n {
n = f.conn.n n = f.conn.n
@ -30,7 +98,7 @@ func (f *flow) available() int32 {
return n return n
} }
func (f *flow) take(n int32) { func (f *outflow) take(n int32) {
if n > f.available() { if n > f.available() {
panic("internal error: took too much") panic("internal error: took too much")
} }
@ -42,7 +110,7 @@ func (f *flow) take(n int32) {
// add adds n bytes (positive or negative) to the flow control window. // add adds n bytes (positive or negative) to the flow control window.
// It returns false if the sum would exceed 2^31-1. // It returns false if the sum would exceed 2^31-1.
func (f *flow) add(n int32) bool { func (f *outflow) add(n int32) bool {
sum := f.n + n sum := f.n + n
if (sum > n) == (f.n > 0) { if (sum > n) == (f.n > 0) {
f.n = sum f.n = sum

View File

@ -662,6 +662,15 @@ func (f *Framer) WriteData(streamID uint32, endStream bool, data []byte) error {
// It is the caller's responsibility not to violate the maximum frame size // It is the caller's responsibility not to violate the maximum frame size
// and to not call other Write methods concurrently. // and to not call other Write methods concurrently.
func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
if err := f.startWriteDataPadded(streamID, endStream, data, pad); err != nil {
return err
}
return f.endWrite()
}
// startWriteDataPadded is WriteDataPadded, but only writes the frame to the Framer's internal buffer.
// The caller should call endWrite to flush the frame to the underlying writer.
func (f *Framer) startWriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
if !validStreamID(streamID) && !f.AllowIllegalWrites { if !validStreamID(streamID) && !f.AllowIllegalWrites {
return errStreamID return errStreamID
} }
@ -691,7 +700,7 @@ func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []by
} }
f.wbuf = append(f.wbuf, data...) f.wbuf = append(f.wbuf, data...)
f.wbuf = append(f.wbuf, pad...) f.wbuf = append(f.wbuf, pad...)
return f.endWrite() return nil
} }
// A SettingsFrame conveys configuration parameters that affect how // A SettingsFrame conveys configuration parameters that affect how

View File

@ -109,6 +109,7 @@ func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if http2VerboseLogs { if http2VerboseLogs {
log.Printf("h2c: error h2c upgrade: %v", err) log.Printf("h2c: error h2c upgrade: %v", err)
} }
w.WriteHeader(http.StatusInternalServerError)
return return
} }
defer conn.Close() defer conn.Close()
@ -167,7 +168,10 @@ func h2cUpgrade(w http.ResponseWriter, r *http.Request) (_ net.Conn, settings []
return nil, nil, errors.New("h2c: connection does not support Hijack") return nil, nil, errors.New("h2c: connection does not support Hijack")
} }
body, _ := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil {
return nil, nil, err
}
r.Body = io.NopCloser(bytes.NewBuffer(body)) r.Body = io.NopCloser(bytes.NewBuffer(body))
conn, rw, err := hijacker.Hijack() conn, rw, err := hijacker.Hijack()

View File

@ -27,7 +27,14 @@ func buildCommonHeaderMaps() {
"accept-language", "accept-language",
"accept-ranges", "accept-ranges",
"age", "age",
"access-control-allow-credentials",
"access-control-allow-headers",
"access-control-allow-methods",
"access-control-allow-origin", "access-control-allow-origin",
"access-control-expose-headers",
"access-control-max-age",
"access-control-request-headers",
"access-control-request-method",
"allow", "allow",
"authorization", "authorization",
"cache-control", "cache-control",
@ -53,6 +60,7 @@ func buildCommonHeaderMaps() {
"link", "link",
"location", "location",
"max-forwards", "max-forwards",
"origin",
"proxy-authenticate", "proxy-authenticate",
"proxy-authorization", "proxy-authorization",
"range", "range",
@ -68,6 +76,8 @@ func buildCommonHeaderMaps() {
"vary", "vary",
"via", "via",
"www-authenticate", "www-authenticate",
"x-forwarded-for",
"x-forwarded-proto",
} }
commonLowerHeader = make(map[string]string, len(common)) commonLowerHeader = make(map[string]string, len(common))
commonCanonHeader = make(map[string]string, len(common)) commonCanonHeader = make(map[string]string, len(common))
@ -85,3 +95,11 @@ func lowerHeader(v string) (lower string, ascii bool) {
} }
return asciiToLower(v) return asciiToLower(v)
} }
func canonicalHeader(v string) string {
buildCommonHeaderMapsOnce()
if s, ok := commonCanonHeader[v]; ok {
return s
}
return http.CanonicalHeaderKey(v)
}

View File

@ -116,6 +116,11 @@ func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
e.dynTab.setMaxSize(v) e.dynTab.setMaxSize(v)
} }
// MaxDynamicTableSize returns the current dynamic header table size.
func (e *Encoder) MaxDynamicTableSize() (v uint32) {
return e.dynTab.maxSize
}
// SetMaxDynamicTableSizeLimit changes the maximum value that can be // SetMaxDynamicTableSizeLimit changes the maximum value that can be
// specified in SetMaxDynamicTableSize to v. By default, it is set to // specified in SetMaxDynamicTableSize to v. By default, it is set to
// 4096, which is the same size of the default dynamic header table // 4096, which is the same size of the default dynamic header table

View File

@ -211,7 +211,7 @@ func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
return dt.ents[dt.len()-(int(i)-staticTable.len())], true return dt.ents[dt.len()-(int(i)-staticTable.len())], true
} }
// Decode decodes an entire block. // DecodeFull decodes an entire block.
// //
// TODO: remove this method and make it incremental later? This is // TODO: remove this method and make it incremental later? This is
// easier for debugging now. // easier for debugging now.
@ -359,6 +359,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
var hf HeaderField var hf HeaderField
wantStr := d.emitEnabled || it.indexed() wantStr := d.emitEnabled || it.indexed()
var undecodedName undecodedString
if nameIdx > 0 { if nameIdx > 0 {
ihf, ok := d.at(nameIdx) ihf, ok := d.at(nameIdx)
if !ok { if !ok {
@ -366,15 +367,27 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
} }
hf.Name = ihf.Name hf.Name = ihf.Name
} else { } else {
hf.Name, buf, err = d.readString(buf, wantStr) undecodedName, buf, err = d.readString(buf)
if err != nil { if err != nil {
return err return err
} }
} }
hf.Value, buf, err = d.readString(buf, wantStr) undecodedValue, buf, err := d.readString(buf)
if err != nil { if err != nil {
return err return err
} }
if wantStr {
if nameIdx <= 0 {
hf.Name, err = d.decodeString(undecodedName)
if err != nil {
return err
}
}
hf.Value, err = d.decodeString(undecodedValue)
if err != nil {
return err
}
}
d.buf = buf d.buf = buf
if it.indexed() { if it.indexed() {
d.dynTab.add(hf) d.dynTab.add(hf)
@ -459,46 +472,52 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
return 0, origP, errNeedMore return 0, origP, errNeedMore
} }
// readString decodes an hpack string from p. // readString reads an hpack string from p.
// //
// wantStr is whether s will be used. If false, decompression and // It returns a reference to the encoded string data to permit deferring decode costs
// []byte->string garbage are skipped if s will be ignored // until after the caller verifies all data is present.
// anyway. This does mean that huffman decoding errors for non-indexed func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) {
// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server
// is returning an error anyway, and because they're not indexed, the error
// won't affect the decoding state.
func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
if len(p) == 0 { if len(p) == 0 {
return "", p, errNeedMore return u, p, errNeedMore
} }
isHuff := p[0]&128 != 0 isHuff := p[0]&128 != 0
strLen, p, err := readVarInt(7, p) strLen, p, err := readVarInt(7, p)
if err != nil { if err != nil {
return "", p, err return u, p, err
} }
if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) { if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
return "", nil, ErrStringLength // Returning an error here means Huffman decoding errors
// for non-indexed strings past the maximum string length
// are ignored, but the server is returning an error anyway
// and because the string is not indexed the error will not
// affect the decoding state.
return u, nil, ErrStringLength
} }
if uint64(len(p)) < strLen { if uint64(len(p)) < strLen {
return "", p, errNeedMore return u, p, errNeedMore
} }
if !isHuff { u.isHuff = isHuff
if wantStr { u.b = p[:strLen]
s = string(p[:strLen]) return u, p[strLen:], nil
}
return s, p[strLen:], nil
} }
if wantStr { type undecodedString struct {
isHuff bool
b []byte
}
func (d *Decoder) decodeString(u undecodedString) (string, error) {
if !u.isHuff {
return string(u.b), nil
}
buf := bufPool.Get().(*bytes.Buffer) buf := bufPool.Get().(*bytes.Buffer)
buf.Reset() // don't trust others buf.Reset() // don't trust others
defer bufPool.Put(buf) var s string
if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil { err := huffmanDecode(buf, d.maxStrLen, u.b)
buf.Reset() if err == nil {
return "", nil, err
}
s = buf.String() s = buf.String()
}
buf.Reset() // be nice to GC buf.Reset() // be nice to GC
} bufPool.Put(buf)
return s, p[strLen:], nil return s, err
} }

188
vendor/golang.org/x/net/http2/hpack/static_table.go generated vendored Normal file
View File

@ -0,0 +1,188 @@
// go generate gen.go
// Code generated by the command above; DO NOT EDIT.
package hpack
var staticTable = &headerFieldTable{
evictCount: 0,
byName: map[string]uint64{
":authority": 1,
":method": 3,
":path": 5,
":scheme": 7,
":status": 14,
"accept-charset": 15,
"accept-encoding": 16,
"accept-language": 17,
"accept-ranges": 18,
"accept": 19,
"access-control-allow-origin": 20,
"age": 21,
"allow": 22,
"authorization": 23,
"cache-control": 24,
"content-disposition": 25,
"content-encoding": 26,
"content-language": 27,
"content-length": 28,
"content-location": 29,
"content-range": 30,
"content-type": 31,
"cookie": 32,
"date": 33,
"etag": 34,
"expect": 35,
"expires": 36,
"from": 37,
"host": 38,
"if-match": 39,
"if-modified-since": 40,
"if-none-match": 41,
"if-range": 42,
"if-unmodified-since": 43,
"last-modified": 44,
"link": 45,
"location": 46,
"max-forwards": 47,
"proxy-authenticate": 48,
"proxy-authorization": 49,
"range": 50,
"referer": 51,
"refresh": 52,
"retry-after": 53,
"server": 54,
"set-cookie": 55,
"strict-transport-security": 56,
"transfer-encoding": 57,
"user-agent": 58,
"vary": 59,
"via": 60,
"www-authenticate": 61,
},
byNameValue: map[pairNameValue]uint64{
{name: ":authority", value: ""}: 1,
{name: ":method", value: "GET"}: 2,
{name: ":method", value: "POST"}: 3,
{name: ":path", value: "/"}: 4,
{name: ":path", value: "/index.html"}: 5,
{name: ":scheme", value: "http"}: 6,
{name: ":scheme", value: "https"}: 7,
{name: ":status", value: "200"}: 8,
{name: ":status", value: "204"}: 9,
{name: ":status", value: "206"}: 10,
{name: ":status", value: "304"}: 11,
{name: ":status", value: "400"}: 12,
{name: ":status", value: "404"}: 13,
{name: ":status", value: "500"}: 14,
{name: "accept-charset", value: ""}: 15,
{name: "accept-encoding", value: "gzip, deflate"}: 16,
{name: "accept-language", value: ""}: 17,
{name: "accept-ranges", value: ""}: 18,
{name: "accept", value: ""}: 19,
{name: "access-control-allow-origin", value: ""}: 20,
{name: "age", value: ""}: 21,
{name: "allow", value: ""}: 22,
{name: "authorization", value: ""}: 23,
{name: "cache-control", value: ""}: 24,
{name: "content-disposition", value: ""}: 25,
{name: "content-encoding", value: ""}: 26,
{name: "content-language", value: ""}: 27,
{name: "content-length", value: ""}: 28,
{name: "content-location", value: ""}: 29,
{name: "content-range", value: ""}: 30,
{name: "content-type", value: ""}: 31,
{name: "cookie", value: ""}: 32,
{name: "date", value: ""}: 33,
{name: "etag", value: ""}: 34,
{name: "expect", value: ""}: 35,
{name: "expires", value: ""}: 36,
{name: "from", value: ""}: 37,
{name: "host", value: ""}: 38,
{name: "if-match", value: ""}: 39,
{name: "if-modified-since", value: ""}: 40,
{name: "if-none-match", value: ""}: 41,
{name: "if-range", value: ""}: 42,
{name: "if-unmodified-since", value: ""}: 43,
{name: "last-modified", value: ""}: 44,
{name: "link", value: ""}: 45,
{name: "location", value: ""}: 46,
{name: "max-forwards", value: ""}: 47,
{name: "proxy-authenticate", value: ""}: 48,
{name: "proxy-authorization", value: ""}: 49,
{name: "range", value: ""}: 50,
{name: "referer", value: ""}: 51,
{name: "refresh", value: ""}: 52,
{name: "retry-after", value: ""}: 53,
{name: "server", value: ""}: 54,
{name: "set-cookie", value: ""}: 55,
{name: "strict-transport-security", value: ""}: 56,
{name: "transfer-encoding", value: ""}: 57,
{name: "user-agent", value: ""}: 58,
{name: "vary", value: ""}: 59,
{name: "via", value: ""}: 60,
{name: "www-authenticate", value: ""}: 61,
},
ents: []HeaderField{
{Name: ":authority", Value: "", Sensitive: false},
{Name: ":method", Value: "GET", Sensitive: false},
{Name: ":method", Value: "POST", Sensitive: false},
{Name: ":path", Value: "/", Sensitive: false},
{Name: ":path", Value: "/index.html", Sensitive: false},
{Name: ":scheme", Value: "http", Sensitive: false},
{Name: ":scheme", Value: "https", Sensitive: false},
{Name: ":status", Value: "200", Sensitive: false},
{Name: ":status", Value: "204", Sensitive: false},
{Name: ":status", Value: "206", Sensitive: false},
{Name: ":status", Value: "304", Sensitive: false},
{Name: ":status", Value: "400", Sensitive: false},
{Name: ":status", Value: "404", Sensitive: false},
{Name: ":status", Value: "500", Sensitive: false},
{Name: "accept-charset", Value: "", Sensitive: false},
{Name: "accept-encoding", Value: "gzip, deflate", Sensitive: false},
{Name: "accept-language", Value: "", Sensitive: false},
{Name: "accept-ranges", Value: "", Sensitive: false},
{Name: "accept", Value: "", Sensitive: false},
{Name: "access-control-allow-origin", Value: "", Sensitive: false},
{Name: "age", Value: "", Sensitive: false},
{Name: "allow", Value: "", Sensitive: false},
{Name: "authorization", Value: "", Sensitive: false},
{Name: "cache-control", Value: "", Sensitive: false},
{Name: "content-disposition", Value: "", Sensitive: false},
{Name: "content-encoding", Value: "", Sensitive: false},
{Name: "content-language", Value: "", Sensitive: false},
{Name: "content-length", Value: "", Sensitive: false},
{Name: "content-location", Value: "", Sensitive: false},
{Name: "content-range", Value: "", Sensitive: false},
{Name: "content-type", Value: "", Sensitive: false},
{Name: "cookie", Value: "", Sensitive: false},
{Name: "date", Value: "", Sensitive: false},
{Name: "etag", Value: "", Sensitive: false},
{Name: "expect", Value: "", Sensitive: false},
{Name: "expires", Value: "", Sensitive: false},
{Name: "from", Value: "", Sensitive: false},
{Name: "host", Value: "", Sensitive: false},
{Name: "if-match", Value: "", Sensitive: false},
{Name: "if-modified-since", Value: "", Sensitive: false},
{Name: "if-none-match", Value: "", Sensitive: false},
{Name: "if-range", Value: "", Sensitive: false},
{Name: "if-unmodified-since", Value: "", Sensitive: false},
{Name: "last-modified", Value: "", Sensitive: false},
{Name: "link", Value: "", Sensitive: false},
{Name: "location", Value: "", Sensitive: false},
{Name: "max-forwards", Value: "", Sensitive: false},
{Name: "proxy-authenticate", Value: "", Sensitive: false},
{Name: "proxy-authorization", Value: "", Sensitive: false},
{Name: "range", Value: "", Sensitive: false},
{Name: "referer", Value: "", Sensitive: false},
{Name: "refresh", Value: "", Sensitive: false},
{Name: "retry-after", Value: "", Sensitive: false},
{Name: "server", Value: "", Sensitive: false},
{Name: "set-cookie", Value: "", Sensitive: false},
{Name: "strict-transport-security", Value: "", Sensitive: false},
{Name: "transfer-encoding", Value: "", Sensitive: false},
{Name: "user-agent", Value: "", Sensitive: false},
{Name: "vary", Value: "", Sensitive: false},
{Name: "via", Value: "", Sensitive: false},
{Name: "www-authenticate", Value: "", Sensitive: false},
},
}

View File

@ -96,8 +96,7 @@ func (t *headerFieldTable) evictOldest(n int) {
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic // meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
// table, the return value i actually refers to the entry t.ents[t.len()-i]. // table, the return value i actually refers to the entry t.ents[t.len()-i].
// //
// All tables are assumed to be a dynamic tables except for the global // All tables are assumed to be a dynamic tables except for the global staticTable.
// staticTable pointer.
// //
// See Section 2.3.3. // See Section 2.3.3.
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) { func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
@ -125,81 +124,6 @@ func (t *headerFieldTable) idToIndex(id uint64) uint64 {
return k + 1 return k + 1
} }
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
var staticTable = newStaticTable()
var staticTableEntries = [...]HeaderField{
{Name: ":authority"},
{Name: ":method", Value: "GET"},
{Name: ":method", Value: "POST"},
{Name: ":path", Value: "/"},
{Name: ":path", Value: "/index.html"},
{Name: ":scheme", Value: "http"},
{Name: ":scheme", Value: "https"},
{Name: ":status", Value: "200"},
{Name: ":status", Value: "204"},
{Name: ":status", Value: "206"},
{Name: ":status", Value: "304"},
{Name: ":status", Value: "400"},
{Name: ":status", Value: "404"},
{Name: ":status", Value: "500"},
{Name: "accept-charset"},
{Name: "accept-encoding", Value: "gzip, deflate"},
{Name: "accept-language"},
{Name: "accept-ranges"},
{Name: "accept"},
{Name: "access-control-allow-origin"},
{Name: "age"},
{Name: "allow"},
{Name: "authorization"},
{Name: "cache-control"},
{Name: "content-disposition"},
{Name: "content-encoding"},
{Name: "content-language"},
{Name: "content-length"},
{Name: "content-location"},
{Name: "content-range"},
{Name: "content-type"},
{Name: "cookie"},
{Name: "date"},
{Name: "etag"},
{Name: "expect"},
{Name: "expires"},
{Name: "from"},
{Name: "host"},
{Name: "if-match"},
{Name: "if-modified-since"},
{Name: "if-none-match"},
{Name: "if-range"},
{Name: "if-unmodified-since"},
{Name: "last-modified"},
{Name: "link"},
{Name: "location"},
{Name: "max-forwards"},
{Name: "proxy-authenticate"},
{Name: "proxy-authorization"},
{Name: "range"},
{Name: "referer"},
{Name: "refresh"},
{Name: "retry-after"},
{Name: "server"},
{Name: "set-cookie"},
{Name: "strict-transport-security"},
{Name: "transfer-encoding"},
{Name: "user-agent"},
{Name: "vary"},
{Name: "via"},
{Name: "www-authenticate"},
}
func newStaticTable() *headerFieldTable {
t := &headerFieldTable{}
t.init()
for _, e := range staticTableEntries[:] {
t.addEntry(e)
}
return t
}
var huffmanCodes = [256]uint32{ var huffmanCodes = [256]uint32{
0x1ff8, 0x1ff8,
0x7fffd8, 0x7fffd8,

View File

@ -98,6 +98,19 @@ type Server struct {
// the HTTP/2 spec's recommendations. // the HTTP/2 spec's recommendations.
MaxConcurrentStreams uint32 MaxConcurrentStreams uint32
// MaxDecoderHeaderTableSize optionally specifies the http2
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
// informs the remote endpoint of the maximum size of the header compression
// table used to decode header blocks, in octets. If zero, the default value
// of 4096 is used.
MaxDecoderHeaderTableSize uint32
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
// header compression table used for encoding request headers. Received
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
// the default value of 4096 is used.
MaxEncoderHeaderTableSize uint32
// MaxReadFrameSize optionally specifies the largest frame // MaxReadFrameSize optionally specifies the largest frame
// this server is willing to read. A valid value is between // this server is willing to read. A valid value is between
// 16k and 16M, inclusive. If zero or otherwise invalid, a // 16k and 16M, inclusive. If zero or otherwise invalid, a
@ -170,6 +183,20 @@ func (s *Server) maxConcurrentStreams() uint32 {
return defaultMaxStreams return defaultMaxStreams
} }
func (s *Server) maxDecoderHeaderTableSize() uint32 {
if v := s.MaxDecoderHeaderTableSize; v > 0 {
return v
}
return initialHeaderTableSize
}
func (s *Server) maxEncoderHeaderTableSize() uint32 {
if v := s.MaxEncoderHeaderTableSize; v > 0 {
return v
}
return initialHeaderTableSize
}
// maxQueuedControlFrames is the maximum number of control frames like // maxQueuedControlFrames is the maximum number of control frames like
// SETTINGS, PING and RST_STREAM that will be queued for writing before // SETTINGS, PING and RST_STREAM that will be queued for writing before
// the connection is closed to prevent memory exhaustion attacks. // the connection is closed to prevent memory exhaustion attacks.
@ -394,7 +421,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
advMaxStreams: s.maxConcurrentStreams(), advMaxStreams: s.maxConcurrentStreams(),
initialStreamSendWindowSize: initialWindowSize, initialStreamSendWindowSize: initialWindowSize,
maxFrameSize: initialMaxFrameSize, maxFrameSize: initialMaxFrameSize,
headerTableSize: initialHeaderTableSize,
serveG: newGoroutineLock(), serveG: newGoroutineLock(),
pushEnabled: true, pushEnabled: true,
sawClientPreface: opts.SawClientPreface, sawClientPreface: opts.SawClientPreface,
@ -422,14 +448,15 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
// configured value for inflow, that will be updated when we send a // configured value for inflow, that will be updated when we send a
// WINDOW_UPDATE shortly after sending SETTINGS. // WINDOW_UPDATE shortly after sending SETTINGS.
sc.flow.add(initialWindowSize) sc.flow.add(initialWindowSize)
sc.inflow.add(initialWindowSize) sc.inflow.init(initialWindowSize)
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
sc.hpackEncoder.SetMaxDynamicTableSizeLimit(s.maxEncoderHeaderTableSize())
fr := NewFramer(sc.bw, c) fr := NewFramer(sc.bw, c)
if s.CountError != nil { if s.CountError != nil {
fr.countError = s.CountError fr.countError = s.CountError
} }
fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil) fr.ReadMetaHeaders = hpack.NewDecoder(s.maxDecoderHeaderTableSize(), nil)
fr.MaxHeaderListSize = sc.maxHeaderListSize() fr.MaxHeaderListSize = sc.maxHeaderListSize()
fr.SetMaxReadFrameSize(s.maxReadFrameSize()) fr.SetMaxReadFrameSize(s.maxReadFrameSize())
sc.framer = fr sc.framer = fr
@ -536,8 +563,8 @@ type serverConn struct {
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
bodyReadCh chan bodyReadMsg // from handlers -> serve bodyReadCh chan bodyReadMsg // from handlers -> serve
serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop
flow flow // conn-wide (not stream-specific) outbound flow control flow outflow // conn-wide (not stream-specific) outbound flow control
inflow flow // conn-wide inbound flow control inflow inflow // conn-wide inbound flow control
tlsState *tls.ConnectionState // shared by all handlers, like net/http tlsState *tls.ConnectionState // shared by all handlers, like net/http
remoteAddrStr string remoteAddrStr string
writeSched WriteScheduler writeSched WriteScheduler
@ -559,9 +586,9 @@ type serverConn struct {
streams map[uint32]*stream streams map[uint32]*stream
initialStreamSendWindowSize int32 initialStreamSendWindowSize int32
maxFrameSize int32 maxFrameSize int32
headerTableSize uint32
peerMaxHeaderListSize uint32 // zero means unknown (default) peerMaxHeaderListSize uint32 // zero means unknown (default)
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
canonHeaderKeysSize int // canonHeader keys size in bytes
writingFrame bool // started writing a frame (on serve goroutine or separate) writingFrame bool // started writing a frame (on serve goroutine or separate)
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
needsFrameFlush bool // last frame write wasn't a flush needsFrameFlush bool // last frame write wasn't a flush
@ -616,13 +643,15 @@ type stream struct {
// owned by serverConn's serve loop: // owned by serverConn's serve loop:
bodyBytes int64 // body bytes seen so far bodyBytes int64 // body bytes seen so far
declBodyBytes int64 // or -1 if undeclared declBodyBytes int64 // or -1 if undeclared
flow flow // limits writing from Handler to client flow outflow // limits writing from Handler to client
inflow flow // what the client is allowed to POST/etc to us inflow inflow // what the client is allowed to POST/etc to us
state streamState state streamState
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
gotTrailerHeader bool // HEADER frame for trailers was seen gotTrailerHeader bool // HEADER frame for trailers was seen
wroteHeaders bool // whether we wrote headers (not status 100) wroteHeaders bool // whether we wrote headers (not status 100)
readDeadline *time.Timer // nil if unused
writeDeadline *time.Timer // nil if unused writeDeadline *time.Timer // nil if unused
closeErr error // set before cw is closed
trailer http.Header // accumulated trailers trailer http.Header // accumulated trailers
reqTrailer http.Header // handler's Request.Trailer reqTrailer http.Header // handler's Request.Trailer
@ -738,6 +767,13 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
} }
} }
// maxCachedCanonicalHeadersKeysSize is an arbitrarily-chosen limit on the size
// of the entries in the canonHeader cache.
// This should be larger than the size of unique, uncommon header keys likely to
// be sent by the peer, while not so high as to permit unreasonable memory usage
// if the peer sends an unbounded number of unique header keys.
const maxCachedCanonicalHeadersKeysSize = 2048
func (sc *serverConn) canonicalHeader(v string) string { func (sc *serverConn) canonicalHeader(v string) string {
sc.serveG.check() sc.serveG.check()
buildCommonHeaderMapsOnce() buildCommonHeaderMapsOnce()
@ -753,14 +789,10 @@ func (sc *serverConn) canonicalHeader(v string) string {
sc.canonHeader = make(map[string]string) sc.canonHeader = make(map[string]string)
} }
cv = http.CanonicalHeaderKey(v) cv = http.CanonicalHeaderKey(v)
// maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of size := 100 + len(v)*2 // 100 bytes of map overhead + key + value
// entries in the canonHeader cache. This should be larger than the number if sc.canonHeaderKeysSize+size <= maxCachedCanonicalHeadersKeysSize {
// of unique, uncommon header keys likely to be sent by the peer, while not
// so high as to permit unreasonable memory usage if the peer sends an unbounded
// number of unique header keys.
const maxCachedCanonicalHeaders = 32
if len(sc.canonHeader) < maxCachedCanonicalHeaders {
sc.canonHeader[v] = cv sc.canonHeader[v] = cv
sc.canonHeaderKeysSize += size
} }
return cv return cv
} }
@ -811,8 +843,13 @@ type frameWriteResult struct {
// and then reports when it's done. // and then reports when it's done.
// At most one goroutine can be running writeFrameAsync at a time per // At most one goroutine can be running writeFrameAsync at a time per
// serverConn. // serverConn.
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest) { func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) {
err := wr.write.writeFrame(sc) var err error
if wd == nil {
err = wr.write.writeFrame(sc)
} else {
err = sc.framer.endWrite()
}
sc.wroteFrameCh <- frameWriteResult{wr: wr, err: err} sc.wroteFrameCh <- frameWriteResult{wr: wr, err: err}
} }
@ -862,6 +899,7 @@ func (sc *serverConn) serve() {
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()}, {SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
{SettingMaxConcurrentStreams, sc.advMaxStreams}, {SettingMaxConcurrentStreams, sc.advMaxStreams},
{SettingMaxHeaderListSize, sc.maxHeaderListSize()}, {SettingMaxHeaderListSize, sc.maxHeaderListSize()},
{SettingHeaderTableSize, sc.srv.maxDecoderHeaderTableSize()},
{SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())}, {SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())},
}, },
}) })
@ -869,7 +907,9 @@ func (sc *serverConn) serve() {
// Each connection starts with initialWindowSize inflow tokens. // Each connection starts with initialWindowSize inflow tokens.
// If a higher value is configured, we add more tokens. // If a higher value is configured, we add more tokens.
sc.sendWindowUpdate(nil) if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 {
sc.sendWindowUpdate(nil, int(diff))
}
if err := sc.readPreface(); err != nil { if err := sc.readPreface(); err != nil {
sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err) sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
@ -946,6 +986,8 @@ func (sc *serverConn) serve() {
} }
case *startPushRequest: case *startPushRequest:
sc.startPush(v) sc.startPush(v)
case func(*serverConn):
v(sc)
default: default:
panic(fmt.Sprintf("unexpected type %T", v)) panic(fmt.Sprintf("unexpected type %T", v))
} }
@ -1214,9 +1256,16 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
sc.writingFrameAsync = false sc.writingFrameAsync = false
err := wr.write.writeFrame(sc) err := wr.write.writeFrame(sc)
sc.wroteFrame(frameWriteResult{wr: wr, err: err}) sc.wroteFrame(frameWriteResult{wr: wr, err: err})
} else if wd, ok := wr.write.(*writeData); ok {
// Encode the frame in the serve goroutine, to ensure we don't have
// any lingering asynchronous references to data passed to Write.
// See https://go.dev/issue/58446.
sc.framer.startWriteDataPadded(wd.streamID, wd.endStream, wd.p, nil)
sc.writingFrameAsync = true
go sc.writeFrameAsync(wr, wd)
} else { } else {
sc.writingFrameAsync = true sc.writingFrameAsync = true
go sc.writeFrameAsync(wr) go sc.writeFrameAsync(wr, nil)
} }
} }
@ -1459,6 +1508,21 @@ func (sc *serverConn) processFrame(f Frame) error {
sc.sawFirstSettings = true sc.sawFirstSettings = true
} }
// Discard frames for streams initiated after the identified last
// stream sent in a GOAWAY, or all frames after sending an error.
// We still need to return connection-level flow control for DATA frames.
// RFC 9113 Section 6.8.
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || f.Header().StreamID > sc.maxClientStreamID) {
if f, ok := f.(*DataFrame); ok {
if !sc.inflow.take(f.Length) {
return sc.countError("data_flow", streamError(f.Header().StreamID, ErrCodeFlowControl))
}
sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
}
return nil
}
switch f := f.(type) { switch f := f.(type) {
case *SettingsFrame: case *SettingsFrame:
return sc.processSettings(f) return sc.processSettings(f)
@ -1501,9 +1565,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
// PROTOCOL_ERROR." // PROTOCOL_ERROR."
return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol)) return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol))
} }
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
return nil
}
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}}) sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
return nil return nil
} }
@ -1565,6 +1626,9 @@ func (sc *serverConn) closeStream(st *stream, err error) {
panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state)) panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
} }
st.state = stateClosed st.state = stateClosed
if st.readDeadline != nil {
st.readDeadline.Stop()
}
if st.writeDeadline != nil { if st.writeDeadline != nil {
st.writeDeadline.Stop() st.writeDeadline.Stop()
} }
@ -1586,10 +1650,18 @@ func (sc *serverConn) closeStream(st *stream, err error) {
if p := st.body; p != nil { if p := st.body; p != nil {
// Return any buffered unread bytes worth of conn-level flow control. // Return any buffered unread bytes worth of conn-level flow control.
// See golang.org/issue/16481 // See golang.org/issue/16481
sc.sendWindowUpdate(nil) sc.sendWindowUpdate(nil, p.Len())
p.CloseWithError(err) p.CloseWithError(err)
} }
if e, ok := err.(StreamError); ok {
if e.Cause != nil {
err = e.Cause
} else {
err = errStreamClosed
}
}
st.closeErr = err
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
sc.writeSched.CloseStream(st.id) sc.writeSched.CloseStream(st.id)
} }
@ -1632,7 +1704,6 @@ func (sc *serverConn) processSetting(s Setting) error {
} }
switch s.ID { switch s.ID {
case SettingHeaderTableSize: case SettingHeaderTableSize:
sc.headerTableSize = s.Val
sc.hpackEncoder.SetMaxDynamicTableSize(s.Val) sc.hpackEncoder.SetMaxDynamicTableSize(s.Val)
case SettingEnablePush: case SettingEnablePush:
sc.pushEnabled = s.Val != 0 sc.pushEnabled = s.Val != 0
@ -1686,16 +1757,6 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
func (sc *serverConn) processData(f *DataFrame) error { func (sc *serverConn) processData(f *DataFrame) error {
sc.serveG.check() sc.serveG.check()
id := f.Header().StreamID id := f.Header().StreamID
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
// Discard all DATA frames if the GOAWAY is due to an
// error, or:
//
// Section 6.8: After sending a GOAWAY frame, the sender
// can discard frames for streams initiated by the
// receiver with identifiers higher than the identified
// last stream.
return nil
}
data := f.Data() data := f.Data()
state, st := sc.state(id) state, st := sc.state(id)
@ -1726,15 +1787,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
// But still enforce their connection-level flow control, // But still enforce their connection-level flow control,
// and return any flow control bytes since we're not going // and return any flow control bytes since we're not going
// to consume them. // to consume them.
if sc.inflow.available() < int32(f.Length) { if !sc.inflow.take(f.Length) {
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl)) return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
} }
// Deduct the flow control from inflow, since we're sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
// going to immediately add it back in
// sendWindowUpdate, which also schedules sending the
// frames.
sc.inflow.take(int32(f.Length))
sc.sendWindowUpdate(nil) // conn-level
if st != nil && st.resetQueued { if st != nil && st.resetQueued {
// Already have a stream error in flight. Don't send another. // Already have a stream error in flight. Don't send another.
@ -1748,11 +1804,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
// Sender sending more than they'd declared? // Sender sending more than they'd declared?
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
if sc.inflow.available() < int32(f.Length) { if !sc.inflow.take(f.Length) {
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl)) return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
} }
sc.inflow.take(int32(f.Length)) sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
sc.sendWindowUpdate(nil) // conn-level
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the // RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
@ -1762,15 +1817,14 @@ func (sc *serverConn) processData(f *DataFrame) error {
} }
if f.Length > 0 { if f.Length > 0 {
// Check whether the client has flow control quota. // Check whether the client has flow control quota.
if st.inflow.available() < int32(f.Length) { if !takeInflows(&sc.inflow, &st.inflow, f.Length) {
return sc.countError("flow_on_data_length", streamError(id, ErrCodeFlowControl)) return sc.countError("flow_on_data_length", streamError(id, ErrCodeFlowControl))
} }
st.inflow.take(int32(f.Length))
if len(data) > 0 { if len(data) > 0 {
wrote, err := st.body.Write(data) wrote, err := st.body.Write(data)
if err != nil { if err != nil {
sc.sendWindowUpdate32(nil, int32(f.Length)-int32(wrote)) sc.sendWindowUpdate(nil, int(f.Length)-wrote)
return sc.countError("body_write_err", streamError(id, ErrCodeStreamClosed)) return sc.countError("body_write_err", streamError(id, ErrCodeStreamClosed))
} }
if wrote != len(data) { if wrote != len(data) {
@ -1781,11 +1835,13 @@ func (sc *serverConn) processData(f *DataFrame) error {
// Return any padded flow control now, since we won't // Return any padded flow control now, since we won't
// refund it later on body reads. // refund it later on body reads.
if pad := int32(f.Length) - int32(len(data)); pad > 0 { // Call sendWindowUpdate even if there is no padding,
// to return buffered flow control credit if the sent
// window has shrunk.
pad := int32(f.Length) - int32(len(data))
sc.sendWindowUpdate32(nil, pad) sc.sendWindowUpdate32(nil, pad)
sc.sendWindowUpdate32(st, pad) sc.sendWindowUpdate32(st, pad)
} }
}
if f.StreamEnded() { if f.StreamEnded() {
st.endStream() st.endStream()
} }
@ -1838,19 +1894,27 @@ func (st *stream) copyTrailersToHandlerRequest() {
} }
} }
// onReadTimeout is run on its own goroutine (from time.AfterFunc)
// when the stream's ReadTimeout has fired.
func (st *stream) onReadTimeout() {
// Wrap the ErrDeadlineExceeded to avoid callers depending on us
// returning the bare error.
st.body.CloseWithError(fmt.Errorf("%w", os.ErrDeadlineExceeded))
}
// onWriteTimeout is run on its own goroutine (from time.AfterFunc) // onWriteTimeout is run on its own goroutine (from time.AfterFunc)
// when the stream's WriteTimeout has fired. // when the stream's WriteTimeout has fired.
func (st *stream) onWriteTimeout() { func (st *stream) onWriteTimeout() {
st.sc.writeFrameFromHandler(FrameWriteRequest{write: streamError(st.id, ErrCodeInternal)}) st.sc.writeFrameFromHandler(FrameWriteRequest{write: StreamError{
StreamID: st.id,
Code: ErrCodeInternal,
Cause: os.ErrDeadlineExceeded,
}})
} }
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
sc.serveG.check() sc.serveG.check()
id := f.StreamID id := f.StreamID
if sc.inGoAway {
// Ignore.
return nil
}
// http://tools.ietf.org/html/rfc7540#section-5.1.1 // http://tools.ietf.org/html/rfc7540#section-5.1.1
// Streams initiated by a client MUST use odd-numbered stream // Streams initiated by a client MUST use odd-numbered stream
// identifiers. [...] An endpoint that receives an unexpected // identifiers. [...] An endpoint that receives an unexpected
@ -1953,6 +2017,9 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// (in Go 1.8), though. That's a more sane option anyway. // (in Go 1.8), though. That's a more sane option anyway.
if sc.hs.ReadTimeout != 0 { if sc.hs.ReadTimeout != 0 {
sc.conn.SetReadDeadline(time.Time{}) sc.conn.SetReadDeadline(time.Time{})
if st.body != nil {
st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
}
} }
go sc.runHandler(rw, req, handler) go sc.runHandler(rw, req, handler)
@ -2021,9 +2088,6 @@ func (sc *serverConn) checkPriority(streamID uint32, p PriorityParam) error {
} }
func (sc *serverConn) processPriority(f *PriorityFrame) error { func (sc *serverConn) processPriority(f *PriorityFrame) error {
if sc.inGoAway {
return nil
}
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil { if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
return err return err
} }
@ -2048,8 +2112,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
st.cw.Init() st.cw.Init()
st.flow.conn = &sc.flow // link to conn-level counter st.flow.conn = &sc.flow // link to conn-level counter
st.flow.add(sc.initialStreamSendWindowSize) st.flow.add(sc.initialStreamSendWindowSize)
st.inflow.conn = &sc.inflow // link to conn-level counter st.inflow.init(sc.srv.initialStreamRecvWindowSize())
st.inflow.add(sc.srv.initialStreamRecvWindowSize())
if sc.hs.WriteTimeout != 0 { if sc.hs.WriteTimeout != 0 {
st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
} }
@ -2141,7 +2204,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
tlsState = sc.tlsState tlsState = sc.tlsState
} }
needsContinue := rp.header.Get("Expect") == "100-continue" needsContinue := httpguts.HeaderValuesContainsToken(rp.header["Expect"], "100-continue")
if needsContinue { if needsContinue {
rp.header.Del("Expect") rp.header.Del("Expect")
} }
@ -2322,71 +2385,37 @@ func (sc *serverConn) noteBodyReadFromHandler(st *stream, n int, err error) {
func (sc *serverConn) noteBodyRead(st *stream, n int) { func (sc *serverConn) noteBodyRead(st *stream, n int) {
sc.serveG.check() sc.serveG.check()
sc.sendWindowUpdate(nil) // conn-level sc.sendWindowUpdate(nil, n) // conn-level
if st.state != stateHalfClosedRemote && st.state != stateClosed { if st.state != stateHalfClosedRemote && st.state != stateClosed {
// Don't send this WINDOW_UPDATE if the stream is closed // Don't send this WINDOW_UPDATE if the stream is closed
// remotely. // remotely.
sc.sendWindowUpdate(st) sc.sendWindowUpdate(st, n)
} }
} }
// st may be nil for conn-level
func (sc *serverConn) sendWindowUpdate(st *stream) {
sc.serveG.check()
var n int32
if st == nil {
if avail, windowSize := sc.inflow.available(), sc.srv.initialConnRecvWindowSize(); avail > windowSize/2 {
return
} else {
n = windowSize - avail
}
} else {
if avail, windowSize := st.inflow.available(), sc.srv.initialStreamRecvWindowSize(); avail > windowSize/2 {
return
} else {
n = windowSize - avail
}
}
// "The legal range for the increment to the flow control
// window is 1 to 2^31-1 (2,147,483,647) octets."
// A Go Read call on 64-bit machines could in theory read
// a larger Read than this. Very unlikely, but we handle it here
// rather than elsewhere for now.
const maxUint31 = 1<<31 - 1
for n >= maxUint31 {
sc.sendWindowUpdate32(st, maxUint31)
n -= maxUint31
}
sc.sendWindowUpdate32(st, int32(n))
}
// st may be nil for conn-level // st may be nil for conn-level
func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) { func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
sc.sendWindowUpdate(st, int(n))
}
// st may be nil for conn-level
func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
sc.serveG.check() sc.serveG.check()
if n == 0 { var streamID uint32
var send int32
if st == nil {
send = sc.inflow.add(n)
} else {
streamID = st.id
send = st.inflow.add(n)
}
if send == 0 {
return return
} }
if n < 0 {
panic("negative update")
}
var streamID uint32
if st != nil {
streamID = st.id
}
sc.writeFrame(FrameWriteRequest{ sc.writeFrame(FrameWriteRequest{
write: writeWindowUpdate{streamID: streamID, n: uint32(n)}, write: writeWindowUpdate{streamID: streamID, n: uint32(send)},
stream: st, stream: st,
}) })
var ok bool
if st == nil {
ok = sc.inflow.add(n)
} else {
ok = st.inflow.add(n)
}
if !ok {
panic("internal error; sent too many window updates without decrements?")
}
} }
// requestBody is the Handler's Request.Body type. // requestBody is the Handler's Request.Body type.
@ -2474,7 +2503,15 @@ type responseWriterState struct {
type chunkWriter struct{ rws *responseWriterState } type chunkWriter struct{ rws *responseWriterState }
func (cw chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) } func (cw chunkWriter) Write(p []byte) (n int, err error) {
n, err = cw.rws.writeChunk(p)
if err == errStreamClosed {
// If writing failed because the stream has been closed,
// return the reason it was closed.
err = cw.rws.stream.closeErr
}
return n, err
}
func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) > 0 } func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) > 0 }
@ -2668,24 +2705,86 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
} }
} }
func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
st := w.rws.stream
if !deadline.IsZero() && deadline.Before(time.Now()) {
// If we're setting a deadline in the past, reset the stream immediately
// so writes after SetWriteDeadline returns will fail.
st.onReadTimeout()
return nil
}
w.rws.conn.sendServeMsg(func(sc *serverConn) {
if st.readDeadline != nil {
if !st.readDeadline.Stop() {
// Deadline already exceeded, or stream has been closed.
return
}
}
if deadline.IsZero() {
st.readDeadline = nil
} else if st.readDeadline == nil {
st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
} else {
st.readDeadline.Reset(deadline.Sub(time.Now()))
}
})
return nil
}
func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
st := w.rws.stream
if !deadline.IsZero() && deadline.Before(time.Now()) {
// If we're setting a deadline in the past, reset the stream immediately
// so writes after SetWriteDeadline returns will fail.
st.onWriteTimeout()
return nil
}
w.rws.conn.sendServeMsg(func(sc *serverConn) {
if st.writeDeadline != nil {
if !st.writeDeadline.Stop() {
// Deadline already exceeded, or stream has been closed.
return
}
}
if deadline.IsZero() {
st.writeDeadline = nil
} else if st.writeDeadline == nil {
st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
} else {
st.writeDeadline.Reset(deadline.Sub(time.Now()))
}
})
return nil
}
func (w *responseWriter) Flush() { func (w *responseWriter) Flush() {
w.FlushError()
}
func (w *responseWriter) FlushError() error {
rws := w.rws rws := w.rws
if rws == nil { if rws == nil {
panic("Header called after Handler finished") panic("Header called after Handler finished")
} }
var err error
if rws.bw.Buffered() > 0 { if rws.bw.Buffered() > 0 {
if err := rws.bw.Flush(); err != nil { err = rws.bw.Flush()
// Ignore the error. The frame writer already knows.
return
}
} else { } else {
// The bufio.Writer won't call chunkWriter.Write // The bufio.Writer won't call chunkWriter.Write
// (writeChunk with zero bytes, so we have to do it // (writeChunk with zero bytes, so we have to do it
// ourselves to force the HTTP response header and/or // ourselves to force the HTTP response header and/or
// final DATA frame (with END_STREAM) to be sent. // final DATA frame (with END_STREAM) to be sent.
rws.writeChunk(nil) _, err = chunkWriter{rws}.Write(nil)
if err == nil {
select {
case <-rws.stream.cw:
err = rws.stream.closeErr
default:
} }
} }
}
return err
}
func (w *responseWriter) CloseNotify() <-chan bool { func (w *responseWriter) CloseNotify() <-chan bool {
rws := w.rws rws := w.rws

View File

@ -16,6 +16,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/fs"
"log" "log"
"math" "math"
mathrand "math/rand" mathrand "math/rand"
@ -46,10 +47,6 @@ const (
// we buffer per stream. // we buffer per stream.
transportDefaultStreamFlow = 4 << 20 transportDefaultStreamFlow = 4 << 20
// transportDefaultStreamMinRefresh is the minimum number of bytes we'll send
// a stream-level WINDOW_UPDATE for at a time.
transportDefaultStreamMinRefresh = 4 << 10
defaultUserAgent = "Go-http-client/2.0" defaultUserAgent = "Go-http-client/2.0"
// initialMaxConcurrentStreams is a connections maxConcurrentStreams until // initialMaxConcurrentStreams is a connections maxConcurrentStreams until
@ -117,6 +114,28 @@ type Transport struct {
// to mean no limit. // to mean no limit.
MaxHeaderListSize uint32 MaxHeaderListSize uint32
// MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the
// initial settings frame. It is the size in bytes of the largest frame
// payload that the sender is willing to receive. If 0, no setting is
// sent, and the value is provided by the peer, which should be 16384
// according to the spec:
// https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2.
// Values are bounded in the range 16k to 16M.
MaxReadFrameSize uint32
// MaxDecoderHeaderTableSize optionally specifies the http2
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
// informs the remote endpoint of the maximum size of the header compression
// table used to decode header blocks, in octets. If zero, the default value
// of 4096 is used.
MaxDecoderHeaderTableSize uint32
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
// header compression table used for encoding request headers. Received
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
// the default value of 4096 is used.
MaxEncoderHeaderTableSize uint32
// StrictMaxConcurrentStreams controls whether the server's // StrictMaxConcurrentStreams controls whether the server's
// SETTINGS_MAX_CONCURRENT_STREAMS should be respected // SETTINGS_MAX_CONCURRENT_STREAMS should be respected
// globally. If false, new TCP connections are created to the // globally. If false, new TCP connections are created to the
@ -170,6 +189,19 @@ func (t *Transport) maxHeaderListSize() uint32 {
return t.MaxHeaderListSize return t.MaxHeaderListSize
} }
func (t *Transport) maxFrameReadSize() uint32 {
if t.MaxReadFrameSize == 0 {
return 0 // use the default provided by the peer
}
if t.MaxReadFrameSize < minMaxFrameSize {
return minMaxFrameSize
}
if t.MaxReadFrameSize > maxFrameSize {
return maxFrameSize
}
return t.MaxReadFrameSize
}
func (t *Transport) disableCompression() bool { func (t *Transport) disableCompression() bool {
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
} }
@ -274,8 +306,8 @@ type ClientConn struct {
mu sync.Mutex // guards following mu sync.Mutex // guards following
cond *sync.Cond // hold mu; broadcast on flow/closed changes cond *sync.Cond // hold mu; broadcast on flow/closed changes
flow flow // our conn-level flow control quota (cs.flow is per stream) flow outflow // our conn-level flow control quota (cs.outflow is per stream)
inflow flow // peer's conn-level flow control inflow inflow // peer's conn-level flow control
doNotReuse bool // whether conn is marked to not be reused for any future requests doNotReuse bool // whether conn is marked to not be reused for any future requests
closing bool closing bool
closed bool closed bool
@ -295,6 +327,7 @@ type ClientConn struct {
maxFrameSize uint32 maxFrameSize uint32
maxConcurrentStreams uint32 maxConcurrentStreams uint32
peerMaxHeaderListSize uint64 peerMaxHeaderListSize uint64
peerMaxHeaderTableSize uint32
initialWindowSize uint32 initialWindowSize uint32
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
@ -339,8 +372,8 @@ type clientStream struct {
respHeaderRecv chan struct{} // closed when headers are received respHeaderRecv chan struct{} // closed when headers are received
res *http.Response // set if respHeaderRecv is closed res *http.Response // set if respHeaderRecv is closed
flow flow // guarded by cc.mu flow outflow // guarded by cc.mu
inflow flow // guarded by cc.mu inflow inflow // guarded by cc.mu
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
readErr error // sticky read error; owned by transportResponseBody.Read readErr error // sticky read error; owned by transportResponseBody.Read
@ -501,6 +534,15 @@ func authorityAddr(scheme string, authority string) (addr string) {
return net.JoinHostPort(host, port) return net.JoinHostPort(host, port)
} }
var retryBackoffHook func(time.Duration) *time.Timer
func backoffNewTimer(d time.Duration) *time.Timer {
if retryBackoffHook != nil {
return retryBackoffHook(d)
}
return time.NewTimer(d)
}
// RoundTripOpt is like RoundTrip, but takes options. // RoundTripOpt is like RoundTrip, but takes options.
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) {
@ -526,11 +568,14 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
} }
backoff := float64(uint(1) << (uint(retry) - 1)) backoff := float64(uint(1) << (uint(retry) - 1))
backoff += backoff * (0.1 * mathrand.Float64()) backoff += backoff * (0.1 * mathrand.Float64())
d := time.Second * time.Duration(backoff)
timer := backoffNewTimer(d)
select { select {
case <-time.After(time.Second * time.Duration(backoff)): case <-timer.C:
t.vlogf("RoundTrip retrying after failure: %v", err) t.vlogf("RoundTrip retrying after failure: %v", err)
continue continue
case <-req.Context().Done(): case <-req.Context().Done():
timer.Stop()
err = req.Context().Err() err = req.Context().Err()
} }
} }
@ -668,6 +713,20 @@ func (t *Transport) expectContinueTimeout() time.Duration {
return t.t1.ExpectContinueTimeout return t.t1.ExpectContinueTimeout
} }
func (t *Transport) maxDecoderHeaderTableSize() uint32 {
if v := t.MaxDecoderHeaderTableSize; v > 0 {
return v
}
return initialHeaderTableSize
}
func (t *Transport) maxEncoderHeaderTableSize() uint32 {
if v := t.MaxEncoderHeaderTableSize; v > 0 {
return v
}
return initialHeaderTableSize
}
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
return t.newClientConn(c, t.disableKeepAlives()) return t.newClientConn(c, t.disableKeepAlives())
} }
@ -708,15 +767,19 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
}) })
cc.br = bufio.NewReader(c) cc.br = bufio.NewReader(c)
cc.fr = NewFramer(cc.bw, cc.br) cc.fr = NewFramer(cc.bw, cc.br)
if t.maxFrameReadSize() != 0 {
cc.fr.SetMaxReadFrameSize(t.maxFrameReadSize())
}
if t.CountError != nil { if t.CountError != nil {
cc.fr.countError = t.CountError cc.fr.countError = t.CountError
} }
cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil) maxHeaderTableSize := t.maxDecoderHeaderTableSize()
cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil)
cc.fr.MaxHeaderListSize = t.maxHeaderListSize() cc.fr.MaxHeaderListSize = t.maxHeaderListSize()
// TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on
// henc in response to SETTINGS frames?
cc.henc = hpack.NewEncoder(&cc.hbuf) cc.henc = hpack.NewEncoder(&cc.hbuf)
cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize())
cc.peerMaxHeaderTableSize = initialHeaderTableSize
if t.AllowHTTP { if t.AllowHTTP {
cc.nextStreamID = 3 cc.nextStreamID = 3
@ -731,14 +794,20 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
{ID: SettingEnablePush, Val: 0}, {ID: SettingEnablePush, Val: 0},
{ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow}, {ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow},
} }
if max := t.maxFrameReadSize(); max != 0 {
initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: max})
}
if max := t.maxHeaderListSize(); max != 0 { if max := t.maxHeaderListSize(); max != 0 {
initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max}) initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})
} }
if maxHeaderTableSize != initialHeaderTableSize {
initialSettings = append(initialSettings, Setting{ID: SettingHeaderTableSize, Val: maxHeaderTableSize})
}
cc.bw.Write(clientPreface) cc.bw.Write(clientPreface)
cc.fr.WriteSettings(initialSettings...) cc.fr.WriteSettings(initialSettings...)
cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow) cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow)
cc.inflow.add(transportDefaultConnFlow + initialWindowSize) cc.inflow.init(transportDefaultConnFlow + initialWindowSize)
cc.bw.Flush() cc.bw.Flush()
if cc.werr != nil { if cc.werr != nil {
cc.Close() cc.Close()
@ -1075,7 +1144,7 @@ var errRequestCanceled = errors.New("net/http: request canceled")
func commaSeparatedTrailers(req *http.Request) (string, error) { func commaSeparatedTrailers(req *http.Request) (string, error) {
keys := make([]string, 0, len(req.Trailer)) keys := make([]string, 0, len(req.Trailer))
for k := range req.Trailer { for k := range req.Trailer {
k = http.CanonicalHeaderKey(k) k = canonicalHeader(k)
switch k { switch k {
case "Transfer-Encoding", "Trailer", "Content-Length": case "Transfer-Encoding", "Trailer", "Content-Length":
return "", fmt.Errorf("invalid Trailer key %q", k) return "", fmt.Errorf("invalid Trailer key %q", k)
@ -1500,7 +1569,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
close(cs.donec) close(cs.donec)
} }
// awaitOpenSlotForStream waits until len(streams) < maxConcurrentStreams. // awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.
// Must hold cc.mu. // Must hold cc.mu.
func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
for { for {
@ -1612,7 +1681,7 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
var sawEOF bool var sawEOF bool
for !sawEOF { for !sawEOF {
n, err := body.Read(buf[:len(buf)]) n, err := body.Read(buf)
if hasContentLen { if hasContentLen {
remainLen -= int64(n) remainLen -= int64(n)
if remainLen == 0 && err == nil { if remainLen == 0 && err == nil {
@ -1915,7 +1984,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
// Header list size is ok. Write the headers. // Header list size is ok. Write the headers.
enumerateHeaders(func(name, value string) { enumerateHeaders(func(name, value string) {
name, ascii := asciiToLower(name) name, ascii := lowerHeader(name)
if !ascii { if !ascii {
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
// field names have to be ASCII characters (just as in HTTP/1.x). // field names have to be ASCII characters (just as in HTTP/1.x).
@ -1968,7 +2037,7 @@ func (cc *ClientConn) encodeTrailers(trailer http.Header) ([]byte, error) {
} }
for k, vv := range trailer { for k, vv := range trailer {
lowKey, ascii := asciiToLower(k) lowKey, ascii := lowerHeader(k)
if !ascii { if !ascii {
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
// field names have to be ASCII characters (just as in HTTP/1.x). // field names have to be ASCII characters (just as in HTTP/1.x).
@ -2000,8 +2069,7 @@ type resAndError struct {
func (cc *ClientConn) addStreamLocked(cs *clientStream) { func (cc *ClientConn) addStreamLocked(cs *clientStream) {
cs.flow.add(int32(cc.initialWindowSize)) cs.flow.add(int32(cc.initialWindowSize))
cs.flow.setConnFlow(&cc.flow) cs.flow.setConnFlow(&cc.flow)
cs.inflow.add(transportDefaultStreamFlow) cs.inflow.init(transportDefaultStreamFlow)
cs.inflow.setConnFlow(&cc.inflow)
cs.ID = cc.nextStreamID cs.ID = cc.nextStreamID
cc.nextStreamID += 2 cc.nextStreamID += 2
cc.streams[cs.ID] = cs cc.streams[cs.ID] = cs
@ -2301,7 +2369,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
Status: status + " " + http.StatusText(statusCode), Status: status + " " + http.StatusText(statusCode),
} }
for _, hf := range regularFields { for _, hf := range regularFields {
key := http.CanonicalHeaderKey(hf.Name) key := canonicalHeader(hf.Name)
if key == "Trailer" { if key == "Trailer" {
t := res.Trailer t := res.Trailer
if t == nil { if t == nil {
@ -2309,7 +2377,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
res.Trailer = t res.Trailer = t
} }
foreachHeaderElement(hf.Value, func(v string) { foreachHeaderElement(hf.Value, func(v string) {
t[http.CanonicalHeaderKey(v)] = nil t[canonicalHeader(v)] = nil
}) })
} else { } else {
vv := header[key] vv := header[key]
@ -2414,7 +2482,7 @@ func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFr
trailer := make(http.Header) trailer := make(http.Header)
for _, hf := range f.RegularFields() { for _, hf := range f.RegularFields() {
key := http.CanonicalHeaderKey(hf.Name) key := canonicalHeader(hf.Name)
trailer[key] = append(trailer[key], hf.Value) trailer[key] = append(trailer[key], hf.Value)
} }
cs.trailer = trailer cs.trailer = trailer
@ -2460,21 +2528,10 @@ func (b transportResponseBody) Read(p []byte) (n int, err error) {
} }
cc.mu.Lock() cc.mu.Lock()
var connAdd, streamAdd int32 connAdd := cc.inflow.add(n)
// Check the conn-level first, before the stream-level. var streamAdd int32
if v := cc.inflow.available(); v < transportDefaultConnFlow/2 {
connAdd = transportDefaultConnFlow - v
cc.inflow.add(connAdd)
}
if err == nil { // No need to refresh if the stream is over or failed. if err == nil { // No need to refresh if the stream is over or failed.
// Consider any buffered body data (read from the conn but not streamAdd = cs.inflow.add(n)
// consumed by the client) when computing flow control for this
// stream.
v := int(cs.inflow.available()) + cs.bufPipe.Len()
if v < transportDefaultStreamFlow-transportDefaultStreamMinRefresh {
streamAdd = int32(transportDefaultStreamFlow - v)
cs.inflow.add(streamAdd)
}
} }
cc.mu.Unlock() cc.mu.Unlock()
@ -2502,17 +2559,15 @@ func (b transportResponseBody) Close() error {
if unread > 0 { if unread > 0 {
cc.mu.Lock() cc.mu.Lock()
// Return connection-level flow control. // Return connection-level flow control.
if unread > 0 { connAdd := cc.inflow.add(unread)
cc.inflow.add(int32(unread))
}
cc.mu.Unlock() cc.mu.Unlock()
// TODO(dneil): Acquiring this mutex can block indefinitely. // TODO(dneil): Acquiring this mutex can block indefinitely.
// Move flow control return to a goroutine? // Move flow control return to a goroutine?
cc.wmu.Lock() cc.wmu.Lock()
// Return connection-level flow control. // Return connection-level flow control.
if unread > 0 { if connAdd > 0 {
cc.fr.WriteWindowUpdate(0, uint32(unread)) cc.fr.WriteWindowUpdate(0, uint32(connAdd))
} }
cc.bw.Flush() cc.bw.Flush()
cc.wmu.Unlock() cc.wmu.Unlock()
@ -2555,14 +2610,19 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
// But at least return their flow control: // But at least return their flow control:
if f.Length > 0 { if f.Length > 0 {
cc.mu.Lock() cc.mu.Lock()
cc.inflow.add(int32(f.Length)) ok := cc.inflow.take(f.Length)
connAdd := cc.inflow.add(int(f.Length))
cc.mu.Unlock() cc.mu.Unlock()
if !ok {
return ConnectionError(ErrCodeFlowControl)
}
if connAdd > 0 {
cc.wmu.Lock() cc.wmu.Lock()
cc.fr.WriteWindowUpdate(0, uint32(f.Length)) cc.fr.WriteWindowUpdate(0, uint32(connAdd))
cc.bw.Flush() cc.bw.Flush()
cc.wmu.Unlock() cc.wmu.Unlock()
} }
}
return nil return nil
} }
if cs.readClosed { if cs.readClosed {
@ -2592,9 +2652,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
} }
// Check connection-level flow control. // Check connection-level flow control.
cc.mu.Lock() cc.mu.Lock()
if cs.inflow.available() >= int32(f.Length) { if !takeInflows(&cc.inflow, &cs.inflow, f.Length) {
cs.inflow.take(int32(f.Length))
} else {
cc.mu.Unlock() cc.mu.Unlock()
return ConnectionError(ErrCodeFlowControl) return ConnectionError(ErrCodeFlowControl)
} }
@ -2616,19 +2674,20 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
} }
} }
if refund > 0 { sendConn := cc.inflow.add(refund)
cc.inflow.add(int32(refund)) var sendStream int32
if !didReset { if !didReset {
cs.inflow.add(int32(refund)) sendStream = cs.inflow.add(refund)
}
} }
cc.mu.Unlock() cc.mu.Unlock()
if refund > 0 { if sendConn > 0 || sendStream > 0 {
cc.wmu.Lock() cc.wmu.Lock()
cc.fr.WriteWindowUpdate(0, uint32(refund)) if sendConn > 0 {
if !didReset { cc.fr.WriteWindowUpdate(0, uint32(sendConn))
cc.fr.WriteWindowUpdate(cs.ID, uint32(refund)) }
if sendStream > 0 {
cc.fr.WriteWindowUpdate(cs.ID, uint32(sendStream))
} }
cc.bw.Flush() cc.bw.Flush()
cc.wmu.Unlock() cc.wmu.Unlock()
@ -2760,8 +2819,10 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
cc.cond.Broadcast() cc.cond.Broadcast()
cc.initialWindowSize = s.Val cc.initialWindowSize = s.Val
case SettingHeaderTableSize:
cc.henc.SetMaxDynamicTableSize(s.Val)
cc.peerMaxHeaderTableSize = s.Val
default: default:
// TODO(bradfitz): handle more settings? SETTINGS_HEADER_TABLE_SIZE probably.
cc.vlogf("Unhandled Setting: %v", s) cc.vlogf("Unhandled Setting: %v", s)
} }
return nil return nil
@ -2985,7 +3046,11 @@ func (gz *gzipReader) Read(p []byte) (n int, err error) {
} }
func (gz *gzipReader) Close() error { func (gz *gzipReader) Close() error {
return gz.body.Close() if err := gz.body.Close(); err != nil {
return err
}
gz.zerr = fs.ErrClosed
return nil
} }
type errorReader struct{ err error } type errorReader struct{ err error }

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gccgo && !aix //go:build gccgo && !aix && !hurd
// +build gccgo,!aix // +build gccgo,!aix,!hurd
package unix package unix

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build gccgo //go:build gccgo && !aix && !hurd
// +build !aix // +build gccgo,!aix,!hurd
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>

View File

@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris // +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
package unix package unix
import ( import (
"runtime"
"unsafe" "unsafe"
) )
@ -27,7 +26,7 @@ func IoctlSetInt(fd int, req uint, value int) error {
// passing the integer value directly. // passing the integer value directly.
func IoctlSetPointerInt(fd int, req uint, value int) error { func IoctlSetPointerInt(fd int, req uint, value int) error {
v := int32(value) v := int32(value)
return ioctl(fd, req, uintptr(unsafe.Pointer(&v))) return ioctlPtr(fd, req, unsafe.Pointer(&v))
} }
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. // IoctlSetWinsize performs an ioctl on fd with a *Winsize argument.
@ -36,9 +35,7 @@ func IoctlSetPointerInt(fd int, req uint, value int) error {
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// TODO: if we get the chance, remove the req parameter and // TODO: if we get the chance, remove the req parameter and
// hardcode TIOCSWINSZ. // hardcode TIOCSWINSZ.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, req, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
// IoctlSetTermios performs an ioctl on fd with a *Termios. // IoctlSetTermios performs an ioctl on fd with a *Termios.
@ -46,9 +43,7 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// The req value will usually be TCSETA or TIOCSETA. // The req value will usually be TCSETA or TIOCSETA.
func IoctlSetTermios(fd int, req uint, value *Termios) error { func IoctlSetTermios(fd int, req uint, value *Termios) error {
// TODO: if we get the chance, remove the req parameter. // TODO: if we get the chance, remove the req parameter.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, req, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
// IoctlGetInt performs an ioctl operation which gets an integer value // IoctlGetInt performs an ioctl operation which gets an integer value
@ -58,18 +53,18 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error {
// for those, IoctlRetInt should be used instead of this function. // for those, IoctlRetInt should be used instead of this function.
func IoctlGetInt(fd int, req uint) (int, error) { func IoctlGetInt(fd int, req uint) (int, error) {
var value int var value int
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return value, err return value, err
} }
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
var value Winsize var value Winsize
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err return &value, err
} }
func IoctlGetTermios(fd int, req uint) (*Termios, error) { func IoctlGetTermios(fd int, req uint) (*Termios, error) {
var value Termios var value Termios
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err return &value, err
} }

View File

@ -27,9 +27,7 @@ func IoctlSetInt(fd int, req uint, value int) error {
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
// TODO: if we get the chance, remove the req parameter and // TODO: if we get the chance, remove the req parameter and
// hardcode TIOCSWINSZ. // hardcode TIOCSWINSZ.
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, req, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
// IoctlSetTermios performs an ioctl on fd with a *Termios. // IoctlSetTermios performs an ioctl on fd with a *Termios.
@ -51,13 +49,13 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error {
// for those, IoctlRetInt should be used instead of this function. // for those, IoctlRetInt should be used instead of this function.
func IoctlGetInt(fd int, req uint) (int, error) { func IoctlGetInt(fd int, req uint) (int, error) {
var value int var value int
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return value, err return value, err
} }
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
var value Winsize var value Winsize
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err return &value, err
} }

View File

@ -174,10 +174,10 @@ openbsd_arm64)
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;; ;;
openbsd_mips64) openbsd_mips64)
mkasm="go run mkasm.go"
mkerrors="$mkerrors -m64" mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -openbsd" mksyscall="go run mksyscall.go -openbsd -libc"
mksysctl="go run mksysctl_openbsd.go" mksysctl="go run mksysctl_openbsd.go"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall # Let the type of C char be signed for making the bare syscall
# API consistent across platforms. # API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"

View File

@ -7,6 +7,12 @@
package unix package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) error { func ptrace(request int, pid int, addr uintptr, data uintptr) error {
return ptrace1(request, pid, addr, data) return ptrace1(request, pid, addr, data)
} }
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) error {
return ptrace1Ptr(request, pid, addr, data)
}

View File

@ -7,6 +7,12 @@
package unix package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
return ENOTSUP return ENOTSUP
} }
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) {
return ENOTSUP
}

View File

@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
return msgs, nil return msgs, nil
} }
// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header,
// message data (a slice of b), and the remainder of b after that single message.
// When there are no remaining messages, len(remainder) == 0.
func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) {
h, dbuf, err := socketControlMessageHeaderAndData(b)
if err != nil {
return Cmsghdr{}, nil, nil, err
}
if i := cmsgAlignOf(int(h.Len)); i < len(b) {
remainder = b[i:]
}
return *h, dbuf, remainder, nil
}
func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) { func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
h := (*Cmsghdr)(unsafe.Pointer(&b[0])) h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) { if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {

View File

@ -292,9 +292,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
break break
} }
} }
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
return sa, nil return sa, nil
case AF_INET: case AF_INET:
@ -411,6 +409,7 @@ func (w WaitStatus) CoreDump() bool { return w&0x80 == 0x80 }
func (w WaitStatus) TrapCause() int { return -1 } func (w WaitStatus) TrapCause() int { return -1 }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = ioctl
// fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX // fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX
// There is no way to create a custom fcntl and to keep //sys fcntl easily, // There is no way to create a custom fcntl and to keep //sys fcntl easily,

View File

@ -245,8 +245,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
break break
} }
} }
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
sa.Name = string(bytes)
return sa, nil return sa, nil
case AF_INET: case AF_INET:

View File

@ -14,7 +14,6 @@ package unix
import ( import (
"fmt" "fmt"
"runtime"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -230,6 +229,7 @@ func direntNamlen(buf []byte) (uint64, bool) {
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) } func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) } func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
func PtraceDenyAttach() (err error) { return ptrace(PT_DENY_ATTACH, 0, 0, 0) }
//sysnb pipe(p *[2]int32) (err error) //sysnb pipe(p *[2]int32) (err error)
@ -375,11 +375,10 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) {
func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) } func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error { func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error {
err := ioctl(fd, CTLIOCGINFO, uintptr(unsafe.Pointer(ctlInfo))) return ioctlPtr(fd, CTLIOCGINFO, unsafe.Pointer(ctlInfo))
runtime.KeepAlive(ctlInfo)
return err
} }
// IfreqMTU is struct ifreq used to get or set a network device's MTU. // IfreqMTU is struct ifreq used to get or set a network device's MTU.
@ -393,16 +392,14 @@ type IfreqMTU struct {
func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) { func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) {
var ifreq IfreqMTU var ifreq IfreqMTU
copy(ifreq.Name[:], ifname) copy(ifreq.Name[:], ifname)
err := ioctl(fd, SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) err := ioctlPtr(fd, SIOCGIFMTU, unsafe.Pointer(&ifreq))
return &ifreq, err return &ifreq, err
} }
// IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU // IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU
// of the network device specified by ifreq.Name. // of the network device specified by ifreq.Name.
func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error { func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error {
err := ioctl(fd, SIOCSIFMTU, uintptr(unsafe.Pointer(ifreq))) return ioctlPtr(fd, SIOCSIFMTU, unsafe.Pointer(ifreq))
runtime.KeepAlive(ifreq)
return err
} }
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL

View File

@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64 //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64 //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64

View File

@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error) //sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error)

View File

@ -172,6 +172,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
} }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
@ -255,6 +256,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Chmod(path string, mode uint32) (err error) //sys Chmod(path string, mode uint32) (err error)
//sys Chown(path string, uid int, gid int) (err error) //sys Chown(path string, uid int, gid int) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)

View File

@ -161,7 +161,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
return return
} }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
@ -253,6 +254,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
} }
//sys ptrace(request int, pid int, addr uintptr, data int) (err error) //sys ptrace(request int, pid int, addr uintptr, data int) (err error)
//sys ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) = SYS_PTRACE
func PtraceAttach(pid int) (err error) { func PtraceAttach(pid int) (err error) {
return ptrace(PT_ATTACH, pid, 0, 0) return ptrace(PT_ATTACH, pid, 0, 0)
@ -267,19 +269,36 @@ func PtraceDetach(pid int) (err error) {
} }
func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) { func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) {
return ptrace(PT_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0) return ptracePtr(PT_GETFPREGS, pid, unsafe.Pointer(fpregsout), 0)
} }
func PtraceGetRegs(pid int, regsout *Reg) (err error) { func PtraceGetRegs(pid int, regsout *Reg) (err error) {
return ptrace(PT_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0) return ptracePtr(PT_GETREGS, pid, unsafe.Pointer(regsout), 0)
}
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{
Op: int32(req),
Offs: offs,
}
if countin > 0 {
_ = out[:countin] // check bounds
ioDesc.Addr = &out[0]
} else if out != nil {
ioDesc.Addr = (*byte)(unsafe.Pointer(&_zero))
}
ioDesc.SetLen(countin)
err = ptracePtr(PT_IO, pid, unsafe.Pointer(&ioDesc), 0)
return int(ioDesc.Len), err
} }
func PtraceLwpEvents(pid int, enable int) (err error) { func PtraceLwpEvents(pid int, enable int) (err error) {
return ptrace(PT_LWP_EVENTS, pid, 0, enable) return ptrace(PT_LWP_EVENTS, pid, 0, enable)
} }
func PtraceLwpInfo(pid int, info uintptr) (err error) { func PtraceLwpInfo(pid int, info *PtraceLwpInfoStruct) (err error) {
return ptrace(PT_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{}))) return ptracePtr(PT_LWPINFO, pid, unsafe.Pointer(info), int(unsafe.Sizeof(*info)))
} }
func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) {
@ -299,13 +318,25 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) {
} }
func PtraceSetRegs(pid int, regs *Reg) (err error) { func PtraceSetRegs(pid int, regs *Reg) (err error) {
return ptrace(PT_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0) return ptracePtr(PT_SETREGS, pid, unsafe.Pointer(regs), 0)
} }
func PtraceSingleStep(pid int) (err error) { func PtraceSingleStep(pid int) (err error) {
return ptrace(PT_STEP, pid, 1, 0) return ptrace(PT_STEP, pid, 1, 0)
} }
func Dup3(oldfd, newfd, flags int) error {
if oldfd == newfd || flags&^O_CLOEXEC != 0 {
return EINVAL
}
how := F_DUP2FD
if flags&O_CLOEXEC != 0 {
how = F_DUP2FD_CLOEXEC
}
_, err := fcntl(oldfd, how, newfd)
return err
}
/* /*
* Exposed directly * Exposed directly
*/ */
@ -319,6 +350,7 @@ func PtraceSingleStep(pid int) (err error) {
//sys Chmod(path string, mode uint32) (err error) //sys Chmod(path string, mode uint32) (err error)
//sys Chown(path string, uid int, gid int) (err error) //sys Chown(path string, uid int, gid int) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint32(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0 var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
@ -57,11 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceGetFsBase(pid int, fsbase *int64) (err error) { func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0)
}
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
} }

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0 var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -57,11 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceGetFsBase(pid int, fsbase *int64) (err error) { func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0)
}
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
} }

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint32(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0 var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
@ -55,9 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
} }
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0 var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -55,9 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
} }
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
func (d *PtraceIoDesc) SetLen(length int) {
d.Len = uint64(length)
}
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var writtenOut uint64 = 0 var writtenOut uint64 = 0
_, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
@ -55,9 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
} }
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

30
vendor/golang.org/x/sys/unix/syscall_hurd.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2022 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 hurd
// +build hurd
package unix
/*
#include <stdint.h>
int ioctl(int, unsigned long int, uintptr_t);
*/
import "C"
func ioctl(fd int, req uint, arg uintptr) (err error) {
r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(arg))
if r0 == -1 && er != nil {
err = er
}
return
}
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(uintptr(arg)))
if r0 == -1 && er != nil {
err = er
}
return
}

29
vendor/golang.org/x/sys/unix/syscall_hurd_386.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2022 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 386 && hurd
// +build 386,hurd
package unix
const (
TIOCGETA = 0x62251713
)
type Winsize struct {
Row uint16
Col uint16
Xpixel uint16
Ypixel uint16
}
type Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed int32
Ospeed int32
}

View File

@ -1015,8 +1015,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 { for n < len(pp.Path) && pp.Path[n] != 0 {
n++ n++
} }
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
sa.Name = string(bytes)
return sa, nil return sa, nil
case AF_INET: case AF_INET:
@ -1365,6 +1364,10 @@ func SetsockoptTCPRepairOpt(fd, level, opt int, o []TCPRepairOpt) (err error) {
return setsockopt(fd, level, opt, unsafe.Pointer(&o[0]), uintptr(SizeofTCPRepairOpt*len(o))) return setsockopt(fd, level, opt, unsafe.Pointer(&o[0]), uintptr(SizeofTCPRepairOpt*len(o)))
} }
func SetsockoptTCPMD5Sig(fd, level, opt int, s *TCPMD5Sig) error {
return setsockopt(fd, level, opt, unsafe.Pointer(s), unsafe.Sizeof(*s))
}
// Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html) // Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html)
// KeyctlInt calls keyctl commands in which each argument is an int. // KeyctlInt calls keyctl commands in which each argument is an int.
@ -1554,6 +1557,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
var iova [1]Iovec var iova [1]Iovec
iova[0].Base = &dummy iova[0].Base = &dummy
iova[0].SetLen(1) iova[0].SetLen(1)
iov = iova[:]
} }
} }
msg.Control = &oob[0] msg.Control = &oob[0]
@ -1578,6 +1582,7 @@ func BindToDevice(fd int, device string) (err error) {
} }
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) //sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
//sys ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) = SYS_PTRACE
func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) { func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) {
// The peek requests are machine-size oriented, so we wrap it // The peek requests are machine-size oriented, so we wrap it
@ -1595,7 +1600,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
// boundary. // boundary.
n := 0 n := 0
if addr%SizeofPtr != 0 { if addr%SizeofPtr != 0 {
err = ptrace(req, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) err = ptracePtr(req, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0]))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -1607,7 +1612,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
for len(out) > 0 { for len(out) > 0 {
// We use an internal buffer to guarantee alignment. // We use an internal buffer to guarantee alignment.
// It's not documented if this is necessary, but we're paranoid. // It's not documented if this is necessary, but we're paranoid.
err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) err = ptracePtr(req, pid, addr+uintptr(n), unsafe.Pointer(&buf[0]))
if err != nil { if err != nil {
return n, err return n, err
} }
@ -1639,7 +1644,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
n := 0 n := 0
if addr%SizeofPtr != 0 { if addr%SizeofPtr != 0 {
var buf [SizeofPtr]byte var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) err = ptracePtr(peekReq, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0]))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -1666,7 +1671,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
// Trailing edge. // Trailing edge.
if len(data) > 0 { if len(data) > 0 {
var buf [SizeofPtr]byte var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) err = ptracePtr(peekReq, pid, addr+uintptr(n), unsafe.Pointer(&buf[0]))
if err != nil { if err != nil {
return n, err return n, err
} }
@ -1695,11 +1700,11 @@ func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) {
} }
func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))
} }
func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))
} }
func PtraceSetOptions(pid int, options int) (err error) { func PtraceSetOptions(pid int, options int) (err error) {
@ -1708,7 +1713,7 @@ func PtraceSetOptions(pid int, options int) (err error) {
func PtraceGetEventMsg(pid int) (msg uint, err error) { func PtraceGetEventMsg(pid int) (msg uint, err error) {
var data _C_long var data _C_long
err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) err = ptracePtr(PTRACE_GETEVENTMSG, pid, 0, unsafe.Pointer(&data))
msg = uint(data) msg = uint(data)
return return
} }
@ -1799,6 +1804,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sysnb Capset(hdr *CapUserHeader, data *CapUserData) (err error) //sysnb Capset(hdr *CapUserHeader, data *CapUserData) (err error)
//sys Chdir(path string) (err error) //sys Chdir(path string) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockAdjtime(clockid int32, buf *Timex) (state int, err error)
//sys ClockGetres(clockid int32, res *Timespec) (err error) //sys ClockGetres(clockid int32, res *Timespec) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error) //sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error) //sys ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error)
@ -1972,36 +1978,46 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
//sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2 //sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
//sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2 //sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2
func bytes2iovec(bs [][]byte) []Iovec { // minIovec is the size of the small initial allocation used by
iovecs := make([]Iovec, len(bs)) // Readv, Writev, etc.
for i, b := range bs { //
iovecs[i].SetLen(len(b)) // This small allocation gets stack allocated, which lets the
// common use case of len(iovs) <= minIovs avoid more expensive
// heap allocations.
const minIovec = 8
// appendBytes converts bs to Iovecs and appends them to vecs.
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
for _, b := range bs {
var v Iovec
v.SetLen(len(b))
if len(b) > 0 { if len(b) > 0 {
iovecs[i].Base = &b[0] v.Base = &b[0]
} else { } else {
iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero)) v.Base = (*byte)(unsafe.Pointer(&_zero))
} }
vecs = append(vecs, v)
} }
return iovecs return vecs
} }
// offs2lohi splits offs into its lower and upper unsigned long. On 64-bit // offs2lohi splits offs into its low and high order bits.
// systems, hi will always be 0. On 32-bit systems, offs will be split in half.
// preadv/pwritev chose this calling convention so they don't need to add a
// padding-register for alignment on ARM.
func offs2lohi(offs int64) (lo, hi uintptr) { func offs2lohi(offs int64) (lo, hi uintptr) {
return uintptr(offs), uintptr(uint64(offs) >> SizeofLong) const longBits = SizeofLong * 8
return uintptr(offs), uintptr(uint64(offs) >> (longBits - 1) >> 1) // two shifts to avoid false positive in vet
} }
func Readv(fd int, iovs [][]byte) (n int, err error) { func Readv(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
n, err = readv(fd, iovecs) n, err = readv(fd, iovecs)
readvRacedetect(iovecs, n, err) readvRacedetect(iovecs, n, err)
return n, err return n, err
} }
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) { func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
lo, hi := offs2lohi(offset) lo, hi := offs2lohi(offset)
n, err = preadv(fd, iovecs, lo, hi) n, err = preadv(fd, iovecs, lo, hi)
readvRacedetect(iovecs, n, err) readvRacedetect(iovecs, n, err)
@ -2009,7 +2025,8 @@ func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
} }
func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) { func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
lo, hi := offs2lohi(offset) lo, hi := offs2lohi(offset)
n, err = preadv2(fd, iovecs, lo, hi, flags) n, err = preadv2(fd, iovecs, lo, hi, flags)
readvRacedetect(iovecs, n, err) readvRacedetect(iovecs, n, err)
@ -2036,7 +2053,8 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
} }
func Writev(fd int, iovs [][]byte) (n int, err error) { func Writev(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled { if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync)) raceReleaseMerge(unsafe.Pointer(&ioSync))
} }
@ -2046,7 +2064,8 @@ func Writev(fd int, iovs [][]byte) (n int, err error) {
} }
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) { func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled { if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync)) raceReleaseMerge(unsafe.Pointer(&ioSync))
} }
@ -2057,7 +2076,8 @@ func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
} }
func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) { func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs) iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled { if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync)) raceReleaseMerge(unsafe.Pointer(&ioSync))
} }
@ -2138,6 +2158,14 @@ func isGroupMember(gid int) bool {
return false return false
} }
func isCapDacOverrideSet() bool {
hdr := CapUserHeader{Version: LINUX_CAPABILITY_VERSION_3}
data := [2]CapUserData{}
err := Capget(&hdr, &data[0])
return err == nil && data[0].Effective&(1<<CAP_DAC_OVERRIDE) != 0
}
//sys faccessat(dirfd int, path string, mode uint32) (err error) //sys faccessat(dirfd int, path string, mode uint32) (err error)
//sys Faccessat2(dirfd int, path string, mode uint32, flags int) (err error) //sys Faccessat2(dirfd int, path string, mode uint32, flags int) (err error)
@ -2173,6 +2201,12 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
var uid int var uid int
if flags&AT_EACCESS != 0 { if flags&AT_EACCESS != 0 {
uid = Geteuid() uid = Geteuid()
if uid != 0 && isCapDacOverrideSet() {
// If CAP_DAC_OVERRIDE is set, file access check is
// done by the kernel in the same way as for root
// (see generic_permission() in the Linux sources).
uid = 0
}
} else { } else {
uid = Getuid() uid = Getuid()
} }

View File

@ -13,7 +13,6 @@
package unix package unix
import ( import (
"runtime"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -110,6 +109,20 @@ func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen)) return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
} }
func SysctlUvmexp(name string) (*Uvmexp, error) {
mib, err := sysctlmib(name)
if err != nil {
return nil, err
}
n := uintptr(SizeofUvmexp)
var u Uvmexp
if err := sysctl(mib, (*byte)(unsafe.Pointer(&u)), &n, nil, 0); err != nil {
return nil, err
}
return &u, nil
}
func Pipe(p []int) (err error) { func Pipe(p []int) (err error) {
return Pipe2(p, 0) return Pipe2(p, 0)
} }
@ -164,13 +177,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
} }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
func IoctlGetPtmget(fd int, req uint) (*Ptmget, error) { func IoctlGetPtmget(fd int, req uint) (*Ptmget, error) {
var value Ptmget var value Ptmget
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
runtime.KeepAlive(value)
return &value, err return &value, err
} }
@ -245,6 +258,7 @@ func Statvfs(path string, buf *Statvfs_t) (err error) {
//sys Chmod(path string, mode uint32) (err error) //sys Chmod(path string, mode uint32) (err error)
//sys Chown(path string, uid int, gid int) (err error) //sys Chown(path string, uid int, gid int) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)

View File

@ -152,6 +152,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
} }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error)
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
@ -220,6 +221,7 @@ func Uname(uname *Utsname) error {
//sys Chmod(path string, mode uint32) (err error) //sys Chmod(path string, mode uint32) (err error)
//sys Chown(path string, uid int, gid int) (err error) //sys Chown(path string, uid int, gid int) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build openbsd && !mips64 //go:build openbsd
// +build openbsd,!mips64 // +build openbsd
package unix package unix

View File

@ -408,8 +408,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 { for n < len(pp.Path) && pp.Path[n] != 0 {
n++ n++
} }
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
sa.Name = string(bytes)
return sa, nil return sa, nil
case AF_INET: case AF_INET:
@ -547,21 +546,25 @@ func Minor(dev uint64) uint32 {
*/ */
//sys ioctlRet(fd int, req uint, arg uintptr) (ret int, err error) = libc.ioctl //sys ioctlRet(fd int, req uint, arg uintptr) (ret int, err error) = libc.ioctl
//sys ioctlPtrRet(fd int, req uint, arg unsafe.Pointer) (ret int, err error) = libc.ioctl
func ioctl(fd int, req uint, arg uintptr) (err error) { func ioctl(fd int, req uint, arg uintptr) (err error) {
_, err = ioctlRet(fd, req, arg) _, err = ioctlRet(fd, req, arg)
return err return err
} }
func IoctlSetTermio(fd int, req uint, value *Termio) error { func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) _, err = ioctlPtrRet(fd, req, arg)
runtime.KeepAlive(value)
return err return err
} }
func IoctlSetTermio(fd int, req uint, value *Termio) error {
return ioctlPtr(fd, req, unsafe.Pointer(value))
}
func IoctlGetTermio(fd int, req uint) (*Termio, error) { func IoctlGetTermio(fd int, req uint) (*Termio, error) {
var value Termio var value Termio
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return &value, err return &value, err
} }
@ -590,6 +593,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Chmod(path string, mode uint32) (err error) //sys Chmod(path string, mode uint32) (err error)
//sys Chown(path string, uid int, gid int) (err error) //sys Chown(path string, uid int, gid int) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Creat(path string, mode uint32) (fd int, err error) //sys Creat(path string, mode uint32) (fd int, err error)
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
@ -1083,7 +1087,7 @@ func IoctlSetIntRetInt(fd int, req uint, arg int) (int, error) {
func IoctlSetString(fd int, req uint, val string) error { func IoctlSetString(fd int, req uint, val string) error {
bs := make([]byte, len(val)+1) bs := make([]byte, len(val)+1)
copy(bs[:len(bs)-1], val) copy(bs[:len(bs)-1], val)
err := ioctl(fd, req, uintptr(unsafe.Pointer(&bs[0]))) err := ioctlPtr(fd, req, unsafe.Pointer(&bs[0]))
runtime.KeepAlive(&bs[0]) runtime.KeepAlive(&bs[0])
return err return err
} }
@ -1117,7 +1121,7 @@ func (l *Lifreq) GetLifruUint() uint {
} }
func IoctlLifreq(fd int, req uint, l *Lifreq) error { func IoctlLifreq(fd int, req uint, l *Lifreq) error {
return ioctl(fd, req, uintptr(unsafe.Pointer(l))) return ioctlPtr(fd, req, unsafe.Pointer(l))
} }
// Strioctl Helpers // Strioctl Helpers
@ -1128,5 +1132,5 @@ func (s *Strioctl) SetInt(i int) {
} }
func IoctlSetStrioctlRetInt(fd int, req uint, s *Strioctl) (int, error) { func IoctlSetStrioctlRetInt(fd int, req uint, s *Strioctl) (int, error) {
return ioctlRet(fd, req, uintptr(unsafe.Pointer(s))) return ioctlPtrRet(fd, req, unsafe.Pointer(s))
} }

View File

@ -331,6 +331,19 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
return return
} }
// Recvmsg receives a message from a socket using the recvmsg system call. The
// received non-control data will be written to p, and any "out of band"
// control data will be written to oob. The flags are passed to recvmsg.
//
// The results are:
// - n is the number of non-control data bytes read into p
// - oobn is the number of control data bytes read into oob; this may be interpreted using [ParseSocketControlMessage]
// - recvflags is flags returned by recvmsg
// - from is the address of the sender
//
// If the underlying socket type is not SOCK_DGRAM, a received message
// containing oob data and a single '\0' of non-control data is treated as if
// the message contained only control data, i.e. n will be zero on return.
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
var iov [1]Iovec var iov [1]Iovec
if len(p) > 0 { if len(p) > 0 {
@ -346,13 +359,9 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
return return
} }
// RecvmsgBuffers receives a message from a socket using the recvmsg // RecvmsgBuffers receives a message from a socket using the recvmsg system
// system call. The flags are passed to recvmsg. Any non-control data // call. This function is equivalent to Recvmsg, but non-control data read is
// read is scattered into the buffers slices. The results are: // scattered into the buffers slices.
// - n is the number of non-control data read into bufs
// - oobn is the number of control data read into oob; this may be interpreted using [ParseSocketControlMessage]
// - recvflags is flags returned by recvmsg
// - from is the address of the sender
func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
iov := make([]Iovec, len(buffers)) iov := make([]Iovec, len(buffers))
for i := range buffers { for i := range buffers {
@ -371,11 +380,38 @@ func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn in
return return
} }
// Sendmsg sends a message on a socket to an address using the sendmsg system
// call. This function is equivalent to SendmsgN, but does not return the
// number of bytes actually sent.
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
_, err = SendmsgN(fd, p, oob, to, flags) _, err = SendmsgN(fd, p, oob, to, flags)
return return
} }
// SendmsgN sends a message on a socket to an address using the sendmsg system
// call. p contains the non-control data to send, and oob contains the "out of
// band" control data. The flags are passed to sendmsg. The number of
// non-control bytes actually written to the socket is returned.
//
// Some socket types do not support sending control data without accompanying
// non-control data. If p is empty, and oob contains control data, and the
// underlying socket type is not SOCK_DGRAM, p will be treated as containing a
// single '\0' and the return value will indicate zero bytes sent.
//
// The Go function Recvmsg, if called with an empty p and a non-empty oob,
// will read and ignore this additional '\0'. If the message is received by
// code that does not use Recvmsg, or that does not use Go at all, that code
// will need to be written to expect and ignore the additional '\0'.
//
// If you need to send non-empty oob with p actually empty, and if the
// underlying socket type supports it, you can do so via a raw system call as
// follows:
//
// msg := &unix.Msghdr{
// Control: &oob[0],
// }
// msg.SetControllen(len(oob))
// n, _, errno := unix.Syscall(unix.SYS_SENDMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), flags)
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
var iov [1]Iovec var iov [1]Iovec
if len(p) > 0 { if len(p) > 0 {
@ -394,9 +430,8 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
} }
// SendmsgBuffers sends a message on a socket to an address using the sendmsg // SendmsgBuffers sends a message on a socket to an address using the sendmsg
// system call. The flags are passed to sendmsg. Any non-control data written // system call. This function is equivalent to SendmsgN, but the non-control
// is gathered from buffers. The function returns the number of bytes written // data is gathered from buffers.
// to the socket.
func SendmsgBuffers(fd int, buffers [][]byte, oob []byte, to Sockaddr, flags int) (n int, err error) { func SendmsgBuffers(fd int, buffers [][]byte, oob []byte, to Sockaddr, flags int) (n int, err error) {
iov := make([]Iovec, len(buffers)) iov := make([]Iovec, len(buffers))
for i := range buffers { for i := range buffers {
@ -543,7 +578,7 @@ func Lutimes(path string, tv []Timeval) error {
return UtimesNanoAt(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW) return UtimesNanoAt(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW)
} }
// emptyIovec reports whether there are no bytes in the slice of Iovec. // emptyIovecs reports whether there are no bytes in the slice of Iovec.
func emptyIovecs(iov []Iovec) bool { func emptyIovecs(iov []Iovec) bool {
for i := range iov { for i := range iov {
if iov[i].Len > 0 { if iov[i].Len > 0 {

View File

@ -139,8 +139,7 @@ func anyToSockaddr(_ int, rsa *RawSockaddrAny) (Sockaddr, error) {
for n < int(pp.Len) && pp.Path[n] != 0 { for n < int(pp.Len) && pp.Path[n] != 0 {
n++ n++
} }
bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
sa.Name = string(bytes)
return sa, nil return sa, nil
case AF_INET: case AF_INET:
@ -214,6 +213,7 @@ func (cmsg *Cmsghdr) SetLen(length int) {
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) = SYS_MMAP //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) = SYS_MMAP
//sys munmap(addr uintptr, length uintptr) (err error) = SYS_MUNMAP //sys munmap(addr uintptr, length uintptr) (err error) = SYS_MUNMAP
//sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL //sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
//sys Access(path string, mode uint32) (err error) = SYS___ACCESS_A //sys Access(path string, mode uint32) (err error) = SYS___ACCESS_A
//sys Chdir(path string) (err error) = SYS___CHDIR_A //sys Chdir(path string) (err error) = SYS___CHDIR_A

View File

@ -9,7 +9,7 @@ package unix
import "time" import "time"
// TimespecToNSec returns the time stored in ts as nanoseconds. // TimespecToNsec returns the time stored in ts as nanoseconds.
func TimespecToNsec(ts Timespec) int64 { return ts.Nano() } func TimespecToNsec(ts Timespec) int64 { return ts.Nano() }
// NsecToTimespec converts a number of nanoseconds into a Timespec. // NsecToTimespec converts a number of nanoseconds into a Timespec.

View File

@ -36,9 +36,14 @@ func xattrnamespace(fullattr string) (ns int, attr string, err error) {
func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) { func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) {
if len(dest) > idx { if len(dest) > idx {
return unsafe.Pointer(&dest[idx]) return unsafe.Pointer(&dest[idx])
} else {
return unsafe.Pointer(_zero)
} }
if dest != nil {
// extattr_get_file and extattr_list_file treat NULL differently from
// a non-NULL pointer of length zero. Preserve the property of nilness,
// even if we can't use dest directly.
return unsafe.Pointer(&_zero)
}
return nil
} }
// FreeBSD and NetBSD implement their own syscalls to handle extended attributes // FreeBSD and NetBSD implement their own syscalls to handle extended attributes

View File

@ -70,6 +70,7 @@ const (
ALG_SET_DRBG_ENTROPY = 0x6 ALG_SET_DRBG_ENTROPY = 0x6
ALG_SET_IV = 0x2 ALG_SET_IV = 0x2
ALG_SET_KEY = 0x1 ALG_SET_KEY = 0x1
ALG_SET_KEY_BY_KEY_SERIAL = 0x7
ALG_SET_OP = 0x3 ALG_SET_OP = 0x3
ANON_INODE_FS_MAGIC = 0x9041934 ANON_INODE_FS_MAGIC = 0x9041934
ARPHRD_6LOWPAN = 0x339 ARPHRD_6LOWPAN = 0x339
@ -457,7 +458,6 @@ const (
B600 = 0x8 B600 = 0x8
B75 = 0x2 B75 = 0x2
B9600 = 0xd B9600 = 0xd
BALLOON_KVM_MAGIC = 0x13661366
BDEVFS_MAGIC = 0x62646576 BDEVFS_MAGIC = 0x62646576
BINDERFS_SUPER_MAGIC = 0x6c6f6f70 BINDERFS_SUPER_MAGIC = 0x6c6f6f70
BINFMTFS_MAGIC = 0x42494e4d BINFMTFS_MAGIC = 0x42494e4d
@ -563,6 +563,7 @@ const (
BUS_USB = 0x3 BUS_USB = 0x3
BUS_VIRTUAL = 0x6 BUS_VIRTUAL = 0x6
CAN_BCM = 0x2 CAN_BCM = 0x2
CAN_BUS_OFF_THRESHOLD = 0x100
CAN_CTRLMODE_3_SAMPLES = 0x4 CAN_CTRLMODE_3_SAMPLES = 0x4
CAN_CTRLMODE_BERR_REPORTING = 0x10 CAN_CTRLMODE_BERR_REPORTING = 0x10
CAN_CTRLMODE_CC_LEN8_DLC = 0x100 CAN_CTRLMODE_CC_LEN8_DLC = 0x100
@ -577,9 +578,12 @@ const (
CAN_EFF_FLAG = 0x80000000 CAN_EFF_FLAG = 0x80000000
CAN_EFF_ID_BITS = 0x1d CAN_EFF_ID_BITS = 0x1d
CAN_EFF_MASK = 0x1fffffff CAN_EFF_MASK = 0x1fffffff
CAN_ERROR_PASSIVE_THRESHOLD = 0x80
CAN_ERROR_WARNING_THRESHOLD = 0x60
CAN_ERR_ACK = 0x20 CAN_ERR_ACK = 0x20
CAN_ERR_BUSERROR = 0x80 CAN_ERR_BUSERROR = 0x80
CAN_ERR_BUSOFF = 0x40 CAN_ERR_BUSOFF = 0x40
CAN_ERR_CNT = 0x200
CAN_ERR_CRTL = 0x4 CAN_ERR_CRTL = 0x4
CAN_ERR_CRTL_ACTIVE = 0x40 CAN_ERR_CRTL_ACTIVE = 0x40
CAN_ERR_CRTL_RX_OVERFLOW = 0x1 CAN_ERR_CRTL_RX_OVERFLOW = 0x1
@ -771,6 +775,8 @@ const (
DEVLINK_GENL_MCGRP_CONFIG_NAME = "config" DEVLINK_GENL_MCGRP_CONFIG_NAME = "config"
DEVLINK_GENL_NAME = "devlink" DEVLINK_GENL_NAME = "devlink"
DEVLINK_GENL_VERSION = 0x1 DEVLINK_GENL_VERSION = 0x1
DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2
DEVLINK_PORT_FN_CAP_ROCE = 0x1
DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14 DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14
DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS = 0x3 DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS = 0x3
DEVMEM_MAGIC = 0x454d444d DEVMEM_MAGIC = 0x454d444d
@ -820,9 +826,9 @@ const (
DM_UUID_FLAG = 0x4000 DM_UUID_FLAG = 0x4000
DM_UUID_LEN = 0x81 DM_UUID_LEN = 0x81
DM_VERSION = 0xc138fd00 DM_VERSION = 0xc138fd00
DM_VERSION_EXTRA = "-ioctl (2022-02-22)" DM_VERSION_EXTRA = "-ioctl (2022-07-28)"
DM_VERSION_MAJOR = 0x4 DM_VERSION_MAJOR = 0x4
DM_VERSION_MINOR = 0x2e DM_VERSION_MINOR = 0x2f
DM_VERSION_PATCHLEVEL = 0x0 DM_VERSION_PATCHLEVEL = 0x0
DT_BLK = 0x6 DT_BLK = 0x6
DT_CHR = 0x2 DT_CHR = 0x2
@ -1049,6 +1055,7 @@ const (
ETH_P_CAIF = 0xf7 ETH_P_CAIF = 0xf7
ETH_P_CAN = 0xc ETH_P_CAN = 0xc
ETH_P_CANFD = 0xd ETH_P_CANFD = 0xd
ETH_P_CANXL = 0xe
ETH_P_CFM = 0x8902 ETH_P_CFM = 0x8902
ETH_P_CONTROL = 0x16 ETH_P_CONTROL = 0x16
ETH_P_CUST = 0x6006 ETH_P_CUST = 0x6006
@ -1060,6 +1067,7 @@ const (
ETH_P_DNA_RT = 0x6003 ETH_P_DNA_RT = 0x6003
ETH_P_DSA = 0x1b ETH_P_DSA = 0x1b
ETH_P_DSA_8021Q = 0xdadb ETH_P_DSA_8021Q = 0xdadb
ETH_P_DSA_A5PSW = 0xe001
ETH_P_ECONET = 0x18 ETH_P_ECONET = 0x18
ETH_P_EDSA = 0xdada ETH_P_EDSA = 0xdada
ETH_P_ERSPAN = 0x88be ETH_P_ERSPAN = 0x88be
@ -1194,8 +1202,10 @@ const (
FAN_MARK_EVICTABLE = 0x200 FAN_MARK_EVICTABLE = 0x200
FAN_MARK_FILESYSTEM = 0x100 FAN_MARK_FILESYSTEM = 0x100
FAN_MARK_FLUSH = 0x80 FAN_MARK_FLUSH = 0x80
FAN_MARK_IGNORE = 0x400
FAN_MARK_IGNORED_MASK = 0x20 FAN_MARK_IGNORED_MASK = 0x20
FAN_MARK_IGNORED_SURV_MODIFY = 0x40 FAN_MARK_IGNORED_SURV_MODIFY = 0x40
FAN_MARK_IGNORE_SURV = 0x440
FAN_MARK_INODE = 0x0 FAN_MARK_INODE = 0x0
FAN_MARK_MOUNT = 0x10 FAN_MARK_MOUNT = 0x10
FAN_MARK_ONLYDIR = 0x8 FAN_MARK_ONLYDIR = 0x8
@ -1253,7 +1263,10 @@ const (
FSCRYPT_MODE_AES_128_CBC = 0x5 FSCRYPT_MODE_AES_128_CBC = 0x5
FSCRYPT_MODE_AES_128_CTS = 0x6 FSCRYPT_MODE_AES_128_CTS = 0x6
FSCRYPT_MODE_AES_256_CTS = 0x4 FSCRYPT_MODE_AES_256_CTS = 0x4
FSCRYPT_MODE_AES_256_HCTR2 = 0xa
FSCRYPT_MODE_AES_256_XTS = 0x1 FSCRYPT_MODE_AES_256_XTS = 0x1
FSCRYPT_MODE_SM4_CTS = 0x8
FSCRYPT_MODE_SM4_XTS = 0x7
FSCRYPT_POLICY_FLAGS_PAD_16 = 0x2 FSCRYPT_POLICY_FLAGS_PAD_16 = 0x2
FSCRYPT_POLICY_FLAGS_PAD_32 = 0x3 FSCRYPT_POLICY_FLAGS_PAD_32 = 0x3
FSCRYPT_POLICY_FLAGS_PAD_4 = 0x0 FSCRYPT_POLICY_FLAGS_PAD_4 = 0x0
@ -1272,8 +1285,6 @@ const (
FS_ENCRYPTION_MODE_AES_256_GCM = 0x2 FS_ENCRYPTION_MODE_AES_256_GCM = 0x2
FS_ENCRYPTION_MODE_AES_256_XTS = 0x1 FS_ENCRYPTION_MODE_AES_256_XTS = 0x1
FS_ENCRYPTION_MODE_INVALID = 0x0 FS_ENCRYPTION_MODE_INVALID = 0x0
FS_ENCRYPTION_MODE_SPECK128_256_CTS = 0x8
FS_ENCRYPTION_MODE_SPECK128_256_XTS = 0x7
FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617 FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617
FS_IOC_GET_ENCRYPTION_KEY_STATUS = 0xc080661a FS_IOC_GET_ENCRYPTION_KEY_STATUS = 0xc080661a
FS_IOC_GET_ENCRYPTION_POLICY_EX = 0xc0096616 FS_IOC_GET_ENCRYPTION_POLICY_EX = 0xc0096616
@ -1430,6 +1441,7 @@ const (
IFF_NOARP = 0x80 IFF_NOARP = 0x80
IFF_NOFILTER = 0x1000 IFF_NOFILTER = 0x1000
IFF_NOTRAILERS = 0x20 IFF_NOTRAILERS = 0x20
IFF_NO_CARRIER = 0x40
IFF_NO_PI = 0x1000 IFF_NO_PI = 0x1000
IFF_ONE_QUEUE = 0x2000 IFF_ONE_QUEUE = 0x2000
IFF_PERSIST = 0x800 IFF_PERSIST = 0x800
@ -1761,6 +1773,7 @@ const (
LANDLOCK_ACCESS_FS_REFER = 0x2000 LANDLOCK_ACCESS_FS_REFER = 0x2000
LANDLOCK_ACCESS_FS_REMOVE_DIR = 0x10 LANDLOCK_ACCESS_FS_REMOVE_DIR = 0x10
LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20 LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20
LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000
LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2 LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2
LANDLOCK_CREATE_RULESET_VERSION = 0x1 LANDLOCK_CREATE_RULESET_VERSION = 0x1
LINUX_REBOOT_CMD_CAD_OFF = 0x0 LINUX_REBOOT_CMD_CAD_OFF = 0x0
@ -1800,11 +1813,13 @@ const (
LWTUNNEL_IP_OPT_GENEVE_MAX = 0x3 LWTUNNEL_IP_OPT_GENEVE_MAX = 0x3
LWTUNNEL_IP_OPT_VXLAN_MAX = 0x1 LWTUNNEL_IP_OPT_VXLAN_MAX = 0x1
MADV_COLD = 0x14 MADV_COLD = 0x14
MADV_COLLAPSE = 0x19
MADV_DODUMP = 0x11 MADV_DODUMP = 0x11
MADV_DOFORK = 0xb MADV_DOFORK = 0xb
MADV_DONTDUMP = 0x10 MADV_DONTDUMP = 0x10
MADV_DONTFORK = 0xa MADV_DONTFORK = 0xa
MADV_DONTNEED = 0x4 MADV_DONTNEED = 0x4
MADV_DONTNEED_LOCKED = 0x18
MADV_FREE = 0x8 MADV_FREE = 0x8
MADV_HUGEPAGE = 0xe MADV_HUGEPAGE = 0xe
MADV_HWPOISON = 0x64 MADV_HWPOISON = 0x64
@ -1846,7 +1861,7 @@ const (
MFD_ALLOW_SEALING = 0x2 MFD_ALLOW_SEALING = 0x2
MFD_CLOEXEC = 0x1 MFD_CLOEXEC = 0x1
MFD_HUGETLB = 0x4 MFD_HUGETLB = 0x4
MFD_HUGE_16GB = -0x78000000 MFD_HUGE_16GB = 0x88000000
MFD_HUGE_16MB = 0x60000000 MFD_HUGE_16MB = 0x60000000
MFD_HUGE_1GB = 0x78000000 MFD_HUGE_1GB = 0x78000000
MFD_HUGE_1MB = 0x50000000 MFD_HUGE_1MB = 0x50000000
@ -2153,6 +2168,7 @@ const (
PACKET_FANOUT_DATA = 0x16 PACKET_FANOUT_DATA = 0x16
PACKET_FANOUT_EBPF = 0x7 PACKET_FANOUT_EBPF = 0x7
PACKET_FANOUT_FLAG_DEFRAG = 0x8000 PACKET_FANOUT_FLAG_DEFRAG = 0x8000
PACKET_FANOUT_FLAG_IGNORE_OUTGOING = 0x4000
PACKET_FANOUT_FLAG_ROLLOVER = 0x1000 PACKET_FANOUT_FLAG_ROLLOVER = 0x1000
PACKET_FANOUT_FLAG_UNIQUEID = 0x2000 PACKET_FANOUT_FLAG_UNIQUEID = 0x2000
PACKET_FANOUT_HASH = 0x0 PACKET_FANOUT_HASH = 0x0
@ -2212,6 +2228,11 @@ const (
PERF_AUX_FLAG_PARTIAL = 0x4 PERF_AUX_FLAG_PARTIAL = 0x4
PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK = 0xff00 PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK = 0xff00
PERF_AUX_FLAG_TRUNCATED = 0x1 PERF_AUX_FLAG_TRUNCATED = 0x1
PERF_BR_ARM64_DEBUG_DATA = 0x7
PERF_BR_ARM64_DEBUG_EXIT = 0x5
PERF_BR_ARM64_DEBUG_HALT = 0x4
PERF_BR_ARM64_DEBUG_INST = 0x6
PERF_BR_ARM64_FIQ = 0x3
PERF_FLAG_FD_CLOEXEC = 0x8 PERF_FLAG_FD_CLOEXEC = 0x8
PERF_FLAG_FD_NO_GROUP = 0x1 PERF_FLAG_FD_NO_GROUP = 0x1
PERF_FLAG_FD_OUTPUT = 0x2 PERF_FLAG_FD_OUTPUT = 0x2
@ -2232,6 +2253,8 @@ const (
PERF_MEM_LOCK_NA = 0x1 PERF_MEM_LOCK_NA = 0x1
PERF_MEM_LOCK_SHIFT = 0x18 PERF_MEM_LOCK_SHIFT = 0x18
PERF_MEM_LVLNUM_ANY_CACHE = 0xb PERF_MEM_LVLNUM_ANY_CACHE = 0xb
PERF_MEM_LVLNUM_CXL = 0x9
PERF_MEM_LVLNUM_IO = 0xa
PERF_MEM_LVLNUM_L1 = 0x1 PERF_MEM_LVLNUM_L1 = 0x1
PERF_MEM_LVLNUM_L2 = 0x2 PERF_MEM_LVLNUM_L2 = 0x2
PERF_MEM_LVLNUM_L3 = 0x3 PERF_MEM_LVLNUM_L3 = 0x3
@ -2265,6 +2288,7 @@ const (
PERF_MEM_REMOTE_REMOTE = 0x1 PERF_MEM_REMOTE_REMOTE = 0x1
PERF_MEM_REMOTE_SHIFT = 0x25 PERF_MEM_REMOTE_SHIFT = 0x25
PERF_MEM_SNOOPX_FWD = 0x1 PERF_MEM_SNOOPX_FWD = 0x1
PERF_MEM_SNOOPX_PEER = 0x2
PERF_MEM_SNOOPX_SHIFT = 0x26 PERF_MEM_SNOOPX_SHIFT = 0x26
PERF_MEM_SNOOP_HIT = 0x4 PERF_MEM_SNOOP_HIT = 0x4
PERF_MEM_SNOOP_HITM = 0x10 PERF_MEM_SNOOP_HITM = 0x10
@ -2301,7 +2325,6 @@ const (
PERF_SAMPLE_BRANCH_PLM_ALL = 0x7 PERF_SAMPLE_BRANCH_PLM_ALL = 0x7
PERF_SAMPLE_WEIGHT_TYPE = 0x1004000 PERF_SAMPLE_WEIGHT_TYPE = 0x1004000
PIPEFS_MAGIC = 0x50495045 PIPEFS_MAGIC = 0x50495045
PPC_CMM_MAGIC = 0xc7571590
PPPIOCGNPMODE = 0xc008744c PPPIOCGNPMODE = 0xc008744c
PPPIOCNEWUNIT = 0xc004743e PPPIOCNEWUNIT = 0xc004743e
PRIO_PGRP = 0x1 PRIO_PGRP = 0x1
@ -2999,6 +3022,7 @@ const (
STATX_BLOCKS = 0x400 STATX_BLOCKS = 0x400
STATX_BTIME = 0x800 STATX_BTIME = 0x800
STATX_CTIME = 0x80 STATX_CTIME = 0x80
STATX_DIOALIGN = 0x2000
STATX_GID = 0x10 STATX_GID = 0x10
STATX_INO = 0x100 STATX_INO = 0x100
STATX_MNT_ID = 0x1000 STATX_MNT_ID = 0x1000
@ -3392,9 +3416,7 @@ const (
XDP_ZEROCOPY = 0x4 XDP_ZEROCOPY = 0x4
XENFS_SUPER_MAGIC = 0xabba1974 XENFS_SUPER_MAGIC = 0xabba1974
XFS_SUPER_MAGIC = 0x58465342 XFS_SUPER_MAGIC = 0x58465342
Z3FOLD_MAGIC = 0x33
ZONEFS_MAGIC = 0x5a4f4653 ZONEFS_MAGIC = 0x5a4f4653
ZSMALLOC_MAGIC = 0x58295829
_HIDIOCGRAWNAME_LEN = 0x80 _HIDIOCGRAWNAME_LEN = 0x80
_HIDIOCGRAWPHYS_LEN = 0x40 _HIDIOCGRAWPHYS_LEN = 0x40
_HIDIOCGRAWUNIQ_LEN = 0x40 _HIDIOCGRAWUNIQ_LEN = 0x40

View File

@ -133,6 +133,7 @@ const (
MEMGETREGIONCOUNT = 0x80044d07 MEMGETREGIONCOUNT = 0x80044d07
MEMISLOCKED = 0x80084d17 MEMISLOCKED = 0x80084d17
MEMLOCK = 0x40084d05 MEMLOCK = 0x40084d05
MEMREAD = 0xc03c4d1a
MEMREADOOB = 0xc00c4d04 MEMREADOOB = 0xc00c4d04
MEMSETBADBLOCK = 0x40084d0c MEMSETBADBLOCK = 0x40084d0c
MEMUNLOCK = 0x40084d06 MEMUNLOCK = 0x40084d06

Some files were not shown because too many files have changed in this diff Show More