[chore]: Bump github.com/coreos/go-oidc/v3 from 3.6.0 to 3.7.0 (#2284)
Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/coreos/go-oidc/releases) - [Commits](https://github.com/coreos/go-oidc/compare/v3.6.0...v3.7.0) --- updated-dependencies: - dependency-name: github.com/coreos/go-oidc/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
9114c5ca1b
commit
3dcc94940d
8
go.mod
8
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/KimMachineGun/automemlimit v0.3.0
|
||||
github.com/abema/go-mp4 v1.1.1
|
||||
github.com/buckket/go-blurhash v1.1.0
|
||||
github.com/coreos/go-oidc/v3 v3.6.0
|
||||
github.com/coreos/go-oidc/v3 v3.7.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/gin-contrib/gzip v0.0.6
|
||||
|
@ -32,7 +32,6 @@ require (
|
|||
github.com/gorilla/feeds v1.1.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/jackc/pgconn v1.14.1
|
||||
github.com/jackc/pgx/v5 v5.4.3
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
github.com/miekg/dns v1.1.56
|
||||
|
@ -122,10 +121,7 @@ require (
|
|||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
|
@ -167,7 +163,7 @@ require (
|
|||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/grpc v1.58.2 // indirect
|
||||
|
|
93
go.sum
93
go.sum
|
@ -120,13 +120,10 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
|||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE=
|
||||
github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw=
|
||||
github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
|
||||
github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-oidc/v3 v3.7.0 h1:FTdj0uexT4diYIPlF4yoFVI5MRO1r5+SEcIpEw9vC0o=
|
||||
github.com/coreos/go-oidc/v3 v3.7.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
|
||||
|
@ -222,7 +219,6 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS
|
|||
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 h1:PSPmmucxGiFBtbQcttHTUc4LQ3P09AW+ldO2qspyKdY=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
|
@ -268,6 +264,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
@ -338,47 +335,12 @@ github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N
|
|||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4=
|
||||
github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
||||
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
|
@ -400,8 +362,6 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
|||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
@ -415,15 +375,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
|
@ -465,7 +419,6 @@ github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZO
|
|||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
|
@ -482,19 +435,12 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
|||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
|
@ -514,8 +460,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
@ -623,7 +567,6 @@ github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta
|
|||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.1/go.mod h1:TSQ0KjMH+pht+bRyvVooJ1rBpvvngSGaPISafq9MxJk=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -646,32 +589,22 @@ go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1
|
|||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/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-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -728,7 +661,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -752,7 +684,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -780,24 +711,18 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -838,10 +763,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.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-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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -851,7 +774,7 @@ 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -865,14 +788,12 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -910,8 +831,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -941,8 +860,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -1024,7 +944,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0 h1:kcsiS+WsTKyIEPABJBJtoG0KkOS6yzvJ+/eZlhD79kk=
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -159,7 +159,7 @@ func (r *RemoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) (
|
|||
// https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys
|
||||
keys, err := r.keysFromRemote(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching keys %v", err)
|
||||
return nil, fmt.Errorf("fetching keys %w", err)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
|
@ -228,11 +228,11 @@ func (r *RemoteKeySet) updateKeys() ([]jose.JSONWebKey, error) {
|
|||
|
||||
resp, err := doRequest(r.ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oidc: get keys failed %v", err)
|
||||
return nil, fmt.Errorf("oidc: get keys failed %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -211,7 +211,7 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource)
|
|||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -182,7 +182,7 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,8 +0,0 @@
|
|||
[![](https://godoc.org/github.com/jackc/chunkreader?status.svg)](https://godoc.org/github.com/jackc/chunkreader)
|
||||
[![Build Status](https://travis-ci.org/jackc/chunkreader.svg)](https://travis-ci.org/jackc/chunkreader)
|
||||
|
||||
# chunkreader
|
||||
|
||||
Package chunkreader provides an io.Reader wrapper that minimizes IO reads and memory allocations.
|
||||
|
||||
Extracted from original implementation in https://github.com/jackc/pgx.
|
|
@ -1,104 +0,0 @@
|
|||
// Package chunkreader provides an io.Reader wrapper that minimizes IO reads and memory allocations.
|
||||
package chunkreader
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ChunkReader is a io.Reader wrapper that minimizes IO reads and memory allocations. It allocates memory in chunks and
|
||||
// will read as much as will fit in the current buffer in a single call regardless of how large a read is actually
|
||||
// requested. The memory returned via Next is owned by the caller. This avoids the need for an additional copy.
|
||||
//
|
||||
// The downside of this approach is that a large buffer can be pinned in memory even if only a small slice is
|
||||
// referenced. For example, an entire 4096 byte block could be pinned in memory by even a 1 byte slice. In these rare
|
||||
// cases it would be advantageous to copy the bytes to another slice.
|
||||
type ChunkReader struct {
|
||||
r io.Reader
|
||||
|
||||
buf []byte
|
||||
rp, wp int // buf read position and write position
|
||||
|
||||
config Config
|
||||
}
|
||||
|
||||
// Config contains configuration parameters for ChunkReader.
|
||||
type Config struct {
|
||||
MinBufLen int // Minimum buffer length
|
||||
}
|
||||
|
||||
// New creates and returns a new ChunkReader for r with default configuration.
|
||||
func New(r io.Reader) *ChunkReader {
|
||||
cr, err := NewConfig(r, Config{})
|
||||
if err != nil {
|
||||
panic("default config can't be bad")
|
||||
}
|
||||
|
||||
return cr
|
||||
}
|
||||
|
||||
// NewConfig creates and a new ChunkReader for r configured by config.
|
||||
func NewConfig(r io.Reader, config Config) (*ChunkReader, error) {
|
||||
if config.MinBufLen == 0 {
|
||||
// By historical reasons Postgres currently has 8KB send buffer inside,
|
||||
// so here we want to have at least the same size buffer.
|
||||
// @see https://github.com/postgres/postgres/blob/249d64999615802752940e017ee5166e726bc7cd/src/backend/libpq/pqcomm.c#L134
|
||||
// @see https://www.postgresql.org/message-id/0cdc5485-cb3c-5e16-4a46-e3b2f7a41322%40ya.ru
|
||||
config.MinBufLen = 8192
|
||||
}
|
||||
|
||||
return &ChunkReader{
|
||||
r: r,
|
||||
buf: make([]byte, config.MinBufLen),
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Next returns buf filled with the next n bytes. The caller gains ownership of buf. It is not necessary to make a copy
|
||||
// of buf. If an error occurs, buf will be nil.
|
||||
func (r *ChunkReader) Next(n int) (buf []byte, err error) {
|
||||
// n bytes already in buf
|
||||
if (r.wp - r.rp) >= n {
|
||||
buf = r.buf[r.rp : r.rp+n]
|
||||
r.rp += n
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// available space in buf is less than n
|
||||
if len(r.buf) < n {
|
||||
r.copyBufContents(r.newBuf(n))
|
||||
}
|
||||
|
||||
// buf is large enough, but need to shift filled area to start to make enough contiguous space
|
||||
minReadCount := n - (r.wp - r.rp)
|
||||
if (len(r.buf) - r.wp) < minReadCount {
|
||||
newBuf := r.newBuf(n)
|
||||
r.copyBufContents(newBuf)
|
||||
}
|
||||
|
||||
if err := r.appendAtLeast(minReadCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf = r.buf[r.rp : r.rp+n]
|
||||
r.rp += n
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (r *ChunkReader) appendAtLeast(fillLen int) error {
|
||||
n, err := io.ReadAtLeast(r.r, r.buf[r.wp:], fillLen)
|
||||
r.wp += n
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ChunkReader) newBuf(size int) []byte {
|
||||
if size < r.config.MinBufLen {
|
||||
size = r.config.MinBufLen
|
||||
}
|
||||
return make([]byte, size)
|
||||
}
|
||||
|
||||
func (r *ChunkReader) copyBufContents(dest []byte) {
|
||||
r.wp = copy(dest, r.buf[r.rp:r.wp])
|
||||
r.rp = 0
|
||||
r.buf = dest
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
.envrc
|
||||
vendor/
|
||||
.vscode
|
|
@ -1,167 +0,0 @@
|
|||
# 1.14.1 (July 19, 2023)
|
||||
|
||||
* Fix: Enable failover efforts when pg_hba.conf disallows non-ssl connections (Brandon Kauffman)
|
||||
* Fix: connect_timeout is not obeyed for sslmode=allow|prefer (smaher-edb)
|
||||
* Optimize redundant pgpass parsing in case password is explicitly set (Aleksandr Alekseev)
|
||||
|
||||
# 1.14.0 (February 11, 2023)
|
||||
|
||||
* Fix: each connection attempt to new node gets own timeout (Nathan Giardina)
|
||||
* Set SNI for SSL connections (Stas Kelvich)
|
||||
* Fix: CopyFrom I/O race (Tommy Reilly)
|
||||
* Minor dependency upgrades
|
||||
|
||||
# 1.13.0 (August 6, 2022)
|
||||
|
||||
* Add sslpassword support (Eric McCormack and yun.xu)
|
||||
* Add prefer-standby target_session_attrs support (sergey.bashilov)
|
||||
* Fix GSS ErrorResponse handling (Oliver Tan)
|
||||
|
||||
# 1.12.1 (May 7, 2022)
|
||||
|
||||
* Fix: setting krbspn and krbsrvname in connection string (sireax)
|
||||
* Add support for Unix sockets on Windows (Eno Compton)
|
||||
* Stop ignoring ErrorResponse during SCRAM auth (Rafi Shamim)
|
||||
|
||||
# 1.12.0 (April 21, 2022)
|
||||
|
||||
* Add pluggable GSSAPI support (Oliver Tan)
|
||||
* Fix: Consider any "0A000" error a possible cached plan changed error due to locale
|
||||
* Better match psql fallback behavior with multiple hosts
|
||||
|
||||
# 1.11.0 (February 7, 2022)
|
||||
|
||||
* Support port in ip from LookupFunc to override config (James Hartig)
|
||||
* Fix TLS connection timeout (Blake Embrey)
|
||||
* Add support for read-only, primary, standby, prefer-standby target_session_attributes (Oscar)
|
||||
* Fix connect when receiving NoticeResponse
|
||||
|
||||
# 1.10.1 (November 20, 2021)
|
||||
|
||||
* Close without waiting for response (Kei Kamikawa)
|
||||
* Save waiting for network round-trip in CopyFrom (Rueian)
|
||||
* Fix concurrency issue with ContextWatcher
|
||||
* LRU.Get always checks context for cancellation / expiration (Georges Varouchas)
|
||||
|
||||
# 1.10.0 (July 24, 2021)
|
||||
|
||||
* net.Timeout errors are no longer returned when a query is canceled via context. A wrapped context error is returned.
|
||||
|
||||
# 1.9.0 (July 10, 2021)
|
||||
|
||||
* pgconn.Timeout only is true for errors originating in pgconn (Michael Darr)
|
||||
* Add defaults for sslcert, sslkey, and sslrootcert (Joshua Brindle)
|
||||
* Solve issue with 'sslmode=verify-full' when there are multiple hosts (mgoddard)
|
||||
* Fix default host when parsing URL without host but with port
|
||||
* Allow dbname query parameter in URL conn string
|
||||
* Update underlying dependencies
|
||||
|
||||
# 1.8.1 (March 25, 2021)
|
||||
|
||||
* Better connection string sanitization (ip.novikov)
|
||||
* Use proper pgpass location on Windows (Moshe Katz)
|
||||
* Use errors instead of golang.org/x/xerrors
|
||||
* Resume fallback on server error in Connect (Andrey Borodin)
|
||||
|
||||
# 1.8.0 (December 3, 2020)
|
||||
|
||||
* Add StatementErrored method to stmtcache.Cache. This allows the cache to purge invalidated prepared statements. (Ethan Pailes)
|
||||
|
||||
# 1.7.2 (November 3, 2020)
|
||||
|
||||
* Fix data value slices into work buffer with capacities larger than length.
|
||||
|
||||
# 1.7.1 (October 31, 2020)
|
||||
|
||||
* Do not asyncClose after receiving FATAL error from PostgreSQL server
|
||||
|
||||
# 1.7.0 (September 26, 2020)
|
||||
|
||||
* Exec(Params|Prepared) return ResultReader with FieldDescriptions loaded
|
||||
* Add ReceiveResults (Sebastiaan Mannem)
|
||||
* Fix parsing DSN connection with bad backslash
|
||||
* Add PgConn.CleanupDone so connection pools can determine when async close is complete
|
||||
|
||||
# 1.6.4 (July 29, 2020)
|
||||
|
||||
* Fix deadlock on error after CommandComplete but before ReadyForQuery
|
||||
* Fix panic on parsing DSN with trailing '='
|
||||
|
||||
# 1.6.3 (July 22, 2020)
|
||||
|
||||
* Fix error message after AppendCertsFromPEM failure (vahid-sohrabloo)
|
||||
|
||||
# 1.6.2 (July 14, 2020)
|
||||
|
||||
* Update pgservicefile library
|
||||
|
||||
# 1.6.1 (June 27, 2020)
|
||||
|
||||
* Update golang.org/x/crypto to latest
|
||||
* Update golang.org/x/text to 0.3.3
|
||||
* Fix error handling for bad PGSERVICE definition
|
||||
* Redact passwords in ParseConfig errors (Lukas Vogel)
|
||||
|
||||
# 1.6.0 (June 6, 2020)
|
||||
|
||||
* Fix panic when closing conn during cancellable query
|
||||
* Fix behavior of sslmode=require with sslrootcert present (Petr Jediný)
|
||||
* Fix field descriptions available after command concluded (Tobias Salzmann)
|
||||
* Support connect_timeout (georgysavva)
|
||||
* Handle IPv6 in connection URLs (Lukas Vogel)
|
||||
* Fix ValidateConnect with cancelable context
|
||||
* Improve CopyFrom performance
|
||||
* Add Config.Copy (georgysavva)
|
||||
|
||||
# 1.5.0 (March 30, 2020)
|
||||
|
||||
* Update golang.org/x/crypto for security fix
|
||||
* Implement "verify-ca" SSL mode (Greg Curtis)
|
||||
|
||||
# 1.4.0 (March 7, 2020)
|
||||
|
||||
* Fix ExecParams and ExecPrepared handling of empty query.
|
||||
* Support reading config from PostgreSQL service files.
|
||||
|
||||
# 1.3.2 (February 14, 2020)
|
||||
|
||||
* Update chunkreader to v2.0.1 for optimized default buffer size.
|
||||
|
||||
# 1.3.1 (February 5, 2020)
|
||||
|
||||
* Fix CopyFrom deadlock when multiple NoticeResponse received during copy
|
||||
|
||||
# 1.3.0 (January 23, 2020)
|
||||
|
||||
* Add Hijack and Construct.
|
||||
* Update pgproto3 to v2.0.1.
|
||||
|
||||
# 1.2.1 (January 13, 2020)
|
||||
|
||||
* Fix data race in context cancellation introduced in v1.2.0.
|
||||
|
||||
# 1.2.0 (January 11, 2020)
|
||||
|
||||
## Features
|
||||
|
||||
* Add Insert(), Update(), Delete(), and Select() statement type query methods to CommandTag.
|
||||
* Add PgError.SQLState method. This could be used for compatibility with other drivers and databases.
|
||||
|
||||
## Performance
|
||||
|
||||
* Improve performance when context.Background() is used. (bakape)
|
||||
* CommandTag.RowsAffected is faster and does not allocate.
|
||||
|
||||
## Fixes
|
||||
|
||||
* Try to cancel any in-progress query when a conn is closed by ctx cancel.
|
||||
* Handle NoticeResponse during CopyFrom.
|
||||
* Ignore errors sending Terminate message while closing connection. This mimics the behavior of libpq PGfinish.
|
||||
|
||||
# 1.1.0 (October 12, 2019)
|
||||
|
||||
* Add PgConn.IsBusy() method.
|
||||
|
||||
# 1.0.1 (September 19, 2019)
|
||||
|
||||
* Fix statement cache not properly cleaning discarded statements.
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2019-2021 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,62 +0,0 @@
|
|||
[![](https://godoc.org/github.com/jackc/pgconn?status.svg)](https://godoc.org/github.com/jackc/pgconn)
|
||||
![CI](https://github.com/jackc/pgconn/workflows/CI/badge.svg)
|
||||
|
||||
---
|
||||
|
||||
This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository.
|
||||
|
||||
---
|
||||
|
||||
# pgconn
|
||||
|
||||
Package pgconn is a low-level PostgreSQL database driver. It operates at nearly the same level as the C library libpq.
|
||||
It is primarily intended to serve as the foundation for higher level libraries such as https://github.com/jackc/pgx.
|
||||
Applications should handle normal queries with a higher level library and only use pgconn directly when required for
|
||||
low-level access to PostgreSQL functionality.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```go
|
||||
pgConn, err := pgconn.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
||||
if err != nil {
|
||||
log.Fatalln("pgconn failed to connect:", err)
|
||||
}
|
||||
defer pgConn.Close(context.Background())
|
||||
|
||||
result := pgConn.ExecParams(context.Background(), "SELECT email FROM users WHERE id=$1", [][]byte{[]byte("123")}, nil, nil, nil)
|
||||
for result.NextRow() {
|
||||
fmt.Println("User 123 has email:", string(result.Values()[0]))
|
||||
}
|
||||
_, err = result.Close()
|
||||
if err != nil {
|
||||
log.Fatalln("failed reading result:", err)
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The pgconn tests require a PostgreSQL database. It will connect to the database specified in the `PGX_TEST_CONN_STRING`
|
||||
environment variable. The `PGX_TEST_CONN_STRING` environment variable can be a URL or DSN. In addition, the standard `PG*`
|
||||
environment variables will be respected. Consider using [direnv](https://github.com/direnv/direnv) to simplify
|
||||
environment variable handling.
|
||||
|
||||
### Example Test Environment
|
||||
|
||||
Connect to your PostgreSQL server and run:
|
||||
|
||||
```
|
||||
create database pgx_test;
|
||||
```
|
||||
|
||||
Now you can run the tests:
|
||||
|
||||
```bash
|
||||
PGX_TEST_CONN_STRING="host=/var/run/postgresql dbname=pgx_test" go test ./...
|
||||
```
|
||||
|
||||
### Connection and Authentication Tests
|
||||
|
||||
Pgconn supports multiple connection types and means of authentication. These tests are optional. They
|
||||
will only run if the appropriate environment variable is set. Run `go test -v | grep SKIP` to see if any tests are being
|
||||
skipped. Most developers will not need to enable these tests. See `ci/setup_test.bash` for an example set up if you need change
|
||||
authentication code.
|
|
@ -1,270 +0,0 @@
|
|||
// SCRAM-SHA-256 authentication
|
||||
//
|
||||
// Resources:
|
||||
// https://tools.ietf.org/html/rfc5802
|
||||
// https://tools.ietf.org/html/rfc8265
|
||||
// https://www.postgresql.org/docs/current/sasl-authentication.html
|
||||
//
|
||||
// Inspiration drawn from other implementations:
|
||||
// https://github.com/lib/pq/pull/608
|
||||
// https://github.com/lib/pq/pull/788
|
||||
// https://github.com/lib/pq/pull/833
|
||||
|
||||
package pgconn
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgproto3/v2"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/text/secure/precis"
|
||||
)
|
||||
|
||||
const clientNonceLen = 18
|
||||
|
||||
// Perform SCRAM authentication.
|
||||
func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
|
||||
sc, err := newScramClient(serverAuthMechanisms, c.config.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send client-first-message in a SASLInitialResponse
|
||||
saslInitialResponse := &pgproto3.SASLInitialResponse{
|
||||
AuthMechanism: "SCRAM-SHA-256",
|
||||
Data: sc.clientFirstMessage(),
|
||||
}
|
||||
_, err = c.conn.Write(saslInitialResponse.Encode(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-first-message payload in a AuthenticationSASLContinue.
|
||||
saslContinue, err := c.rxSASLContinue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sc.recvServerFirstMessage(saslContinue.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send client-final-message in a SASLResponse
|
||||
saslResponse := &pgproto3.SASLResponse{
|
||||
Data: []byte(sc.clientFinalMessage()),
|
||||
}
|
||||
_, err = c.conn.Write(saslResponse.Encode(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-final-message payload in a AuthenticationSASLFinal.
|
||||
saslFinal, err := c.rxSASLFinal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sc.recvServerFinalMessage(saslFinal.Data)
|
||||
}
|
||||
|
||||
func (c *PgConn) rxSASLContinue() (*pgproto3.AuthenticationSASLContinue, error) {
|
||||
msg, err := c.receiveMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch m := msg.(type) {
|
||||
case *pgproto3.AuthenticationSASLContinue:
|
||||
return m, nil
|
||||
case *pgproto3.ErrorResponse:
|
||||
return nil, ErrorResponseToPgError(m)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("expected AuthenticationSASLContinue message but received unexpected message %T", msg)
|
||||
}
|
||||
|
||||
func (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) {
|
||||
msg, err := c.receiveMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch m := msg.(type) {
|
||||
case *pgproto3.AuthenticationSASLFinal:
|
||||
return m, nil
|
||||
case *pgproto3.ErrorResponse:
|
||||
return nil, ErrorResponseToPgError(m)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("expected AuthenticationSASLFinal message but received unexpected message %T", msg)
|
||||
}
|
||||
|
||||
type scramClient struct {
|
||||
serverAuthMechanisms []string
|
||||
password []byte
|
||||
clientNonce []byte
|
||||
|
||||
clientFirstMessageBare []byte
|
||||
|
||||
serverFirstMessage []byte
|
||||
clientAndServerNonce []byte
|
||||
salt []byte
|
||||
iterations int
|
||||
|
||||
saltedPassword []byte
|
||||
authMessage []byte
|
||||
}
|
||||
|
||||
func newScramClient(serverAuthMechanisms []string, password string) (*scramClient, error) {
|
||||
sc := &scramClient{
|
||||
serverAuthMechanisms: serverAuthMechanisms,
|
||||
}
|
||||
|
||||
// Ensure server supports SCRAM-SHA-256
|
||||
hasScramSHA256 := false
|
||||
for _, mech := range sc.serverAuthMechanisms {
|
||||
if mech == "SCRAM-SHA-256" {
|
||||
hasScramSHA256 = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasScramSHA256 {
|
||||
return nil, errors.New("server does not support SCRAM-SHA-256")
|
||||
}
|
||||
|
||||
// precis.OpaqueString is equivalent to SASLprep for password.
|
||||
var err error
|
||||
sc.password, err = precis.OpaqueString.Bytes([]byte(password))
|
||||
if err != nil {
|
||||
// PostgreSQL allows passwords invalid according to SCRAM / SASLprep.
|
||||
sc.password = []byte(password)
|
||||
}
|
||||
|
||||
buf := make([]byte, clientNonceLen)
|
||||
_, err = rand.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc.clientNonce = make([]byte, base64.RawStdEncoding.EncodedLen(len(buf)))
|
||||
base64.RawStdEncoding.Encode(sc.clientNonce, buf)
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
func (sc *scramClient) clientFirstMessage() []byte {
|
||||
sc.clientFirstMessageBare = []byte(fmt.Sprintf("n=,r=%s", sc.clientNonce))
|
||||
return []byte(fmt.Sprintf("n,,%s", sc.clientFirstMessageBare))
|
||||
}
|
||||
|
||||
func (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {
|
||||
sc.serverFirstMessage = serverFirstMessage
|
||||
buf := serverFirstMessage
|
||||
if !bytes.HasPrefix(buf, []byte("r=")) {
|
||||
return errors.New("invalid SCRAM server-first-message received from server: did not include r=")
|
||||
}
|
||||
buf = buf[2:]
|
||||
|
||||
idx := bytes.IndexByte(buf, ',')
|
||||
if idx == -1 {
|
||||
return errors.New("invalid SCRAM server-first-message received from server: did not include s=")
|
||||
}
|
||||
sc.clientAndServerNonce = buf[:idx]
|
||||
buf = buf[idx+1:]
|
||||
|
||||
if !bytes.HasPrefix(buf, []byte("s=")) {
|
||||
return errors.New("invalid SCRAM server-first-message received from server: did not include s=")
|
||||
}
|
||||
buf = buf[2:]
|
||||
|
||||
idx = bytes.IndexByte(buf, ',')
|
||||
if idx == -1 {
|
||||
return errors.New("invalid SCRAM server-first-message received from server: did not include i=")
|
||||
}
|
||||
saltStr := buf[:idx]
|
||||
buf = buf[idx+1:]
|
||||
|
||||
if !bytes.HasPrefix(buf, []byte("i=")) {
|
||||
return errors.New("invalid SCRAM server-first-message received from server: did not include i=")
|
||||
}
|
||||
buf = buf[2:]
|
||||
iterationsStr := buf
|
||||
|
||||
var err error
|
||||
sc.salt, err = base64.StdEncoding.DecodeString(string(saltStr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid SCRAM salt received from server: %w", err)
|
||||
}
|
||||
|
||||
sc.iterations, err = strconv.Atoi(string(iterationsStr))
|
||||
if err != nil || sc.iterations <= 0 {
|
||||
return fmt.Errorf("invalid SCRAM iteration count received from server: %w", err)
|
||||
}
|
||||
|
||||
if !bytes.HasPrefix(sc.clientAndServerNonce, sc.clientNonce) {
|
||||
return errors.New("invalid SCRAM nonce: did not start with client nonce")
|
||||
}
|
||||
|
||||
if len(sc.clientAndServerNonce) <= len(sc.clientNonce) {
|
||||
return errors.New("invalid SCRAM nonce: did not include server nonce")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *scramClient) clientFinalMessage() string {
|
||||
clientFinalMessageWithoutProof := []byte(fmt.Sprintf("c=biws,r=%s", sc.clientAndServerNonce))
|
||||
|
||||
sc.saltedPassword = pbkdf2.Key([]byte(sc.password), sc.salt, sc.iterations, 32, sha256.New)
|
||||
sc.authMessage = bytes.Join([][]byte{sc.clientFirstMessageBare, sc.serverFirstMessage, clientFinalMessageWithoutProof}, []byte(","))
|
||||
|
||||
clientProof := computeClientProof(sc.saltedPassword, sc.authMessage)
|
||||
|
||||
return fmt.Sprintf("%s,p=%s", clientFinalMessageWithoutProof, clientProof)
|
||||
}
|
||||
|
||||
func (sc *scramClient) recvServerFinalMessage(serverFinalMessage []byte) error {
|
||||
if !bytes.HasPrefix(serverFinalMessage, []byte("v=")) {
|
||||
return errors.New("invalid SCRAM server-final-message received from server")
|
||||
}
|
||||
|
||||
serverSignature := serverFinalMessage[2:]
|
||||
|
||||
if !hmac.Equal(serverSignature, computeServerSignature(sc.saltedPassword, sc.authMessage)) {
|
||||
return errors.New("invalid SCRAM ServerSignature received from server")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeHMAC(key, msg []byte) []byte {
|
||||
mac := hmac.New(sha256.New, key)
|
||||
mac.Write(msg)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func computeClientProof(saltedPassword, authMessage []byte) []byte {
|
||||
clientKey := computeHMAC(saltedPassword, []byte("Client Key"))
|
||||
storedKey := sha256.Sum256(clientKey)
|
||||
clientSignature := computeHMAC(storedKey[:], authMessage)
|
||||
|
||||
clientProof := make([]byte, len(clientSignature))
|
||||
for i := 0; i < len(clientSignature); i++ {
|
||||
clientProof[i] = clientKey[i] ^ clientSignature[i]
|
||||
}
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.EncodedLen(len(clientProof)))
|
||||
base64.StdEncoding.Encode(buf, clientProof)
|
||||
return buf
|
||||
}
|
||||
|
||||
func computeServerSignature(saltedPassword []byte, authMessage []byte) []byte {
|
||||
serverKey := computeHMAC(saltedPassword, []byte("Server Key"))
|
||||
serverSignature := computeHMAC(serverKey, authMessage)
|
||||
buf := make([]byte, base64.StdEncoding.EncodedLen(len(serverSignature)))
|
||||
base64.StdEncoding.Encode(buf, serverSignature)
|
||||
return buf
|
||||
}
|
|
@ -1,905 +0,0 @@
|
|||
package pgconn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/chunkreader/v2"
|
||||
"github.com/jackc/pgpassfile"
|
||||
"github.com/jackc/pgproto3/v2"
|
||||
"github.com/jackc/pgservicefile"
|
||||
)
|
||||
|
||||
type AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error
|
||||
type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error
|
||||
type GetSSLPasswordFunc func(ctx context.Context) string
|
||||
|
||||
// Config is the settings used to establish a connection to a PostgreSQL server. It must be created by ParseConfig. A
|
||||
// manually initialized Config will cause ConnectConfig to panic.
|
||||
type Config struct {
|
||||
Host string // host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp)
|
||||
Port uint16
|
||||
Database string
|
||||
User string
|
||||
Password string
|
||||
TLSConfig *tls.Config // nil disables TLS
|
||||
ConnectTimeout time.Duration
|
||||
DialFunc DialFunc // e.g. net.Dialer.DialContext
|
||||
LookupFunc LookupFunc // e.g. net.Resolver.LookupHost
|
||||
BuildFrontend BuildFrontendFunc
|
||||
RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)
|
||||
|
||||
KerberosSrvName string
|
||||
KerberosSpn string
|
||||
Fallbacks []*FallbackConfig
|
||||
|
||||
// ValidateConnect is called during a connection attempt after a successful authentication with the PostgreSQL server.
|
||||
// It can be used to validate that the server is acceptable. If this returns an error the connection is closed and the next
|
||||
// fallback config is tried. This allows implementing high availability behavior such as libpq does with target_session_attrs.
|
||||
ValidateConnect ValidateConnectFunc
|
||||
|
||||
// AfterConnect is called after ValidateConnect. It can be used to set up the connection (e.g. Set session variables
|
||||
// or prepare statements). If this returns an error the connection attempt fails.
|
||||
AfterConnect AfterConnectFunc
|
||||
|
||||
// OnNotice is a callback function called when a notice response is received.
|
||||
OnNotice NoticeHandler
|
||||
|
||||
// OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.
|
||||
OnNotification NotificationHandler
|
||||
|
||||
createdByParseConfig bool // Used to enforce created by ParseConfig rule.
|
||||
}
|
||||
|
||||
// ParseConfigOptions contains options that control how a config is built such as getsslpassword.
|
||||
type ParseConfigOptions struct {
|
||||
// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the the libpq function
|
||||
// PQsetSSLKeyPassHook_OpenSSL.
|
||||
GetSSLPassword GetSSLPasswordFunc
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the config that is safe to use and modify.
|
||||
// The only exception is the TLSConfig field:
|
||||
// according to the tls.Config docs it must not be modified after creation.
|
||||
func (c *Config) Copy() *Config {
|
||||
newConf := new(Config)
|
||||
*newConf = *c
|
||||
if newConf.TLSConfig != nil {
|
||||
newConf.TLSConfig = c.TLSConfig.Clone()
|
||||
}
|
||||
if newConf.RuntimeParams != nil {
|
||||
newConf.RuntimeParams = make(map[string]string, len(c.RuntimeParams))
|
||||
for k, v := range c.RuntimeParams {
|
||||
newConf.RuntimeParams[k] = v
|
||||
}
|
||||
}
|
||||
if newConf.Fallbacks != nil {
|
||||
newConf.Fallbacks = make([]*FallbackConfig, len(c.Fallbacks))
|
||||
for i, fallback := range c.Fallbacks {
|
||||
newFallback := new(FallbackConfig)
|
||||
*newFallback = *fallback
|
||||
if newFallback.TLSConfig != nil {
|
||||
newFallback.TLSConfig = fallback.TLSConfig.Clone()
|
||||
}
|
||||
newConf.Fallbacks[i] = newFallback
|
||||
}
|
||||
}
|
||||
return newConf
|
||||
}
|
||||
|
||||
// FallbackConfig is additional settings to attempt a connection with when the primary Config fails to establish a
|
||||
// network connection. It is used for TLS fallback such as sslmode=prefer and high availability (HA) connections.
|
||||
type FallbackConfig struct {
|
||||
Host string // host (e.g. localhost) or path to unix domain socket directory (e.g. /private/tmp)
|
||||
Port uint16
|
||||
TLSConfig *tls.Config // nil disables TLS
|
||||
}
|
||||
|
||||
// isAbsolutePath checks if the provided value is an absolute path either
|
||||
// beginning with a forward slash (as on Linux-based systems) or with a capital
|
||||
// letter A-Z followed by a colon and a backslash, e.g., "C:\", (as on Windows).
|
||||
func isAbsolutePath(path string) bool {
|
||||
isWindowsPath := func(p string) bool {
|
||||
if len(p) < 3 {
|
||||
return false
|
||||
}
|
||||
drive := p[0]
|
||||
colon := p[1]
|
||||
backslash := p[2]
|
||||
if drive >= 'A' && drive <= 'Z' && colon == ':' && backslash == '\\' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(path, "/") || isWindowsPath(path)
|
||||
}
|
||||
|
||||
// NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with
|
||||
// net.Dial.
|
||||
func NetworkAddress(host string, port uint16) (network, address string) {
|
||||
if isAbsolutePath(host) {
|
||||
network = "unix"
|
||||
address = filepath.Join(host, ".s.PGSQL.") + strconv.FormatInt(int64(port), 10)
|
||||
} else {
|
||||
network = "tcp"
|
||||
address = net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||
}
|
||||
return network, address
|
||||
}
|
||||
|
||||
// ParseConfig builds a *Config from connString with similar behavior to the PostgreSQL standard C library libpq. It
|
||||
// uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely
|
||||
// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style).
|
||||
// See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be
|
||||
// empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.
|
||||
//
|
||||
// # Example DSN
|
||||
// user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca
|
||||
//
|
||||
// # Example URL
|
||||
// postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca
|
||||
//
|
||||
// The returned *Config may be modified. However, it is strongly recommended that any configuration that can be done
|
||||
// through the connection string be done there. In particular the fields Host, Port, TLSConfig, and Fallbacks can be
|
||||
// interdependent (e.g. TLSConfig needs knowledge of the host to validate the server certificate). These fields should
|
||||
// not be modified individually. They should all be modified or all left unchanged.
|
||||
//
|
||||
// ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated
|
||||
// values that will be tried in order. This can be used as part of a high availability system. See
|
||||
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information.
|
||||
//
|
||||
// # Example URL
|
||||
// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb
|
||||
//
|
||||
// ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed
|
||||
// via database URL or DSN:
|
||||
//
|
||||
// PGHOST
|
||||
// PGPORT
|
||||
// PGDATABASE
|
||||
// PGUSER
|
||||
// PGPASSWORD
|
||||
// PGPASSFILE
|
||||
// PGSERVICE
|
||||
// PGSERVICEFILE
|
||||
// PGSSLMODE
|
||||
// PGSSLCERT
|
||||
// PGSSLKEY
|
||||
// PGSSLROOTCERT
|
||||
// PGSSLPASSWORD
|
||||
// PGAPPNAME
|
||||
// PGCONNECT_TIMEOUT
|
||||
// PGTARGETSESSIONATTRS
|
||||
//
|
||||
// See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables.
|
||||
//
|
||||
// See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are
|
||||
// usually but not always the environment variable name downcased and without the "PG" prefix.
|
||||
//
|
||||
// Important Security Notes:
|
||||
//
|
||||
// ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to "prefer" behavior if
|
||||
// not set.
|
||||
//
|
||||
// See http://www.postgresql.org/docs/11/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of
|
||||
// security each sslmode provides.
|
||||
//
|
||||
// The sslmode "prefer" (the default), sslmode "allow", and multiple hosts are implemented via the Fallbacks field of
|
||||
// the Config struct. If TLSConfig is manually changed it will not affect the fallbacks. For example, in the case of
|
||||
// sslmode "prefer" this means it will first try the main Config settings which use TLS, then it will try the fallback
|
||||
// which does not use TLS. This can lead to an unexpected unencrypted connection if the main TLS config is manually
|
||||
// changed later but the unencrypted fallback is present. Ensure there are no stale fallbacks when manually setting
|
||||
// TLSConfig.
|
||||
//
|
||||
// Other known differences with libpq:
|
||||
//
|
||||
// When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn
|
||||
// does not.
|
||||
//
|
||||
// In addition, ParseConfig accepts the following options:
|
||||
//
|
||||
// min_read_buffer_size
|
||||
// The minimum size of the internal read buffer. Default 8192.
|
||||
// servicefile
|
||||
// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a
|
||||
// part of the connection string.
|
||||
func ParseConfig(connString string) (*Config, error) {
|
||||
var parseConfigOptions ParseConfigOptions
|
||||
return ParseConfigWithOptions(connString, parseConfigOptions)
|
||||
}
|
||||
|
||||
// ParseConfigWithOptions builds a *Config from connString and options with similar behavior to the PostgreSQL standard
|
||||
// C library libpq. options contains settings that cannot be specified in a connString such as providing a function to
|
||||
// get the SSL password.
|
||||
func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Config, error) {
|
||||
defaultSettings := defaultSettings()
|
||||
envSettings := parseEnvSettings()
|
||||
|
||||
connStringSettings := make(map[string]string)
|
||||
if connString != "" {
|
||||
var err error
|
||||
// connString may be a database URL or a DSN
|
||||
if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
|
||||
connStringSettings, err = parseURLSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as URL", err: err}
|
||||
}
|
||||
} else {
|
||||
connStringSettings, err = parseDSNSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as DSN", err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
settings := mergeSettings(defaultSettings, envSettings, connStringSettings)
|
||||
if service, present := settings["service"]; present {
|
||||
serviceSettings, err := parseServiceSettings(settings["servicefile"], service)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to read service", err: err}
|
||||
}
|
||||
|
||||
settings = mergeSettings(defaultSettings, envSettings, serviceSettings, connStringSettings)
|
||||
}
|
||||
|
||||
minReadBufferSize, err := strconv.ParseInt(settings["min_read_buffer_size"], 10, 32)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "cannot parse min_read_buffer_size", err: err}
|
||||
}
|
||||
|
||||
config := &Config{
|
||||
createdByParseConfig: true,
|
||||
Database: settings["database"],
|
||||
User: settings["user"],
|
||||
Password: settings["password"],
|
||||
RuntimeParams: make(map[string]string),
|
||||
BuildFrontend: makeDefaultBuildFrontendFunc(int(minReadBufferSize)),
|
||||
}
|
||||
|
||||
if connectTimeoutSetting, present := settings["connect_timeout"]; present {
|
||||
connectTimeout, err := parseConnectTimeoutSetting(connectTimeoutSetting)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid connect_timeout", err: err}
|
||||
}
|
||||
config.ConnectTimeout = connectTimeout
|
||||
config.DialFunc = makeConnectTimeoutDialFunc(connectTimeout)
|
||||
} else {
|
||||
defaultDialer := makeDefaultDialer()
|
||||
config.DialFunc = defaultDialer.DialContext
|
||||
}
|
||||
|
||||
config.LookupFunc = makeDefaultResolver().LookupHost
|
||||
|
||||
notRuntimeParams := map[string]struct{}{
|
||||
"host": {},
|
||||
"port": {},
|
||||
"database": {},
|
||||
"user": {},
|
||||
"password": {},
|
||||
"passfile": {},
|
||||
"connect_timeout": {},
|
||||
"sslmode": {},
|
||||
"sslkey": {},
|
||||
"sslcert": {},
|
||||
"sslrootcert": {},
|
||||
"sslpassword": {},
|
||||
"sslsni": {},
|
||||
"krbspn": {},
|
||||
"krbsrvname": {},
|
||||
"target_session_attrs": {},
|
||||
"min_read_buffer_size": {},
|
||||
"service": {},
|
||||
"servicefile": {},
|
||||
}
|
||||
|
||||
// Adding kerberos configuration
|
||||
if _, present := settings["krbsrvname"]; present {
|
||||
config.KerberosSrvName = settings["krbsrvname"]
|
||||
}
|
||||
if _, present := settings["krbspn"]; present {
|
||||
config.KerberosSpn = settings["krbspn"]
|
||||
}
|
||||
|
||||
for k, v := range settings {
|
||||
if _, present := notRuntimeParams[k]; present {
|
||||
continue
|
||||
}
|
||||
config.RuntimeParams[k] = v
|
||||
}
|
||||
|
||||
fallbacks := []*FallbackConfig{}
|
||||
|
||||
hosts := strings.Split(settings["host"], ",")
|
||||
ports := strings.Split(settings["port"], ",")
|
||||
|
||||
for i, host := range hosts {
|
||||
var portStr string
|
||||
if i < len(ports) {
|
||||
portStr = ports[i]
|
||||
} else {
|
||||
portStr = ports[0]
|
||||
}
|
||||
|
||||
port, err := parsePort(portStr)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid port", err: err}
|
||||
}
|
||||
|
||||
var tlsConfigs []*tls.Config
|
||||
|
||||
// Ignore TLS settings if Unix domain socket like libpq
|
||||
if network, _ := NetworkAddress(host, port); network == "unix" {
|
||||
tlsConfigs = append(tlsConfigs, nil)
|
||||
} else {
|
||||
var err error
|
||||
tlsConfigs, err = configTLS(settings, host, options)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to configure TLS", err: err}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tlsConfig := range tlsConfigs {
|
||||
fallbacks = append(fallbacks, &FallbackConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
config.Host = fallbacks[0].Host
|
||||
config.Port = fallbacks[0].Port
|
||||
config.TLSConfig = fallbacks[0].TLSConfig
|
||||
config.Fallbacks = fallbacks[1:]
|
||||
|
||||
if config.Password == "" {
|
||||
passfile, err := pgpassfile.ReadPassfile(settings["passfile"])
|
||||
if err == nil {
|
||||
host := config.Host
|
||||
if network, _ := NetworkAddress(config.Host, config.Port); network == "unix" {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
config.Password = passfile.FindPassword(host, strconv.Itoa(int(config.Port)), config.Database, config.User)
|
||||
}
|
||||
}
|
||||
|
||||
switch tsa := settings["target_session_attrs"]; tsa {
|
||||
case "read-write":
|
||||
config.ValidateConnect = ValidateConnectTargetSessionAttrsReadWrite
|
||||
case "read-only":
|
||||
config.ValidateConnect = ValidateConnectTargetSessionAttrsReadOnly
|
||||
case "primary":
|
||||
config.ValidateConnect = ValidateConnectTargetSessionAttrsPrimary
|
||||
case "standby":
|
||||
config.ValidateConnect = ValidateConnectTargetSessionAttrsStandby
|
||||
case "prefer-standby":
|
||||
config.ValidateConnect = ValidateConnectTargetSessionAttrsPreferStandby
|
||||
case "any":
|
||||
// do nothing
|
||||
default:
|
||||
return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func mergeSettings(settingSets ...map[string]string) map[string]string {
|
||||
settings := make(map[string]string)
|
||||
|
||||
for _, s2 := range settingSets {
|
||||
for k, v := range s2 {
|
||||
settings[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
func parseEnvSettings() map[string]string {
|
||||
settings := make(map[string]string)
|
||||
|
||||
nameMap := map[string]string{
|
||||
"PGHOST": "host",
|
||||
"PGPORT": "port",
|
||||
"PGDATABASE": "database",
|
||||
"PGUSER": "user",
|
||||
"PGPASSWORD": "password",
|
||||
"PGPASSFILE": "passfile",
|
||||
"PGAPPNAME": "application_name",
|
||||
"PGCONNECT_TIMEOUT": "connect_timeout",
|
||||
"PGSSLMODE": "sslmode",
|
||||
"PGSSLKEY": "sslkey",
|
||||
"PGSSLCERT": "sslcert",
|
||||
"PGSSLSNI": "sslsni",
|
||||
"PGSSLROOTCERT": "sslrootcert",
|
||||
"PGSSLPASSWORD": "sslpassword",
|
||||
"PGTARGETSESSIONATTRS": "target_session_attrs",
|
||||
"PGSERVICE": "service",
|
||||
"PGSERVICEFILE": "servicefile",
|
||||
}
|
||||
|
||||
for envname, realname := range nameMap {
|
||||
value := os.Getenv(envname)
|
||||
if value != "" {
|
||||
settings[realname] = value
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
func parseURLSettings(connString string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
url, err := url.Parse(connString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if url.User != nil {
|
||||
settings["user"] = url.User.Username()
|
||||
if password, present := url.User.Password(); present {
|
||||
settings["password"] = password
|
||||
}
|
||||
}
|
||||
|
||||
// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
|
||||
var hosts []string
|
||||
var ports []string
|
||||
for _, host := range strings.Split(url.Host, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
if isIPOnly(host) {
|
||||
hosts = append(hosts, strings.Trim(host, "[]"))
|
||||
continue
|
||||
}
|
||||
h, p, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to split host:port in '%s', err: %w", host, err)
|
||||
}
|
||||
if h != "" {
|
||||
hosts = append(hosts, h)
|
||||
}
|
||||
if p != "" {
|
||||
ports = append(ports, p)
|
||||
}
|
||||
}
|
||||
if len(hosts) > 0 {
|
||||
settings["host"] = strings.Join(hosts, ",")
|
||||
}
|
||||
if len(ports) > 0 {
|
||||
settings["port"] = strings.Join(ports, ",")
|
||||
}
|
||||
|
||||
database := strings.TrimLeft(url.Path, "/")
|
||||
if database != "" {
|
||||
settings["database"] = database
|
||||
}
|
||||
|
||||
nameMap := map[string]string{
|
||||
"dbname": "database",
|
||||
}
|
||||
|
||||
for k, v := range url.Query() {
|
||||
if k2, present := nameMap[k]; present {
|
||||
k = k2
|
||||
}
|
||||
|
||||
settings[k] = v[0]
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func isIPOnly(host string) bool {
|
||||
return net.ParseIP(strings.Trim(host, "[]")) != nil || !strings.Contains(host, ":")
|
||||
}
|
||||
|
||||
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
|
||||
|
||||
func parseDSNSettings(s string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
nameMap := map[string]string{
|
||||
"dbname": "database",
|
||||
}
|
||||
|
||||
for len(s) > 0 {
|
||||
var key, val string
|
||||
eqIdx := strings.IndexRune(s, '=')
|
||||
if eqIdx < 0 {
|
||||
return nil, errors.New("invalid dsn")
|
||||
}
|
||||
|
||||
key = strings.Trim(s[:eqIdx], " \t\n\r\v\f")
|
||||
s = strings.TrimLeft(s[eqIdx+1:], " \t\n\r\v\f")
|
||||
if len(s) == 0 {
|
||||
} else if s[0] != '\'' {
|
||||
end := 0
|
||||
for ; end < len(s); end++ {
|
||||
if asciiSpace[s[end]] == 1 {
|
||||
break
|
||||
}
|
||||
if s[end] == '\\' {
|
||||
end++
|
||||
if end == len(s) {
|
||||
return nil, errors.New("invalid backslash")
|
||||
}
|
||||
}
|
||||
}
|
||||
val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1)
|
||||
if end == len(s) {
|
||||
s = ""
|
||||
} else {
|
||||
s = s[end+1:]
|
||||
}
|
||||
} else { // quoted string
|
||||
s = s[1:]
|
||||
end := 0
|
||||
for ; end < len(s); end++ {
|
||||
if s[end] == '\'' {
|
||||
break
|
||||
}
|
||||
if s[end] == '\\' {
|
||||
end++
|
||||
}
|
||||
}
|
||||
if end == len(s) {
|
||||
return nil, errors.New("unterminated quoted string in connection info string")
|
||||
}
|
||||
val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1)
|
||||
if end == len(s) {
|
||||
s = ""
|
||||
} else {
|
||||
s = s[end+1:]
|
||||
}
|
||||
}
|
||||
|
||||
if k, ok := nameMap[key]; ok {
|
||||
key = k
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return nil, errors.New("invalid dsn")
|
||||
}
|
||||
|
||||
settings[key] = val
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func parseServiceSettings(servicefilePath, serviceName string) (map[string]string, error) {
|
||||
servicefile, err := pgservicefile.ReadServicefile(servicefilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read service file: %v", servicefilePath)
|
||||
}
|
||||
|
||||
service, err := servicefile.GetService(serviceName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to find service: %v", serviceName)
|
||||
}
|
||||
|
||||
nameMap := map[string]string{
|
||||
"dbname": "database",
|
||||
}
|
||||
|
||||
settings := make(map[string]string, len(service.Settings))
|
||||
for k, v := range service.Settings {
|
||||
if k2, present := nameMap[k]; present {
|
||||
k = k2
|
||||
}
|
||||
settings[k] = v
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// configTLS uses libpq's TLS parameters to construct []*tls.Config. It is
|
||||
// necessary to allow returning multiple TLS configs as sslmode "allow" and
|
||||
// "prefer" allow fallback.
|
||||
func configTLS(settings map[string]string, thisHost string, parseConfigOptions ParseConfigOptions) ([]*tls.Config, error) {
|
||||
host := thisHost
|
||||
sslmode := settings["sslmode"]
|
||||
sslrootcert := settings["sslrootcert"]
|
||||
sslcert := settings["sslcert"]
|
||||
sslkey := settings["sslkey"]
|
||||
sslpassword := settings["sslpassword"]
|
||||
sslsni := settings["sslsni"]
|
||||
|
||||
// Match libpq default behavior
|
||||
if sslmode == "" {
|
||||
sslmode = "prefer"
|
||||
}
|
||||
if sslsni == "" {
|
||||
sslsni = "1"
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
switch sslmode {
|
||||
case "disable":
|
||||
return []*tls.Config{nil}, nil
|
||||
case "allow", "prefer":
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
case "require":
|
||||
// According to PostgreSQL documentation, if a root CA file exists,
|
||||
// the behavior of sslmode=require should be the same as that of verify-ca
|
||||
//
|
||||
// See https://www.postgresql.org/docs/12/libpq-ssl.html
|
||||
if sslrootcert != "" {
|
||||
goto nextCase
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
break
|
||||
nextCase:
|
||||
fallthrough
|
||||
case "verify-ca":
|
||||
// Don't perform the default certificate verification because it
|
||||
// will verify the hostname. Instead, verify the server's
|
||||
// certificate chain ourselves in VerifyPeerCertificate and
|
||||
// ignore the server name. This emulates libpq's verify-ca
|
||||
// behavior.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/21971#issuecomment-332693931
|
||||
// and https://pkg.go.dev/crypto/tls?tab=doc#example-Config-VerifyPeerCertificate
|
||||
// for more info.
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.VerifyPeerCertificate = func(certificates [][]byte, _ [][]*x509.Certificate) error {
|
||||
certs := make([]*x509.Certificate, len(certificates))
|
||||
for i, asn1Data := range certificates {
|
||||
cert, err := x509.ParseCertificate(asn1Data)
|
||||
if err != nil {
|
||||
return errors.New("failed to parse certificate from server: " + err.Error())
|
||||
}
|
||||
certs[i] = cert
|
||||
}
|
||||
|
||||
// Leave DNSName empty to skip hostname verification.
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
// Skip the first cert because it's the leaf. All others
|
||||
// are intermediates.
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
_, err := certs[0].Verify(opts)
|
||||
return err
|
||||
}
|
||||
case "verify-full":
|
||||
tlsConfig.ServerName = host
|
||||
default:
|
||||
return nil, errors.New("sslmode is invalid")
|
||||
}
|
||||
|
||||
if sslrootcert != "" {
|
||||
caCertPool := x509.NewCertPool()
|
||||
|
||||
caPath := sslrootcert
|
||||
caCert, err := ioutil.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read CA file: %w", err)
|
||||
}
|
||||
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("unable to add CA to cert pool")
|
||||
}
|
||||
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
tlsConfig.ClientCAs = caCertPool
|
||||
}
|
||||
|
||||
if (sslcert != "" && sslkey == "") || (sslcert == "" && sslkey != "") {
|
||||
return nil, errors.New(`both "sslcert" and "sslkey" are required`)
|
||||
}
|
||||
|
||||
if sslcert != "" && sslkey != "" {
|
||||
buf, err := ioutil.ReadFile(sslkey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read sslkey: %w", err)
|
||||
}
|
||||
block, _ := pem.Decode(buf)
|
||||
var pemKey []byte
|
||||
var decryptedKey []byte
|
||||
var decryptedError error
|
||||
// If PEM is encrypted, attempt to decrypt using pass phrase
|
||||
if x509.IsEncryptedPEMBlock(block) {
|
||||
// Attempt decryption with pass phrase
|
||||
// NOTE: only supports RSA (PKCS#1)
|
||||
if sslpassword != "" {
|
||||
decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword))
|
||||
}
|
||||
//if sslpassword not provided or has decryption error when use it
|
||||
//try to find sslpassword with callback function
|
||||
if sslpassword == "" || decryptedError != nil {
|
||||
if parseConfigOptions.GetSSLPassword != nil {
|
||||
sslpassword = parseConfigOptions.GetSSLPassword(context.Background())
|
||||
}
|
||||
if sslpassword == "" {
|
||||
return nil, fmt.Errorf("unable to find sslpassword")
|
||||
}
|
||||
}
|
||||
decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword))
|
||||
// Should we also provide warning for PKCS#1 needed?
|
||||
if decryptedError != nil {
|
||||
return nil, fmt.Errorf("unable to decrypt key: %w", err)
|
||||
}
|
||||
|
||||
pemBytes := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: decryptedKey,
|
||||
}
|
||||
pemKey = pem.EncodeToMemory(&pemBytes)
|
||||
} else {
|
||||
pemKey = pem.EncodeToMemory(block)
|
||||
}
|
||||
certfile, err := ioutil.ReadFile(sslcert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read cert: %w", err)
|
||||
}
|
||||
cert, err := tls.X509KeyPair(certfile, pemKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load cert: %w", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
// Set Server Name Indication (SNI), if enabled by connection parameters.
|
||||
// Per RFC 6066, do not set it if the host is a literal IP address (IPv4
|
||||
// or IPv6).
|
||||
if sslsni == "1" && net.ParseIP(host) == nil {
|
||||
tlsConfig.ServerName = host
|
||||
}
|
||||
|
||||
switch sslmode {
|
||||
case "allow":
|
||||
return []*tls.Config{nil, tlsConfig}, nil
|
||||
case "prefer":
|
||||
return []*tls.Config{tlsConfig, nil}, nil
|
||||
case "require", "verify-ca", "verify-full":
|
||||
return []*tls.Config{tlsConfig}, nil
|
||||
default:
|
||||
panic("BUG: bad sslmode should already have been caught")
|
||||
}
|
||||
}
|
||||
|
||||
func parsePort(s string) (uint16, error) {
|
||||
port, err := strconv.ParseUint(s, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if port < 1 || port > math.MaxUint16 {
|
||||
return 0, errors.New("outside range")
|
||||
}
|
||||
return uint16(port), nil
|
||||
}
|
||||
|
||||
func makeDefaultDialer() *net.Dialer {
|
||||
return &net.Dialer{KeepAlive: 5 * time.Minute}
|
||||
}
|
||||
|
||||
func makeDefaultResolver() *net.Resolver {
|
||||
return net.DefaultResolver
|
||||
}
|
||||
|
||||
func makeDefaultBuildFrontendFunc(minBufferLen int) BuildFrontendFunc {
|
||||
return func(r io.Reader, w io.Writer) Frontend {
|
||||
cr, err := chunkreader.NewConfig(r, chunkreader.Config{MinBufLen: minBufferLen})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("BUG: chunkreader.NewConfig failed: %v", err))
|
||||
}
|
||||
frontend := pgproto3.NewFrontend(cr, w)
|
||||
|
||||
return frontend
|
||||
}
|
||||
}
|
||||
|
||||
func parseConnectTimeoutSetting(s string) (time.Duration, error) {
|
||||
timeout, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if timeout < 0 {
|
||||
return 0, errors.New("negative timeout")
|
||||
}
|
||||
return time.Duration(timeout) * time.Second, nil
|
||||
}
|
||||
|
||||
func makeConnectTimeoutDialFunc(timeout time.Duration) DialFunc {
|
||||
d := makeDefaultDialer()
|
||||
d.Timeout = timeout
|
||||
return d.DialContext
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadWrite is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-write.
|
||||
func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if string(result.Rows[0][0]) == "on" {
|
||||
return errors.New("read only connection")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadOnly is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-only.
|
||||
func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if string(result.Rows[0][0]) != "on" {
|
||||
return errors.New("connection is not read only")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=standby.
|
||||
func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if string(result.Rows[0][0]) != "t" {
|
||||
return errors.New("server is not in hot standby mode")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPrimary is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=primary.
|
||||
func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if string(result.Rows[0][0]) == "t" {
|
||||
return errors.New("server is in standby mode")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPreferStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=prefer-standby.
|
||||
func ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if string(result.Rows[0][0]) != "t" {
|
||||
return &NotPreferredError{err: errors.New("server is not in hot standby mode")}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package pgconn
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func defaultSettings() map[string]string {
|
||||
settings := make(map[string]string)
|
||||
|
||||
settings["host"] = defaultHost()
|
||||
settings["port"] = "5432"
|
||||
|
||||
// Default to the OS user name. Purposely ignoring err getting user name from
|
||||
// OS. The client application will simply have to specify the user in that
|
||||
// case (which they typically will be doing anyway).
|
||||
user, err := user.Current()
|
||||
if err == nil {
|
||||
settings["user"] = user.Username
|
||||
settings["passfile"] = filepath.Join(user.HomeDir, ".pgpass")
|
||||
settings["servicefile"] = filepath.Join(user.HomeDir, ".pg_service.conf")
|
||||
sslcert := filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
|
||||
sslkey := filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
|
||||
if _, err := os.Stat(sslcert); err == nil {
|
||||
if _, err := os.Stat(sslkey); err == nil {
|
||||
// Both the cert and key must be present to use them, or do not use either
|
||||
settings["sslcert"] = sslcert
|
||||
settings["sslkey"] = sslkey
|
||||
}
|
||||
}
|
||||
sslrootcert := filepath.Join(user.HomeDir, ".postgresql", "root.crt")
|
||||
if _, err := os.Stat(sslrootcert); err == nil {
|
||||
settings["sslrootcert"] = sslrootcert
|
||||
}
|
||||
}
|
||||
|
||||
settings["target_session_attrs"] = "any"
|
||||
|
||||
settings["min_read_buffer_size"] = "8192"
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
// defaultHost attempts to mimic libpq's default host. libpq uses the default unix socket location on *nix and localhost
|
||||
// on Windows. The default socket location is compiled into libpq. Since pgx does not have access to that default it
|
||||
// checks the existence of common locations.
|
||||
func defaultHost() string {
|
||||
candidatePaths := []string{
|
||||
"/var/run/postgresql", // Debian
|
||||
"/private/tmp", // OSX - homebrew
|
||||
"/tmp", // standard PostgreSQL
|
||||
}
|
||||
|
||||
for _, path := range candidatePaths {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
return "localhost"
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package pgconn
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func defaultSettings() map[string]string {
|
||||
settings := make(map[string]string)
|
||||
|
||||
settings["host"] = defaultHost()
|
||||
settings["port"] = "5432"
|
||||
|
||||
// Default to the OS user name. Purposely ignoring err getting user name from
|
||||
// OS. The client application will simply have to specify the user in that
|
||||
// case (which they typically will be doing anyway).
|
||||
user, err := user.Current()
|
||||
appData := os.Getenv("APPDATA")
|
||||
if err == nil {
|
||||
// Windows gives us the username here as `DOMAIN\user` or `LOCALPCNAME\user`,
|
||||
// but the libpq default is just the `user` portion, so we strip off the first part.
|
||||
username := user.Username
|
||||
if strings.Contains(username, "\\") {
|
||||
username = username[strings.LastIndex(username, "\\")+1:]
|
||||
}
|
||||
|
||||
settings["user"] = username
|
||||
settings["passfile"] = filepath.Join(appData, "postgresql", "pgpass.conf")
|
||||
settings["servicefile"] = filepath.Join(user.HomeDir, ".pg_service.conf")
|
||||
sslcert := filepath.Join(appData, "postgresql", "postgresql.crt")
|
||||
sslkey := filepath.Join(appData, "postgresql", "postgresql.key")
|
||||
if _, err := os.Stat(sslcert); err == nil {
|
||||
if _, err := os.Stat(sslkey); err == nil {
|
||||
// Both the cert and key must be present to use them, or do not use either
|
||||
settings["sslcert"] = sslcert
|
||||
settings["sslkey"] = sslkey
|
||||
}
|
||||
}
|
||||
sslrootcert := filepath.Join(appData, "postgresql", "root.crt")
|
||||
if _, err := os.Stat(sslrootcert); err == nil {
|
||||
settings["sslrootcert"] = sslrootcert
|
||||
}
|
||||
}
|
||||
|
||||
settings["target_session_attrs"] = "any"
|
||||
|
||||
settings["min_read_buffer_size"] = "8192"
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
// defaultHost attempts to mimic libpq's default host. libpq uses the default unix socket location on *nix and localhost
|
||||
// on Windows. The default socket location is compiled into libpq. Since pgx does not have access to that default it
|
||||
// checks the existence of common locations.
|
||||
func defaultHost() string {
|
||||
return "localhost"
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// Package pgconn is a low-level PostgreSQL database driver.
|
||||
/*
|
||||
pgconn provides lower level access to a PostgreSQL connection than a database/sql or pgx connection. It operates at
|
||||
nearly the same level is the C library libpq.
|
||||
|
||||
Establishing a Connection
|
||||
|
||||
Use Connect to establish a connection. It accepts a connection string in URL or DSN and will read the environment for
|
||||
libpq style environment variables.
|
||||
|
||||
Executing a Query
|
||||
|
||||
ExecParams and ExecPrepared execute a single query. They return readers that iterate over each row. The Read method
|
||||
reads all rows into memory.
|
||||
|
||||
Executing Multiple Queries in a Single Round Trip
|
||||
|
||||
Exec and ExecBatch can execute multiple queries in a single round trip. They return readers that iterate over each query
|
||||
result. The ReadAll method reads all query results into memory.
|
||||
|
||||
Context Support
|
||||
|
||||
All potentially blocking operations take a context.Context. If a context is canceled while the method is in progress the
|
||||
method immediately returns. In most circumstances, this will close the underlying connection.
|
||||
|
||||
The CancelRequest method may be used to request the PostgreSQL server cancel an in-progress query without forcing the
|
||||
client to abort.
|
||||
*/
|
||||
package pgconn
|
|
@ -1,238 +0,0 @@
|
|||
package pgconn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
|
||||
func SafeToRetry(err error) bool {
|
||||
if e, ok := err.(interface{ SafeToRetry() bool }); ok {
|
||||
return e.SafeToRetry()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Timeout checks if err was was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
|
||||
// context.Canceled, context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
|
||||
func Timeout(err error) bool {
|
||||
var timeoutErr *errTimeout
|
||||
return errors.As(err, &timeoutErr)
|
||||
}
|
||||
|
||||
// PgError represents an error reported by the PostgreSQL server. See
|
||||
// http://www.postgresql.org/docs/11/static/protocol-error-fields.html for
|
||||
// detailed field description.
|
||||
type PgError struct {
|
||||
Severity string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
}
|
||||
|
||||
func (pe *PgError) Error() string {
|
||||
return pe.Severity + ": " + pe.Message + " (SQLSTATE " + pe.Code + ")"
|
||||
}
|
||||
|
||||
// SQLState returns the SQLState of the error.
|
||||
func (pe *PgError) SQLState() string {
|
||||
return pe.Code
|
||||
}
|
||||
|
||||
type connectError struct {
|
||||
config *Config
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *connectError) Error() string {
|
||||
sb := &strings.Builder{}
|
||||
fmt.Fprintf(sb, "failed to connect to `host=%s user=%s database=%s`: %s", e.config.Host, e.config.User, e.config.Database, e.msg)
|
||||
if e.err != nil {
|
||||
fmt.Fprintf(sb, " (%s)", e.err.Error())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *connectError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type connLockError struct {
|
||||
status string
|
||||
}
|
||||
|
||||
func (e *connLockError) SafeToRetry() bool {
|
||||
return true // a lock failure by definition happens before the connection is used.
|
||||
}
|
||||
|
||||
func (e *connLockError) Error() string {
|
||||
return e.status
|
||||
}
|
||||
|
||||
type parseConfigError struct {
|
||||
connString string
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *parseConfigError) Error() string {
|
||||
connString := redactPW(e.connString)
|
||||
if e.err == nil {
|
||||
return fmt.Sprintf("cannot parse `%s`: %s", connString, e.msg)
|
||||
}
|
||||
return fmt.Sprintf("cannot parse `%s`: %s (%s)", connString, e.msg, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *parseConfigError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// preferContextOverNetTimeoutError returns ctx.Err() if ctx.Err() is present and err is a net.Error with Timeout() ==
|
||||
// true. Otherwise returns err.
|
||||
func preferContextOverNetTimeoutError(ctx context.Context, err error) error {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() && ctx.Err() != nil {
|
||||
return &errTimeout{err: ctx.Err()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type pgconnError struct {
|
||||
msg string
|
||||
err error
|
||||
safeToRetry bool
|
||||
}
|
||||
|
||||
func (e *pgconnError) Error() string {
|
||||
if e.msg == "" {
|
||||
return e.err.Error()
|
||||
}
|
||||
if e.err == nil {
|
||||
return e.msg
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", e.msg, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *pgconnError) SafeToRetry() bool {
|
||||
return e.safeToRetry
|
||||
}
|
||||
|
||||
func (e *pgconnError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is
|
||||
// context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true.
|
||||
type errTimeout struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *errTimeout) Error() string {
|
||||
return fmt.Sprintf("timeout: %s", e.err.Error())
|
||||
}
|
||||
|
||||
func (e *errTimeout) SafeToRetry() bool {
|
||||
return SafeToRetry(e.err)
|
||||
}
|
||||
|
||||
func (e *errTimeout) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type contextAlreadyDoneError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *contextAlreadyDoneError) Error() string {
|
||||
return fmt.Sprintf("context already done: %s", e.err.Error())
|
||||
}
|
||||
|
||||
func (e *contextAlreadyDoneError) SafeToRetry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *contextAlreadyDoneError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`.
|
||||
func newContextAlreadyDoneError(ctx context.Context) (err error) {
|
||||
return &errTimeout{&contextAlreadyDoneError{err: ctx.Err()}}
|
||||
}
|
||||
|
||||
type writeError struct {
|
||||
err error
|
||||
safeToRetry bool
|
||||
}
|
||||
|
||||
func (e *writeError) Error() string {
|
||||
return fmt.Sprintf("write failed: %s", e.err.Error())
|
||||
}
|
||||
|
||||
func (e *writeError) SafeToRetry() bool {
|
||||
return e.safeToRetry
|
||||
}
|
||||
|
||||
func (e *writeError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func redactPW(connString string) string {
|
||||
if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
|
||||
if u, err := url.Parse(connString); err == nil {
|
||||
return redactURL(u)
|
||||
}
|
||||
}
|
||||
quotedDSN := regexp.MustCompile(`password='[^']*'`)
|
||||
connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
plainDSN := regexp.MustCompile(`password=[^ ]*`)
|
||||
connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
brokenURL := regexp.MustCompile(`:[^:@]+?@`)
|
||||
connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@")
|
||||
return connString
|
||||
}
|
||||
|
||||
func redactURL(u *url.URL) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
if _, pwSet := u.User.Password(); pwSet {
|
||||
u.User = url.UserPassword(u.User.Username(), "xxxxx")
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
type NotPreferredError struct {
|
||||
err error
|
||||
safeToRetry bool
|
||||
}
|
||||
|
||||
func (e *NotPreferredError) Error() string {
|
||||
return fmt.Sprintf("standby server not found: %s", e.err.Error())
|
||||
}
|
||||
|
||||
func (e *NotPreferredError) SafeToRetry() bool {
|
||||
return e.safeToRetry
|
||||
}
|
||||
|
||||
func (e *NotPreferredError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package ctxwatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ContextWatcher watches a context and performs an action when the context is canceled. It can watch one context at a
|
||||
// time.
|
||||
type ContextWatcher struct {
|
||||
onCancel func()
|
||||
onUnwatchAfterCancel func()
|
||||
unwatchChan chan struct{}
|
||||
|
||||
lock sync.Mutex
|
||||
watchInProgress bool
|
||||
onCancelWasCalled bool
|
||||
}
|
||||
|
||||
// NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled.
|
||||
// OnUnwatchAfterCancel will be called when Unwatch is called and the watched context had already been canceled and
|
||||
// onCancel called.
|
||||
func NewContextWatcher(onCancel func(), onUnwatchAfterCancel func()) *ContextWatcher {
|
||||
cw := &ContextWatcher{
|
||||
onCancel: onCancel,
|
||||
onUnwatchAfterCancel: onUnwatchAfterCancel,
|
||||
unwatchChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
return cw
|
||||
}
|
||||
|
||||
// Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called.
|
||||
func (cw *ContextWatcher) Watch(ctx context.Context) {
|
||||
cw.lock.Lock()
|
||||
defer cw.lock.Unlock()
|
||||
|
||||
if cw.watchInProgress {
|
||||
panic("Watch already in progress")
|
||||
}
|
||||
|
||||
cw.onCancelWasCalled = false
|
||||
|
||||
if ctx.Done() != nil {
|
||||
cw.watchInProgress = true
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cw.onCancel()
|
||||
cw.onCancelWasCalled = true
|
||||
<-cw.unwatchChan
|
||||
case <-cw.unwatchChan:
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
cw.watchInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
// Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was
|
||||
// called then onUnwatchAfterCancel will also be called.
|
||||
func (cw *ContextWatcher) Unwatch() {
|
||||
cw.lock.Lock()
|
||||
defer cw.lock.Unlock()
|
||||
|
||||
if cw.watchInProgress {
|
||||
cw.unwatchChan <- struct{}{}
|
||||
if cw.onCancelWasCalled {
|
||||
cw.onUnwatchAfterCancel()
|
||||
}
|
||||
cw.watchInProgress = false
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package pgconn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgproto3/v2"
|
||||
)
|
||||
|
||||
// NewGSSFunc creates a GSS authentication provider, for use with
|
||||
// RegisterGSSProvider.
|
||||
type NewGSSFunc func() (GSS, error)
|
||||
|
||||
var newGSS NewGSSFunc
|
||||
|
||||
// RegisterGSSProvider registers a GSS authentication provider. For example, if
|
||||
// you need to use Kerberos to authenticate with your server, add this to your
|
||||
// main package:
|
||||
//
|
||||
// import "github.com/otan/gopgkrb5"
|
||||
//
|
||||
// func init() {
|
||||
// pgconn.RegisterGSSProvider(func() (pgconn.GSS, error) { return gopgkrb5.NewGSS() })
|
||||
// }
|
||||
func RegisterGSSProvider(newGSSArg NewGSSFunc) {
|
||||
newGSS = newGSSArg
|
||||
}
|
||||
|
||||
// GSS provides GSSAPI authentication (e.g., Kerberos).
|
||||
type GSS interface {
|
||||
GetInitToken(host string, service string) ([]byte, error)
|
||||
GetInitTokenFromSPN(spn string) ([]byte, error)
|
||||
Continue(inToken []byte) (done bool, outToken []byte, err error)
|
||||
}
|
||||
|
||||
func (c *PgConn) gssAuth() error {
|
||||
if newGSS == nil {
|
||||
return errors.New("kerberos error: no GSSAPI provider registered, see https://github.com/otan/gopgkrb5")
|
||||
}
|
||||
cli, err := newGSS()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var nextData []byte
|
||||
if c.config.KerberosSpn != "" {
|
||||
// Use the supplied SPN if provided.
|
||||
nextData, err = cli.GetInitTokenFromSPN(c.config.KerberosSpn)
|
||||
} else {
|
||||
// Allow the kerberos service name to be overridden
|
||||
service := "postgres"
|
||||
if c.config.KerberosSrvName != "" {
|
||||
service = c.config.KerberosSrvName
|
||||
}
|
||||
nextData, err = cli.GetInitToken(c.config.Host, service)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
gssResponse := &pgproto3.GSSResponse{
|
||||
Data: nextData,
|
||||
}
|
||||
_, err = c.conn.Write(gssResponse.Encode(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := c.rxGSSContinue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var done bool
|
||||
done, nextData, err = cli.Continue(resp.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PgConn) rxGSSContinue() (*pgproto3.AuthenticationGSSContinue, error) {
|
||||
msg, err := c.receiveMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch m := msg.(type) {
|
||||
case *pgproto3.AuthenticationGSSContinue:
|
||||
return m, nil
|
||||
case *pgproto3.ErrorResponse:
|
||||
return nil, ErrorResponseToPgError(m)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("expected AuthenticationGSSContinue message but received unexpected message %T", msg)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,11 +0,0 @@
|
|||
[![](https://godoc.org/github.com/jackc/pgio?status.svg)](https://godoc.org/github.com/jackc/pgio)
|
||||
[![Build Status](https://travis-ci.org/jackc/pgio.svg)](https://travis-ci.org/jackc/pgio)
|
||||
|
||||
# pgio
|
||||
|
||||
Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.
|
||||
|
||||
pgio provides functions for appending integers to a []byte while doing byte
|
||||
order conversion.
|
||||
|
||||
Extracted from original implementation in https://github.com/jackc/pgx.
|
|
@ -1,6 +0,0 @@
|
|||
// Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.
|
||||
/*
|
||||
pgio provides functions for appending integers to a []byte while doing byte
|
||||
order conversion.
|
||||
*/
|
||||
package pgio
|
|
@ -1,40 +0,0 @@
|
|||
package pgio
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
func AppendUint16(buf []byte, n uint16) []byte {
|
||||
wp := len(buf)
|
||||
buf = append(buf, 0, 0)
|
||||
binary.BigEndian.PutUint16(buf[wp:], n)
|
||||
return buf
|
||||
}
|
||||
|
||||
func AppendUint32(buf []byte, n uint32) []byte {
|
||||
wp := len(buf)
|
||||
buf = append(buf, 0, 0, 0, 0)
|
||||
binary.BigEndian.PutUint32(buf[wp:], n)
|
||||
return buf
|
||||
}
|
||||
|
||||
func AppendUint64(buf []byte, n uint64) []byte {
|
||||
wp := len(buf)
|
||||
buf = append(buf, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
binary.BigEndian.PutUint64(buf[wp:], n)
|
||||
return buf
|
||||
}
|
||||
|
||||
func AppendInt16(buf []byte, n int16) []byte {
|
||||
return AppendUint16(buf, uint16(n))
|
||||
}
|
||||
|
||||
func AppendInt32(buf []byte, n int32) []byte {
|
||||
return AppendUint32(buf, uint32(n))
|
||||
}
|
||||
|
||||
func AppendInt64(buf []byte, n int64) []byte {
|
||||
return AppendUint64(buf, uint64(n))
|
||||
}
|
||||
|
||||
func SetInt32(buf []byte, n int32) {
|
||||
binary.BigEndian.PutUint32(buf, uint32(n))
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,18 +0,0 @@
|
|||
[![](https://godoc.org/github.com/jackc/pgproto3?status.svg)](https://godoc.org/github.com/jackc/pgproto3)
|
||||
[![Build Status](https://travis-ci.org/jackc/pgproto3.svg)](https://travis-ci.org/jackc/pgproto3)
|
||||
|
||||
---
|
||||
|
||||
This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository.
|
||||
|
||||
---
|
||||
|
||||
# pgproto3
|
||||
|
||||
Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
|
||||
pgproto3 can be used as a foundation for PostgreSQL drivers, proxies, mock servers, load balancers and more.
|
||||
|
||||
See example/pgfortune for a playful example of a fake PostgreSQL server.
|
||||
|
||||
Extracted from original implementation in https://github.com/jackc/pgx.
|
|
@ -1,52 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationCleartextPassword is a message sent from the backend indicating that a clear-text password is required.
|
||||
type AuthenticationCleartextPassword struct {
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationCleartextPassword) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationCleartextPassword) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationCleartextPassword) Decode(src []byte) error {
|
||||
if len(src) != 4 {
|
||||
return errors.New("bad authentication message size")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeCleartextPassword {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationCleartextPassword) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeCleartextPassword)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src AuthenticationCleartextPassword) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "AuthenticationCleartextPassword",
|
||||
})
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type AuthenticationGSS struct{}
|
||||
|
||||
func (a *AuthenticationGSS) Backend() {}
|
||||
|
||||
func (a *AuthenticationGSS) AuthenticationResponse() {}
|
||||
|
||||
func (a *AuthenticationGSS) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("authentication message too short")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeGSS {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 4)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSS)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data []byte
|
||||
}{
|
||||
Type: "AuthenticationGSS",
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Type string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type AuthenticationGSSContinue struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) Backend() {}
|
||||
|
||||
func (a *AuthenticationGSSContinue) AuthenticationResponse() {}
|
||||
|
||||
func (a *AuthenticationGSSContinue) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("authentication message too short")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeGSSCont {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
a.Data = src[4:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, int32(len(a.Data))+8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSSCont)
|
||||
dst = append(dst, a.Data...)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data []byte
|
||||
}{
|
||||
Type: "AuthenticationGSSContinue",
|
||||
Data: a.Data,
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Type string
|
||||
Data []byte
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Data = msg.Data
|
||||
return nil
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationMD5Password is a message sent from the backend indicating that an MD5 hashed password is required.
|
||||
type AuthenticationMD5Password struct {
|
||||
Salt [4]byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationMD5Password) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationMD5Password) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationMD5Password) Decode(src []byte) error {
|
||||
if len(src) != 8 {
|
||||
return errors.New("bad authentication message size")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeMD5Password {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
copy(dst.Salt[:], src[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationMD5Password) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 12)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeMD5Password)
|
||||
dst = append(dst, src.Salt[:]...)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src AuthenticationMD5Password) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Salt [4]byte
|
||||
}{
|
||||
Type: "AuthenticationMD5Password",
|
||||
Salt: src.Salt,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *AuthenticationMD5Password) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Type string
|
||||
Salt [4]byte
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Salt = msg.Salt
|
||||
return nil
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationOk is a message sent from the backend indicating that authentication was successful.
|
||||
type AuthenticationOk struct {
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationOk) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationOk) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationOk) Decode(src []byte) error {
|
||||
if len(src) != 4 {
|
||||
return errors.New("bad authentication message size")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeOk {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationOk) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeOk)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src AuthenticationOk) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "AuthenticationOK",
|
||||
})
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationSASL is a message sent from the backend indicating that SASL authentication is required.
|
||||
type AuthenticationSASL struct {
|
||||
AuthMechanisms []string
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationSASL) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationSASL) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationSASL) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("authentication message too short")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeSASL {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
authMechanisms := src[4:]
|
||||
for len(authMechanisms) > 1 {
|
||||
idx := bytes.IndexByte(authMechanisms, 0)
|
||||
if idx > 0 {
|
||||
dst.AuthMechanisms = append(dst.AuthMechanisms, string(authMechanisms[:idx]))
|
||||
authMechanisms = authMechanisms[idx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASL) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASL)
|
||||
|
||||
for _, s := range src.AuthMechanisms {
|
||||
dst = append(dst, []byte(s)...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src AuthenticationSASL) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
AuthMechanisms []string
|
||||
}{
|
||||
Type: "AuthenticationSASL",
|
||||
AuthMechanisms: src.AuthMechanisms,
|
||||
})
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationSASLContinue is a message sent from the backend containing a SASL challenge.
|
||||
type AuthenticationSASLContinue struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationSASLContinue) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationSASLContinue) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationSASLContinue) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("authentication message too short")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeSASLContinue {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
dst.Data = src[4:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASLContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASLContinue)
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src AuthenticationSASLContinue) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data string
|
||||
}{
|
||||
Type: "AuthenticationSASLContinue",
|
||||
Data: string(src.Data),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *AuthenticationSASLContinue) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Data string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Data = []byte(msg.Data)
|
||||
return nil
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// AuthenticationSASLFinal is a message sent from the backend indicating a SASL authentication has completed.
|
||||
type AuthenticationSASLFinal struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*AuthenticationSASLFinal) Backend() {}
|
||||
|
||||
// Backend identifies this message as an authentication response.
|
||||
func (*AuthenticationSASLFinal) AuthenticationResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *AuthenticationSASLFinal) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("authentication message too short")
|
||||
}
|
||||
|
||||
authType := binary.BigEndian.Uint32(src)
|
||||
|
||||
if authType != AuthTypeSASLFinal {
|
||||
return errors.New("bad auth type")
|
||||
}
|
||||
|
||||
dst.Data = src[4:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASLFinal) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASLFinal)
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (src AuthenticationSASLFinal) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data string
|
||||
}{
|
||||
Type: "AuthenticationSASLFinal",
|
||||
Data: string(src.Data),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *AuthenticationSASLFinal) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Data string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Data = []byte(msg.Data)
|
||||
return nil
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Backend acts as a server for the PostgreSQL wire protocol version 3.
|
||||
type Backend struct {
|
||||
cr ChunkReader
|
||||
w io.Writer
|
||||
|
||||
// Frontend message flyweights
|
||||
bind Bind
|
||||
cancelRequest CancelRequest
|
||||
_close Close
|
||||
copyFail CopyFail
|
||||
copyData CopyData
|
||||
copyDone CopyDone
|
||||
describe Describe
|
||||
execute Execute
|
||||
flush Flush
|
||||
functionCall FunctionCall
|
||||
gssEncRequest GSSEncRequest
|
||||
parse Parse
|
||||
query Query
|
||||
sslRequest SSLRequest
|
||||
startupMessage StartupMessage
|
||||
sync Sync
|
||||
terminate Terminate
|
||||
|
||||
bodyLen int
|
||||
msgType byte
|
||||
partialMsg bool
|
||||
authType uint32
|
||||
}
|
||||
|
||||
const (
|
||||
minStartupPacketLen = 4 // minStartupPacketLen is a single 32-bit int version or code.
|
||||
maxStartupPacketLen = 10000 // maxStartupPacketLen is MAX_STARTUP_PACKET_LENGTH from PG source.
|
||||
)
|
||||
|
||||
// NewBackend creates a new Backend.
|
||||
func NewBackend(cr ChunkReader, w io.Writer) *Backend {
|
||||
return &Backend{cr: cr, w: w}
|
||||
}
|
||||
|
||||
// Send sends a message to the frontend.
|
||||
func (b *Backend) Send(msg BackendMessage) error {
|
||||
_, err := b.w.Write(msg.Encode(nil))
|
||||
return err
|
||||
}
|
||||
|
||||
// ReceiveStartupMessage receives the initial connection message. This method is used of the normal Receive method
|
||||
// because the initial connection message is "special" and does not include the message type as the first byte. This
|
||||
// will return either a StartupMessage, SSLRequest, GSSEncRequest, or CancelRequest.
|
||||
func (b *Backend) ReceiveStartupMessage() (FrontendMessage, error) {
|
||||
buf, err := b.cr.Next(4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgSize := int(binary.BigEndian.Uint32(buf) - 4)
|
||||
|
||||
if msgSize < minStartupPacketLen || msgSize > maxStartupPacketLen {
|
||||
return nil, fmt.Errorf("invalid length of startup packet: %d", msgSize)
|
||||
}
|
||||
|
||||
buf, err = b.cr.Next(msgSize)
|
||||
if err != nil {
|
||||
return nil, translateEOFtoErrUnexpectedEOF(err)
|
||||
}
|
||||
|
||||
code := binary.BigEndian.Uint32(buf)
|
||||
|
||||
switch code {
|
||||
case ProtocolVersionNumber:
|
||||
err = b.startupMessage.Decode(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b.startupMessage, nil
|
||||
case sslRequestNumber:
|
||||
err = b.sslRequest.Decode(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b.sslRequest, nil
|
||||
case cancelRequestCode:
|
||||
err = b.cancelRequest.Decode(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b.cancelRequest, nil
|
||||
case gssEncReqNumber:
|
||||
err = b.gssEncRequest.Decode(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b.gssEncRequest, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown startup message code: %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
// Receive receives a message from the frontend. The returned message is only valid until the next call to Receive.
|
||||
func (b *Backend) Receive() (FrontendMessage, error) {
|
||||
if !b.partialMsg {
|
||||
header, err := b.cr.Next(5)
|
||||
if err != nil {
|
||||
return nil, translateEOFtoErrUnexpectedEOF(err)
|
||||
}
|
||||
|
||||
b.msgType = header[0]
|
||||
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
|
||||
b.partialMsg = true
|
||||
if b.bodyLen < 0 {
|
||||
return nil, errors.New("invalid message with negative body length received")
|
||||
}
|
||||
}
|
||||
|
||||
var msg FrontendMessage
|
||||
switch b.msgType {
|
||||
case 'B':
|
||||
msg = &b.bind
|
||||
case 'C':
|
||||
msg = &b._close
|
||||
case 'D':
|
||||
msg = &b.describe
|
||||
case 'E':
|
||||
msg = &b.execute
|
||||
case 'F':
|
||||
msg = &b.functionCall
|
||||
case 'f':
|
||||
msg = &b.copyFail
|
||||
case 'd':
|
||||
msg = &b.copyData
|
||||
case 'c':
|
||||
msg = &b.copyDone
|
||||
case 'H':
|
||||
msg = &b.flush
|
||||
case 'P':
|
||||
msg = &b.parse
|
||||
case 'p':
|
||||
switch b.authType {
|
||||
case AuthTypeSASL:
|
||||
msg = &SASLInitialResponse{}
|
||||
case AuthTypeSASLContinue:
|
||||
msg = &SASLResponse{}
|
||||
case AuthTypeSASLFinal:
|
||||
msg = &SASLResponse{}
|
||||
case AuthTypeGSS, AuthTypeGSSCont:
|
||||
msg = &GSSResponse{}
|
||||
case AuthTypeCleartextPassword, AuthTypeMD5Password:
|
||||
fallthrough
|
||||
default:
|
||||
// to maintain backwards compatability
|
||||
msg = &PasswordMessage{}
|
||||
}
|
||||
case 'Q':
|
||||
msg = &b.query
|
||||
case 'S':
|
||||
msg = &b.sync
|
||||
case 'X':
|
||||
msg = &b.terminate
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown message type: %c", b.msgType)
|
||||
}
|
||||
|
||||
msgBody, err := b.cr.Next(b.bodyLen)
|
||||
if err != nil {
|
||||
return nil, translateEOFtoErrUnexpectedEOF(err)
|
||||
}
|
||||
|
||||
b.partialMsg = false
|
||||
|
||||
err = msg.Decode(msgBody)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// SetAuthType sets the authentication type in the backend.
|
||||
// Since multiple message types can start with 'p', SetAuthType allows
|
||||
// contextual identification of FrontendMessages. For example, in the
|
||||
// PG message flow documentation for PasswordMessage:
|
||||
//
|
||||
// Byte1('p')
|
||||
//
|
||||
// Identifies the message as a password response. Note that this is also used for
|
||||
// GSSAPI, SSPI and SASL response messages. The exact message type can be deduced from
|
||||
// the context.
|
||||
//
|
||||
// Since the Frontend does not know about the state of a backend, it is important
|
||||
// to call SetAuthType() after an authentication request is received by the Frontend.
|
||||
func (b *Backend) SetAuthType(authType uint32) error {
|
||||
switch authType {
|
||||
case AuthTypeOk,
|
||||
AuthTypeCleartextPassword,
|
||||
AuthTypeMD5Password,
|
||||
AuthTypeSCMCreds,
|
||||
AuthTypeGSS,
|
||||
AuthTypeGSSCont,
|
||||
AuthTypeSSPI,
|
||||
AuthTypeSASL,
|
||||
AuthTypeSASLContinue,
|
||||
AuthTypeSASLFinal:
|
||||
b.authType = authType
|
||||
default:
|
||||
return fmt.Errorf("authType not recognized: %d", authType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type BackendKeyData struct {
|
||||
ProcessID uint32
|
||||
SecretKey uint32
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*BackendKeyData) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *BackendKeyData) Decode(src []byte) error {
|
||||
if len(src) != 8 {
|
||||
return &invalidMessageLenErr{messageType: "BackendKeyData", expectedLen: 8, actualLen: len(src)}
|
||||
}
|
||||
|
||||
dst.ProcessID = binary.BigEndian.Uint32(src[:4])
|
||||
dst.SecretKey = binary.BigEndian.Uint32(src[4:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *BackendKeyData) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'K')
|
||||
dst = pgio.AppendUint32(dst, 12)
|
||||
dst = pgio.AppendUint32(dst, src.ProcessID)
|
||||
dst = pgio.AppendUint32(dst, src.SecretKey)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src BackendKeyData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ProcessID uint32
|
||||
SecretKey uint32
|
||||
}{
|
||||
Type: "BackendKeyData",
|
||||
ProcessID: src.ProcessID,
|
||||
SecretKey: src.SecretKey,
|
||||
})
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type BigEndianBuf [8]byte
|
||||
|
||||
func (b BigEndianBuf) Int16(n int16) []byte {
|
||||
buf := b[0:2]
|
||||
binary.BigEndian.PutUint16(buf, uint16(n))
|
||||
return buf
|
||||
}
|
||||
|
||||
func (b BigEndianBuf) Uint16(n uint16) []byte {
|
||||
buf := b[0:2]
|
||||
binary.BigEndian.PutUint16(buf, n)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (b BigEndianBuf) Int32(n int32) []byte {
|
||||
buf := b[0:4]
|
||||
binary.BigEndian.PutUint32(buf, uint32(n))
|
||||
return buf
|
||||
}
|
||||
|
||||
func (b BigEndianBuf) Uint32(n uint32) []byte {
|
||||
buf := b[0:4]
|
||||
binary.BigEndian.PutUint32(buf, n)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (b BigEndianBuf) Int64(n int64) []byte {
|
||||
buf := b[0:8]
|
||||
binary.BigEndian.PutUint64(buf, uint64(n))
|
||||
return buf
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Bind struct {
|
||||
DestinationPortal string
|
||||
PreparedStatement string
|
||||
ParameterFormatCodes []int16
|
||||
Parameters [][]byte
|
||||
ResultFormatCodes []int16
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Bind) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Bind) Decode(src []byte) error {
|
||||
*dst = Bind{}
|
||||
|
||||
idx := bytes.IndexByte(src, 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
dst.DestinationPortal = string(src[:idx])
|
||||
rp := idx + 1
|
||||
|
||||
idx = bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
dst.PreparedStatement = string(src[rp : rp+idx])
|
||||
rp += idx + 1
|
||||
|
||||
if len(src[rp:]) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
parameterFormatCodeCount := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
|
||||
if parameterFormatCodeCount > 0 {
|
||||
dst.ParameterFormatCodes = make([]int16, parameterFormatCodeCount)
|
||||
|
||||
if len(src[rp:]) < len(dst.ParameterFormatCodes)*2 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
for i := 0; i < parameterFormatCodeCount; i++ {
|
||||
dst.ParameterFormatCodes[i] = int16(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
}
|
||||
}
|
||||
|
||||
if len(src[rp:]) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
parameterCount := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
|
||||
if parameterCount > 0 {
|
||||
dst.Parameters = make([][]byte, parameterCount)
|
||||
|
||||
for i := 0; i < parameterCount; i++ {
|
||||
if len(src[rp:]) < 4 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
|
||||
msgSize := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
// null
|
||||
if msgSize == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(src[rp:]) < msgSize {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
|
||||
dst.Parameters[i] = src[rp : rp+msgSize]
|
||||
rp += msgSize
|
||||
}
|
||||
}
|
||||
|
||||
if len(src[rp:]) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
resultFormatCodeCount := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
|
||||
dst.ResultFormatCodes = make([]int16, resultFormatCodeCount)
|
||||
if len(src[rp:]) < len(dst.ResultFormatCodes)*2 {
|
||||
return &invalidMessageFormatErr{messageType: "Bind"}
|
||||
}
|
||||
for i := 0; i < resultFormatCodeCount; i++ {
|
||||
dst.ResultFormatCodes[i] = int16(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Bind) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'B')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.DestinationPortal...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.PreparedStatement...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes)))
|
||||
for _, fc := range src.ParameterFormatCodes {
|
||||
dst = pgio.AppendInt16(dst, fc)
|
||||
}
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Parameters)))
|
||||
for _, p := range src.Parameters {
|
||||
if p == nil {
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
continue
|
||||
}
|
||||
|
||||
dst = pgio.AppendInt32(dst, int32(len(p)))
|
||||
dst = append(dst, p...)
|
||||
}
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes)))
|
||||
for _, fc := range src.ResultFormatCodes {
|
||||
dst = pgio.AppendInt16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Bind) MarshalJSON() ([]byte, error) {
|
||||
formattedParameters := make([]map[string]string, len(src.Parameters))
|
||||
for i, p := range src.Parameters {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
textFormat := true
|
||||
if len(src.ParameterFormatCodes) == 1 {
|
||||
textFormat = src.ParameterFormatCodes[0] == 0
|
||||
} else if len(src.ParameterFormatCodes) > 1 {
|
||||
textFormat = src.ParameterFormatCodes[i] == 0
|
||||
}
|
||||
|
||||
if textFormat {
|
||||
formattedParameters[i] = map[string]string{"text": string(p)}
|
||||
} else {
|
||||
formattedParameters[i] = map[string]string{"binary": hex.EncodeToString(p)}
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
DestinationPortal string
|
||||
PreparedStatement string
|
||||
ParameterFormatCodes []int16
|
||||
Parameters []map[string]string
|
||||
ResultFormatCodes []int16
|
||||
}{
|
||||
Type: "Bind",
|
||||
DestinationPortal: src.DestinationPortal,
|
||||
PreparedStatement: src.PreparedStatement,
|
||||
ParameterFormatCodes: src.ParameterFormatCodes,
|
||||
Parameters: formattedParameters,
|
||||
ResultFormatCodes: src.ResultFormatCodes,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *Bind) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
DestinationPortal string
|
||||
PreparedStatement string
|
||||
ParameterFormatCodes []int16
|
||||
Parameters []map[string]string
|
||||
ResultFormatCodes []int16
|
||||
}
|
||||
err := json.Unmarshal(data, &msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.DestinationPortal = msg.DestinationPortal
|
||||
dst.PreparedStatement = msg.PreparedStatement
|
||||
dst.ParameterFormatCodes = msg.ParameterFormatCodes
|
||||
dst.Parameters = make([][]byte, len(msg.Parameters))
|
||||
dst.ResultFormatCodes = msg.ResultFormatCodes
|
||||
for n, parameter := range msg.Parameters {
|
||||
dst.Parameters[n], err = getValueFromJSON(parameter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get param %d: %w", n, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type BindComplete struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*BindComplete) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *BindComplete) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "BindComplete", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *BindComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '2', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src BindComplete) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "BindComplete",
|
||||
})
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
const cancelRequestCode = 80877102
|
||||
|
||||
type CancelRequest struct {
|
||||
ProcessID uint32
|
||||
SecretKey uint32
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*CancelRequest) Frontend() {}
|
||||
|
||||
func (dst *CancelRequest) Decode(src []byte) error {
|
||||
if len(src) != 12 {
|
||||
return errors.New("bad cancel request size")
|
||||
}
|
||||
|
||||
requestCode := binary.BigEndian.Uint32(src)
|
||||
|
||||
if requestCode != cancelRequestCode {
|
||||
return errors.New("bad cancel request code")
|
||||
}
|
||||
|
||||
dst.ProcessID = binary.BigEndian.Uint32(src[4:])
|
||||
dst.SecretKey = binary.BigEndian.Uint32(src[8:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *CancelRequest) Encode(dst []byte) []byte {
|
||||
dst = pgio.AppendInt32(dst, 16)
|
||||
dst = pgio.AppendInt32(dst, cancelRequestCode)
|
||||
dst = pgio.AppendUint32(dst, src.ProcessID)
|
||||
dst = pgio.AppendUint32(dst, src.SecretKey)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CancelRequest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ProcessID uint32
|
||||
SecretKey uint32
|
||||
}{
|
||||
Type: "CancelRequest",
|
||||
ProcessID: src.ProcessID,
|
||||
SecretKey: src.SecretKey,
|
||||
})
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/jackc/chunkreader/v2"
|
||||
)
|
||||
|
||||
// ChunkReader is an interface to decouple github.com/jackc/chunkreader from this package.
|
||||
type ChunkReader interface {
|
||||
// Next returns buf filled with the next n bytes. If an error (including a partial read) occurs,
|
||||
// buf must be nil. Next must preserve any partially read data. Next must not reuse buf.
|
||||
Next(n int) (buf []byte, err error)
|
||||
}
|
||||
|
||||
// NewChunkReader creates and returns a new default ChunkReader.
|
||||
func NewChunkReader(r io.Reader) ChunkReader {
|
||||
return chunkreader.New(r)
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Close struct {
|
||||
ObjectType byte // 'S' = prepared statement, 'P' = portal
|
||||
Name string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Close) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Close) Decode(src []byte) error {
|
||||
if len(src) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Close"}
|
||||
}
|
||||
|
||||
dst.ObjectType = src[0]
|
||||
rp := 1
|
||||
|
||||
idx := bytes.IndexByte(src[rp:], 0)
|
||||
if idx != len(src[rp:])-1 {
|
||||
return &invalidMessageFormatErr{messageType: "Close"}
|
||||
}
|
||||
|
||||
dst.Name = string(src[rp : len(src)-1])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Close) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'C')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.ObjectType)
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Close) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ObjectType string
|
||||
Name string
|
||||
}{
|
||||
Type: "Close",
|
||||
ObjectType: string(src.ObjectType),
|
||||
Name: src.Name,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *Close) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
ObjectType string
|
||||
Name string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(msg.ObjectType) != 1 {
|
||||
return errors.New("invalid length for Close.ObjectType")
|
||||
}
|
||||
|
||||
dst.ObjectType = byte(msg.ObjectType[0])
|
||||
dst.Name = msg.Name
|
||||
return nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type CloseComplete struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CloseComplete) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CloseComplete) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "CloseComplete", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CloseComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '3', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CloseComplete) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "CloseComplete",
|
||||
})
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CommandComplete struct {
|
||||
CommandTag []byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CommandComplete) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CommandComplete) Decode(src []byte) error {
|
||||
idx := bytes.IndexByte(src, 0)
|
||||
if idx != len(src)-1 {
|
||||
return &invalidMessageFormatErr{messageType: "CommandComplete"}
|
||||
}
|
||||
|
||||
dst.CommandTag = src[:idx]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CommandComplete) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'C')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.CommandTag...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CommandComplete) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
CommandTag string
|
||||
}{
|
||||
Type: "CommandComplete",
|
||||
CommandTag: string(src.CommandTag),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *CommandComplete) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
CommandTag string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.CommandTag = []byte(msg.CommandTag)
|
||||
return nil
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CopyBothResponse struct {
|
||||
OverallFormat byte
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CopyBothResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyBothResponse) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
if buf.Len() < 3 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyBothResponse"}
|
||||
}
|
||||
|
||||
overallFormat := buf.Next(1)[0]
|
||||
|
||||
columnCount := int(binary.BigEndian.Uint16(buf.Next(2)))
|
||||
if buf.Len() != columnCount*2 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyBothResponse"}
|
||||
}
|
||||
|
||||
columnFormatCodes := make([]uint16, columnCount)
|
||||
for i := 0; i < columnCount; i++ {
|
||||
columnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))
|
||||
}
|
||||
|
||||
*dst = CopyBothResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyBothResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'W')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = append(dst, src.OverallFormat)
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyBothResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ColumnFormatCodes []uint16
|
||||
}{
|
||||
Type: "CopyBothResponse",
|
||||
ColumnFormatCodes: src.ColumnFormatCodes,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *CopyBothResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
OverallFormat string
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(msg.OverallFormat) != 1 {
|
||||
return errors.New("invalid length for CopyBothResponse.OverallFormat")
|
||||
}
|
||||
|
||||
dst.OverallFormat = msg.OverallFormat[0]
|
||||
dst.ColumnFormatCodes = msg.ColumnFormatCodes
|
||||
return nil
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CopyData struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CopyData) Backend() {}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*CopyData) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyData) Decode(src []byte) error {
|
||||
dst.Data = src
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyData) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'd')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))
|
||||
dst = append(dst, src.Data...)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data string
|
||||
}{
|
||||
Type: "CopyData",
|
||||
Data: hex.EncodeToString(src.Data),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *CopyData) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Data string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Data = []byte(msg.Data)
|
||||
return nil
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type CopyDone struct {
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CopyDone) Backend() {}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*CopyDone) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyDone) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "CopyDone", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyDone) Encode(dst []byte) []byte {
|
||||
return append(dst, 'c', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyDone) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "CopyDone",
|
||||
})
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CopyFail struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*CopyFail) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyFail) Decode(src []byte) error {
|
||||
idx := bytes.IndexByte(src, 0)
|
||||
if idx != len(src)-1 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyFail"}
|
||||
}
|
||||
|
||||
dst.Message = string(src[:idx])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyFail) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'f')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.Message...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyFail) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Message string
|
||||
}{
|
||||
Type: "CopyFail",
|
||||
Message: src.Message,
|
||||
})
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CopyInResponse struct {
|
||||
OverallFormat byte
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*CopyInResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyInResponse) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
if buf.Len() < 3 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyInResponse"}
|
||||
}
|
||||
|
||||
overallFormat := buf.Next(1)[0]
|
||||
|
||||
columnCount := int(binary.BigEndian.Uint16(buf.Next(2)))
|
||||
if buf.Len() != columnCount*2 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyInResponse"}
|
||||
}
|
||||
|
||||
columnFormatCodes := make([]uint16, columnCount)
|
||||
for i := 0; i < columnCount; i++ {
|
||||
columnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))
|
||||
}
|
||||
|
||||
*dst = CopyInResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyInResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'G')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.OverallFormat)
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyInResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ColumnFormatCodes []uint16
|
||||
}{
|
||||
Type: "CopyInResponse",
|
||||
ColumnFormatCodes: src.ColumnFormatCodes,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *CopyInResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
OverallFormat string
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(msg.OverallFormat) != 1 {
|
||||
return errors.New("invalid length for CopyInResponse.OverallFormat")
|
||||
}
|
||||
|
||||
dst.OverallFormat = msg.OverallFormat[0]
|
||||
dst.ColumnFormatCodes = msg.ColumnFormatCodes
|
||||
return nil
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CopyOutResponse struct {
|
||||
OverallFormat byte
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
|
||||
func (*CopyOutResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *CopyOutResponse) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
if buf.Len() < 3 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyOutResponse"}
|
||||
}
|
||||
|
||||
overallFormat := buf.Next(1)[0]
|
||||
|
||||
columnCount := int(binary.BigEndian.Uint16(buf.Next(2)))
|
||||
if buf.Len() != columnCount*2 {
|
||||
return &invalidMessageFormatErr{messageType: "CopyOutResponse"}
|
||||
}
|
||||
|
||||
columnFormatCodes := make([]uint16, columnCount)
|
||||
for i := 0; i < columnCount; i++ {
|
||||
columnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))
|
||||
}
|
||||
|
||||
*dst = CopyOutResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyOutResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'H')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.OverallFormat)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src CopyOutResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ColumnFormatCodes []uint16
|
||||
}{
|
||||
Type: "CopyOutResponse",
|
||||
ColumnFormatCodes: src.ColumnFormatCodes,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *CopyOutResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
OverallFormat string
|
||||
ColumnFormatCodes []uint16
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(msg.OverallFormat) != 1 {
|
||||
return errors.New("invalid length for CopyOutResponse.OverallFormat")
|
||||
}
|
||||
|
||||
dst.OverallFormat = msg.OverallFormat[0]
|
||||
dst.ColumnFormatCodes = msg.ColumnFormatCodes
|
||||
return nil
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type DataRow struct {
|
||||
Values [][]byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*DataRow) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *DataRow) Decode(src []byte) error {
|
||||
if len(src) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "DataRow"}
|
||||
}
|
||||
rp := 0
|
||||
fieldCount := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
|
||||
// If the capacity of the values slice is too small OR substantially too
|
||||
// large reallocate. This is too avoid one row with many columns from
|
||||
// permanently allocating memory.
|
||||
if cap(dst.Values) < fieldCount || cap(dst.Values)-fieldCount > 32 {
|
||||
newCap := 32
|
||||
if newCap < fieldCount {
|
||||
newCap = fieldCount
|
||||
}
|
||||
dst.Values = make([][]byte, fieldCount, newCap)
|
||||
} else {
|
||||
dst.Values = dst.Values[:fieldCount]
|
||||
}
|
||||
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
if len(src[rp:]) < 4 {
|
||||
return &invalidMessageFormatErr{messageType: "DataRow"}
|
||||
}
|
||||
|
||||
msgSize := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
// null
|
||||
if msgSize == -1 {
|
||||
dst.Values[i] = nil
|
||||
} else {
|
||||
if len(src[rp:]) < msgSize {
|
||||
return &invalidMessageFormatErr{messageType: "DataRow"}
|
||||
}
|
||||
|
||||
dst.Values[i] = src[rp : rp+msgSize : rp+msgSize]
|
||||
rp += msgSize
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *DataRow) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'D')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Values)))
|
||||
for _, v := range src.Values {
|
||||
if v == nil {
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
continue
|
||||
}
|
||||
|
||||
dst = pgio.AppendInt32(dst, int32(len(v)))
|
||||
dst = append(dst, v...)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src DataRow) MarshalJSON() ([]byte, error) {
|
||||
formattedValues := make([]map[string]string, len(src.Values))
|
||||
for i, v := range src.Values {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var hasNonPrintable bool
|
||||
for _, b := range v {
|
||||
if b < 32 {
|
||||
hasNonPrintable = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasNonPrintable {
|
||||
formattedValues[i] = map[string]string{"binary": hex.EncodeToString(v)}
|
||||
} else {
|
||||
formattedValues[i] = map[string]string{"text": string(v)}
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Values []map[string]string
|
||||
}{
|
||||
Type: "DataRow",
|
||||
Values: formattedValues,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *DataRow) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Values []map[string]string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Values = make([][]byte, len(msg.Values))
|
||||
for n, parameter := range msg.Values {
|
||||
var err error
|
||||
dst.Values[n], err = getValueFromJSON(parameter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Describe struct {
|
||||
ObjectType byte // 'S' = prepared statement, 'P' = portal
|
||||
Name string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Describe) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Describe) Decode(src []byte) error {
|
||||
if len(src) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Describe"}
|
||||
}
|
||||
|
||||
dst.ObjectType = src[0]
|
||||
rp := 1
|
||||
|
||||
idx := bytes.IndexByte(src[rp:], 0)
|
||||
if idx != len(src[rp:])-1 {
|
||||
return &invalidMessageFormatErr{messageType: "Describe"}
|
||||
}
|
||||
|
||||
dst.Name = string(src[rp : len(src)-1])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Describe) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'D')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.ObjectType)
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Describe) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ObjectType string
|
||||
Name string
|
||||
}{
|
||||
Type: "Describe",
|
||||
ObjectType: string(src.ObjectType),
|
||||
Name: src.Name,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *Describe) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
ObjectType string
|
||||
Name string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(msg.ObjectType) != 1 {
|
||||
return errors.New("invalid length for Describe.ObjectType")
|
||||
}
|
||||
|
||||
dst.ObjectType = byte(msg.ObjectType[0])
|
||||
dst.Name = msg.Name
|
||||
return nil
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
// Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
//
|
||||
// See https://www.postgresql.org/docs/current/protocol-message-formats.html for meanings of the different messages.
|
||||
package pgproto3
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type EmptyQueryResponse struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*EmptyQueryResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *EmptyQueryResponse) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "EmptyQueryResponse", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *EmptyQueryResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, 'I', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src EmptyQueryResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "EmptyQueryResponse",
|
||||
})
|
||||
}
|
|
@ -1,334 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
Severity string
|
||||
SeverityUnlocalized string // only in 9.6 and greater
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
|
||||
UnknownFields map[byte]string
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*ErrorResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *ErrorResponse) Decode(src []byte) error {
|
||||
*dst = ErrorResponse{}
|
||||
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
for {
|
||||
k, err := buf.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if k == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
vb, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := string(vb[:len(vb)-1])
|
||||
|
||||
switch k {
|
||||
case 'S':
|
||||
dst.Severity = v
|
||||
case 'V':
|
||||
dst.SeverityUnlocalized = v
|
||||
case 'C':
|
||||
dst.Code = v
|
||||
case 'M':
|
||||
dst.Message = v
|
||||
case 'D':
|
||||
dst.Detail = v
|
||||
case 'H':
|
||||
dst.Hint = v
|
||||
case 'P':
|
||||
s := v
|
||||
n, _ := strconv.ParseInt(s, 10, 32)
|
||||
dst.Position = int32(n)
|
||||
case 'p':
|
||||
s := v
|
||||
n, _ := strconv.ParseInt(s, 10, 32)
|
||||
dst.InternalPosition = int32(n)
|
||||
case 'q':
|
||||
dst.InternalQuery = v
|
||||
case 'W':
|
||||
dst.Where = v
|
||||
case 's':
|
||||
dst.SchemaName = v
|
||||
case 't':
|
||||
dst.TableName = v
|
||||
case 'c':
|
||||
dst.ColumnName = v
|
||||
case 'd':
|
||||
dst.DataTypeName = v
|
||||
case 'n':
|
||||
dst.ConstraintName = v
|
||||
case 'F':
|
||||
dst.File = v
|
||||
case 'L':
|
||||
s := v
|
||||
n, _ := strconv.ParseInt(s, 10, 32)
|
||||
dst.Line = int32(n)
|
||||
case 'R':
|
||||
dst.Routine = v
|
||||
|
||||
default:
|
||||
if dst.UnknownFields == nil {
|
||||
dst.UnknownFields = make(map[byte]string)
|
||||
}
|
||||
dst.UnknownFields[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ErrorResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, src.marshalBinary('E')...)
|
||||
}
|
||||
|
||||
func (src *ErrorResponse) marshalBinary(typeByte byte) []byte {
|
||||
var bigEndian BigEndianBuf
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(typeByte)
|
||||
buf.Write(bigEndian.Uint32(0))
|
||||
|
||||
if src.Severity != "" {
|
||||
buf.WriteByte('S')
|
||||
buf.WriteString(src.Severity)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.SeverityUnlocalized != "" {
|
||||
buf.WriteByte('V')
|
||||
buf.WriteString(src.SeverityUnlocalized)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Code != "" {
|
||||
buf.WriteByte('C')
|
||||
buf.WriteString(src.Code)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Message != "" {
|
||||
buf.WriteByte('M')
|
||||
buf.WriteString(src.Message)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Detail != "" {
|
||||
buf.WriteByte('D')
|
||||
buf.WriteString(src.Detail)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Hint != "" {
|
||||
buf.WriteByte('H')
|
||||
buf.WriteString(src.Hint)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Position != 0 {
|
||||
buf.WriteByte('P')
|
||||
buf.WriteString(strconv.Itoa(int(src.Position)))
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.InternalPosition != 0 {
|
||||
buf.WriteByte('p')
|
||||
buf.WriteString(strconv.Itoa(int(src.InternalPosition)))
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.InternalQuery != "" {
|
||||
buf.WriteByte('q')
|
||||
buf.WriteString(src.InternalQuery)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Where != "" {
|
||||
buf.WriteByte('W')
|
||||
buf.WriteString(src.Where)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.SchemaName != "" {
|
||||
buf.WriteByte('s')
|
||||
buf.WriteString(src.SchemaName)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.TableName != "" {
|
||||
buf.WriteByte('t')
|
||||
buf.WriteString(src.TableName)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.ColumnName != "" {
|
||||
buf.WriteByte('c')
|
||||
buf.WriteString(src.ColumnName)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.DataTypeName != "" {
|
||||
buf.WriteByte('d')
|
||||
buf.WriteString(src.DataTypeName)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.ConstraintName != "" {
|
||||
buf.WriteByte('n')
|
||||
buf.WriteString(src.ConstraintName)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.File != "" {
|
||||
buf.WriteByte('F')
|
||||
buf.WriteString(src.File)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Line != 0 {
|
||||
buf.WriteByte('L')
|
||||
buf.WriteString(strconv.Itoa(int(src.Line)))
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if src.Routine != "" {
|
||||
buf.WriteByte('R')
|
||||
buf.WriteString(src.Routine)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
|
||||
for k, v := range src.UnknownFields {
|
||||
buf.WriteByte(k)
|
||||
buf.WriteByte(0)
|
||||
buf.WriteString(v)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
|
||||
buf.WriteByte(0)
|
||||
|
||||
binary.BigEndian.PutUint32(buf.Bytes()[1:5], uint32(buf.Len()-1))
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src ErrorResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Severity string
|
||||
SeverityUnlocalized string // only in 9.6 and greater
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
|
||||
UnknownFields map[byte]string
|
||||
}{
|
||||
Type: "ErrorResponse",
|
||||
Severity: src.Severity,
|
||||
SeverityUnlocalized: src.SeverityUnlocalized,
|
||||
Code: src.Code,
|
||||
Message: src.Message,
|
||||
Detail: src.Detail,
|
||||
Hint: src.Hint,
|
||||
Position: src.Position,
|
||||
InternalPosition: src.InternalPosition,
|
||||
InternalQuery: src.InternalQuery,
|
||||
Where: src.Where,
|
||||
SchemaName: src.SchemaName,
|
||||
TableName: src.TableName,
|
||||
ColumnName: src.ColumnName,
|
||||
DataTypeName: src.DataTypeName,
|
||||
ConstraintName: src.ConstraintName,
|
||||
File: src.File,
|
||||
Line: src.Line,
|
||||
Routine: src.Routine,
|
||||
UnknownFields: src.UnknownFields,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *ErrorResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Type string
|
||||
Severity string
|
||||
SeverityUnlocalized string // only in 9.6 and greater
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
|
||||
UnknownFields map[byte]string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Severity = msg.Severity
|
||||
dst.SeverityUnlocalized = msg.SeverityUnlocalized
|
||||
dst.Code = msg.Code
|
||||
dst.Message = msg.Message
|
||||
dst.Detail = msg.Detail
|
||||
dst.Hint = msg.Hint
|
||||
dst.Position = msg.Position
|
||||
dst.InternalPosition = msg.InternalPosition
|
||||
dst.InternalQuery = msg.InternalQuery
|
||||
dst.Where = msg.Where
|
||||
dst.SchemaName = msg.SchemaName
|
||||
dst.TableName = msg.TableName
|
||||
dst.ColumnName = msg.ColumnName
|
||||
dst.DataTypeName = msg.DataTypeName
|
||||
dst.ConstraintName = msg.ConstraintName
|
||||
dst.File = msg.File
|
||||
dst.Line = msg.Line
|
||||
dst.Routine = msg.Routine
|
||||
|
||||
dst.UnknownFields = msg.UnknownFields
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Execute struct {
|
||||
Portal string
|
||||
MaxRows uint32
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Execute) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Execute) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
b, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Portal = string(b[:len(b)-1])
|
||||
|
||||
if buf.Len() < 4 {
|
||||
return &invalidMessageFormatErr{messageType: "Execute"}
|
||||
}
|
||||
dst.MaxRows = binary.BigEndian.Uint32(buf.Next(4))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Execute) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'E')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.Portal...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendUint32(dst, src.MaxRows)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Execute) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Portal string
|
||||
MaxRows uint32
|
||||
}{
|
||||
Type: "Execute",
|
||||
Portal: src.Portal,
|
||||
MaxRows: src.MaxRows,
|
||||
})
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Flush struct{}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Flush) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Flush) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "Flush", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Flush) Encode(dst []byte) []byte {
|
||||
return append(dst, 'H', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Flush) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "Flush",
|
||||
})
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Frontend acts as a client for the PostgreSQL wire protocol version 3.
|
||||
type Frontend struct {
|
||||
cr ChunkReader
|
||||
w io.Writer
|
||||
|
||||
// Backend message flyweights
|
||||
authenticationOk AuthenticationOk
|
||||
authenticationCleartextPassword AuthenticationCleartextPassword
|
||||
authenticationMD5Password AuthenticationMD5Password
|
||||
authenticationGSS AuthenticationGSS
|
||||
authenticationGSSContinue AuthenticationGSSContinue
|
||||
authenticationSASL AuthenticationSASL
|
||||
authenticationSASLContinue AuthenticationSASLContinue
|
||||
authenticationSASLFinal AuthenticationSASLFinal
|
||||
backendKeyData BackendKeyData
|
||||
bindComplete BindComplete
|
||||
closeComplete CloseComplete
|
||||
commandComplete CommandComplete
|
||||
copyBothResponse CopyBothResponse
|
||||
copyData CopyData
|
||||
copyInResponse CopyInResponse
|
||||
copyOutResponse CopyOutResponse
|
||||
copyDone CopyDone
|
||||
dataRow DataRow
|
||||
emptyQueryResponse EmptyQueryResponse
|
||||
errorResponse ErrorResponse
|
||||
functionCallResponse FunctionCallResponse
|
||||
noData NoData
|
||||
noticeResponse NoticeResponse
|
||||
notificationResponse NotificationResponse
|
||||
parameterDescription ParameterDescription
|
||||
parameterStatus ParameterStatus
|
||||
parseComplete ParseComplete
|
||||
readyForQuery ReadyForQuery
|
||||
rowDescription RowDescription
|
||||
portalSuspended PortalSuspended
|
||||
|
||||
bodyLen int
|
||||
msgType byte
|
||||
partialMsg bool
|
||||
authType uint32
|
||||
}
|
||||
|
||||
// NewFrontend creates a new Frontend.
|
||||
func NewFrontend(cr ChunkReader, w io.Writer) *Frontend {
|
||||
return &Frontend{cr: cr, w: w}
|
||||
}
|
||||
|
||||
// Send sends a message to the backend.
|
||||
func (f *Frontend) Send(msg FrontendMessage) error {
|
||||
_, err := f.w.Write(msg.Encode(nil))
|
||||
return err
|
||||
}
|
||||
|
||||
func translateEOFtoErrUnexpectedEOF(err error) error {
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive receives a message from the backend. The returned message is only valid until the next call to Receive.
|
||||
func (f *Frontend) Receive() (BackendMessage, error) {
|
||||
if !f.partialMsg {
|
||||
header, err := f.cr.Next(5)
|
||||
if err != nil {
|
||||
return nil, translateEOFtoErrUnexpectedEOF(err)
|
||||
}
|
||||
|
||||
f.msgType = header[0]
|
||||
f.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
|
||||
f.partialMsg = true
|
||||
if f.bodyLen < 0 {
|
||||
return nil, errors.New("invalid message with negative body length received")
|
||||
}
|
||||
}
|
||||
|
||||
msgBody, err := f.cr.Next(f.bodyLen)
|
||||
if err != nil {
|
||||
return nil, translateEOFtoErrUnexpectedEOF(err)
|
||||
}
|
||||
|
||||
f.partialMsg = false
|
||||
|
||||
var msg BackendMessage
|
||||
switch f.msgType {
|
||||
case '1':
|
||||
msg = &f.parseComplete
|
||||
case '2':
|
||||
msg = &f.bindComplete
|
||||
case '3':
|
||||
msg = &f.closeComplete
|
||||
case 'A':
|
||||
msg = &f.notificationResponse
|
||||
case 'c':
|
||||
msg = &f.copyDone
|
||||
case 'C':
|
||||
msg = &f.commandComplete
|
||||
case 'd':
|
||||
msg = &f.copyData
|
||||
case 'D':
|
||||
msg = &f.dataRow
|
||||
case 'E':
|
||||
msg = &f.errorResponse
|
||||
case 'G':
|
||||
msg = &f.copyInResponse
|
||||
case 'H':
|
||||
msg = &f.copyOutResponse
|
||||
case 'I':
|
||||
msg = &f.emptyQueryResponse
|
||||
case 'K':
|
||||
msg = &f.backendKeyData
|
||||
case 'n':
|
||||
msg = &f.noData
|
||||
case 'N':
|
||||
msg = &f.noticeResponse
|
||||
case 'R':
|
||||
var err error
|
||||
msg, err = f.findAuthenticationMessageType(msgBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 's':
|
||||
msg = &f.portalSuspended
|
||||
case 'S':
|
||||
msg = &f.parameterStatus
|
||||
case 't':
|
||||
msg = &f.parameterDescription
|
||||
case 'T':
|
||||
msg = &f.rowDescription
|
||||
case 'V':
|
||||
msg = &f.functionCallResponse
|
||||
case 'W':
|
||||
msg = &f.copyBothResponse
|
||||
case 'Z':
|
||||
msg = &f.readyForQuery
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown message type: %c", f.msgType)
|
||||
}
|
||||
|
||||
err = msg.Decode(msgBody)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// Authentication message type constants.
|
||||
// See src/include/libpq/pqcomm.h for all
|
||||
// constants.
|
||||
const (
|
||||
AuthTypeOk = 0
|
||||
AuthTypeCleartextPassword = 3
|
||||
AuthTypeMD5Password = 5
|
||||
AuthTypeSCMCreds = 6
|
||||
AuthTypeGSS = 7
|
||||
AuthTypeGSSCont = 8
|
||||
AuthTypeSSPI = 9
|
||||
AuthTypeSASL = 10
|
||||
AuthTypeSASLContinue = 11
|
||||
AuthTypeSASLFinal = 12
|
||||
)
|
||||
|
||||
func (f *Frontend) findAuthenticationMessageType(src []byte) (BackendMessage, error) {
|
||||
if len(src) < 4 {
|
||||
return nil, errors.New("authentication message too short")
|
||||
}
|
||||
f.authType = binary.BigEndian.Uint32(src[:4])
|
||||
|
||||
switch f.authType {
|
||||
case AuthTypeOk:
|
||||
return &f.authenticationOk, nil
|
||||
case AuthTypeCleartextPassword:
|
||||
return &f.authenticationCleartextPassword, nil
|
||||
case AuthTypeMD5Password:
|
||||
return &f.authenticationMD5Password, nil
|
||||
case AuthTypeSCMCreds:
|
||||
return nil, errors.New("AuthTypeSCMCreds is unimplemented")
|
||||
case AuthTypeGSS:
|
||||
return &f.authenticationGSS, nil
|
||||
case AuthTypeGSSCont:
|
||||
return &f.authenticationGSSContinue, nil
|
||||
case AuthTypeSSPI:
|
||||
return nil, errors.New("AuthTypeSSPI is unimplemented")
|
||||
case AuthTypeSASL:
|
||||
return &f.authenticationSASL, nil
|
||||
case AuthTypeSASLContinue:
|
||||
return &f.authenticationSASLContinue, nil
|
||||
case AuthTypeSASLFinal:
|
||||
return &f.authenticationSASLFinal, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown authentication type: %d", f.authType)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthType returns the authType used in the current state of the frontend.
|
||||
// See SetAuthType for more information.
|
||||
func (f *Frontend) GetAuthType() uint32 {
|
||||
return f.authType
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type FunctionCall struct {
|
||||
Function uint32
|
||||
ArgFormatCodes []uint16
|
||||
Arguments [][]byte
|
||||
ResultFormatCode uint16
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*FunctionCall) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *FunctionCall) Decode(src []byte) error {
|
||||
*dst = FunctionCall{}
|
||||
rp := 0
|
||||
// Specifies the object ID of the function to call.
|
||||
dst.Function = binary.BigEndian.Uint32(src[rp:])
|
||||
rp += 4
|
||||
// The number of argument format codes that follow (denoted C below).
|
||||
// This can be zero to indicate that there are no arguments or that the arguments all use the default format (text);
|
||||
// or one, in which case the specified format code is applied to all arguments;
|
||||
// or it can equal the actual number of arguments.
|
||||
nArgumentCodes := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
argumentCodes := make([]uint16, nArgumentCodes)
|
||||
for i := 0; i < nArgumentCodes; i++ {
|
||||
// The argument format codes. Each must presently be zero (text) or one (binary).
|
||||
ac := binary.BigEndian.Uint16(src[rp:])
|
||||
if ac != 0 && ac != 1 {
|
||||
return &invalidMessageFormatErr{messageType: "FunctionCall"}
|
||||
}
|
||||
argumentCodes[i] = ac
|
||||
rp += 2
|
||||
}
|
||||
dst.ArgFormatCodes = argumentCodes
|
||||
|
||||
// Specifies the number of arguments being supplied to the function.
|
||||
nArguments := int(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
arguments := make([][]byte, nArguments)
|
||||
for i := 0; i < nArguments; i++ {
|
||||
// The length of the argument value, in bytes (this count does not include itself). Can be zero.
|
||||
// As a special case, -1 indicates a NULL argument value. No value bytes follow in the NULL case.
|
||||
argumentLength := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
if argumentLength == -1 {
|
||||
arguments[i] = nil
|
||||
} else {
|
||||
// The value of the argument, in the format indicated by the associated format code. n is the above length.
|
||||
argumentValue := src[rp : rp+argumentLength]
|
||||
rp += argumentLength
|
||||
arguments[i] = argumentValue
|
||||
}
|
||||
}
|
||||
dst.Arguments = arguments
|
||||
// The format code for the function result. Must presently be zero (text) or one (binary).
|
||||
resultFormatCode := binary.BigEndian.Uint16(src[rp:])
|
||||
if resultFormatCode != 0 && resultFormatCode != 1 {
|
||||
return &invalidMessageFormatErr{messageType: "FunctionCall"}
|
||||
}
|
||||
dst.ResultFormatCode = resultFormatCode
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *FunctionCall) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'F')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendUint32(dst, 0) // Unknown length, set it at the end
|
||||
dst = pgio.AppendUint32(dst, src.Function)
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ArgFormatCodes)))
|
||||
for _, argFormatCode := range src.ArgFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, argFormatCode)
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Arguments)))
|
||||
for _, argument := range src.Arguments {
|
||||
if argument == nil {
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
} else {
|
||||
dst = pgio.AppendInt32(dst, int32(len(argument)))
|
||||
dst = append(dst, argument...)
|
||||
}
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, src.ResultFormatCode)
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
return dst
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type FunctionCallResponse struct {
|
||||
Result []byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*FunctionCallResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *FunctionCallResponse) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return &invalidMessageFormatErr{messageType: "FunctionCallResponse"}
|
||||
}
|
||||
rp := 0
|
||||
resultSize := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if resultSize == -1 {
|
||||
dst.Result = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src[rp:]) != resultSize {
|
||||
return &invalidMessageFormatErr{messageType: "FunctionCallResponse"}
|
||||
}
|
||||
|
||||
dst.Result = src[rp:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *FunctionCallResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'V')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
if src.Result == nil {
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
} else {
|
||||
dst = pgio.AppendInt32(dst, int32(len(src.Result)))
|
||||
dst = append(dst, src.Result...)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src FunctionCallResponse) MarshalJSON() ([]byte, error) {
|
||||
var formattedValue map[string]string
|
||||
var hasNonPrintable bool
|
||||
for _, b := range src.Result {
|
||||
if b < 32 {
|
||||
hasNonPrintable = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasNonPrintable {
|
||||
formattedValue = map[string]string{"binary": hex.EncodeToString(src.Result)}
|
||||
} else {
|
||||
formattedValue = map[string]string{"text": string(src.Result)}
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Result map[string]string
|
||||
}{
|
||||
Type: "FunctionCallResponse",
|
||||
Result: formattedValue,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *FunctionCallResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
Result map[string]string
|
||||
}
|
||||
err := json.Unmarshal(data, &msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Result, err = getValueFromJSON(msg.Result)
|
||||
return err
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
const gssEncReqNumber = 80877104
|
||||
|
||||
type GSSEncRequest struct {
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*GSSEncRequest) Frontend() {}
|
||||
|
||||
func (dst *GSSEncRequest) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("gss encoding request too short")
|
||||
}
|
||||
|
||||
requestCode := binary.BigEndian.Uint32(src)
|
||||
|
||||
if requestCode != gssEncReqNumber {
|
||||
return errors.New("bad gss encoding request code")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *GSSEncRequest) Encode(dst []byte) []byte {
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendInt32(dst, gssEncReqNumber)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src GSSEncRequest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ProtocolVersion uint32
|
||||
Parameters map[string]string
|
||||
}{
|
||||
Type: "GSSEncRequest",
|
||||
})
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type GSSResponse struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (g *GSSResponse) Frontend() {}
|
||||
|
||||
func (g *GSSResponse) Decode(data []byte) error {
|
||||
g.Data = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GSSResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(g.Data)))
|
||||
dst = append(dst, g.Data...)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (g *GSSResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data []byte
|
||||
}{
|
||||
Type: "GSSResponse",
|
||||
Data: g.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (g *GSSResponse) UnmarshalJSON(data []byte) error {
|
||||
var msg struct {
|
||||
Data []byte
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Data = msg.Data
|
||||
return nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type NoData struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*NoData) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *NoData) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "NoData", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NoData) Encode(dst []byte) []byte {
|
||||
return append(dst, 'n', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src NoData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "NoData",
|
||||
})
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
type NoticeResponse ErrorResponse
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*NoticeResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *NoticeResponse) Decode(src []byte) error {
|
||||
return (*ErrorResponse)(dst).Decode(src)
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NoticeResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, (*ErrorResponse)(src).marshalBinary('N')...)
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type NotificationResponse struct {
|
||||
PID uint32
|
||||
Channel string
|
||||
Payload string
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*NotificationResponse) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *NotificationResponse) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
pid := binary.BigEndian.Uint32(buf.Next(4))
|
||||
|
||||
b, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
channel := string(b[:len(b)-1])
|
||||
|
||||
b, err = buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := string(b[:len(b)-1])
|
||||
|
||||
*dst = NotificationResponse{PID: pid, Channel: channel, Payload: payload}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NotificationResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'A')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = pgio.AppendUint32(dst, src.PID)
|
||||
dst = append(dst, src.Channel...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Payload...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src NotificationResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
PID uint32
|
||||
Channel string
|
||||
Payload string
|
||||
}{
|
||||
Type: "NotificationResponse",
|
||||
PID: src.PID,
|
||||
Channel: src.Channel,
|
||||
Payload: src.Payload,
|
||||
})
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type ParameterDescription struct {
|
||||
ParameterOIDs []uint32
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*ParameterDescription) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *ParameterDescription) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
if buf.Len() < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "ParameterDescription"}
|
||||
}
|
||||
|
||||
// Reported parameter count will be incorrect when number of args is greater than uint16
|
||||
buf.Next(2)
|
||||
// Instead infer parameter count by remaining size of message
|
||||
parameterCount := buf.Len() / 4
|
||||
|
||||
*dst = ParameterDescription{ParameterOIDs: make([]uint32, parameterCount)}
|
||||
|
||||
for i := 0; i < parameterCount; i++ {
|
||||
dst.ParameterOIDs[i] = binary.BigEndian.Uint32(buf.Next(4))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParameterDescription) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 't')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
|
||||
for _, oid := range src.ParameterOIDs {
|
||||
dst = pgio.AppendUint32(dst, oid)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src ParameterDescription) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ParameterOIDs []uint32
|
||||
}{
|
||||
Type: "ParameterDescription",
|
||||
ParameterOIDs: src.ParameterOIDs,
|
||||
})
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type ParameterStatus struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*ParameterStatus) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *ParameterStatus) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
b, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := string(b[:len(b)-1])
|
||||
|
||||
b, err = buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value := string(b[:len(b)-1])
|
||||
|
||||
*dst = ParameterStatus{Name: name, Value: value}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParameterStatus) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'S')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Value...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (ps ParameterStatus) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Name string
|
||||
Value string
|
||||
}{
|
||||
Type: "ParameterStatus",
|
||||
Name: ps.Name,
|
||||
Value: ps.Value,
|
||||
})
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Parse struct {
|
||||
Name string
|
||||
Query string
|
||||
ParameterOIDs []uint32
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Parse) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Parse) Decode(src []byte) error {
|
||||
*dst = Parse{}
|
||||
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
b, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Name = string(b[:len(b)-1])
|
||||
|
||||
b, err = buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Query = string(b[:len(b)-1])
|
||||
|
||||
if buf.Len() < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "Parse"}
|
||||
}
|
||||
parameterOIDCount := int(binary.BigEndian.Uint16(buf.Next(2)))
|
||||
|
||||
for i := 0; i < parameterOIDCount; i++ {
|
||||
if buf.Len() < 4 {
|
||||
return &invalidMessageFormatErr{messageType: "Parse"}
|
||||
}
|
||||
dst.ParameterOIDs = append(dst.ParameterOIDs, binary.BigEndian.Uint32(buf.Next(4)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Parse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'P')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Query...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
|
||||
for _, oid := range src.ParameterOIDs {
|
||||
dst = pgio.AppendUint32(dst, oid)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Parse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Name string
|
||||
Query string
|
||||
ParameterOIDs []uint32
|
||||
}{
|
||||
Type: "Parse",
|
||||
Name: src.Name,
|
||||
Query: src.Query,
|
||||
ParameterOIDs: src.ParameterOIDs,
|
||||
})
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type ParseComplete struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*ParseComplete) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *ParseComplete) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "ParseComplete", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParseComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '1', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src ParseComplete) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "ParseComplete",
|
||||
})
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type PasswordMessage struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*PasswordMessage) Frontend() {}
|
||||
|
||||
// Frontend identifies this message as an authentication response.
|
||||
func (*PasswordMessage) InitialResponse() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *PasswordMessage) Decode(src []byte) error {
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
b, err := buf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Password = string(b[:len(b)-1])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *PasswordMessage) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Password)+1))
|
||||
|
||||
dst = append(dst, src.Password...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src PasswordMessage) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Password string
|
||||
}{
|
||||
Type: "PasswordMessage",
|
||||
Password: src.Password,
|
||||
})
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Message is the interface implemented by an object that can decode and encode
|
||||
// a particular PostgreSQL message.
|
||||
type Message interface {
|
||||
// Decode is allowed and expected to retain a reference to data after
|
||||
// returning (unlike encoding.BinaryUnmarshaler).
|
||||
Decode(data []byte) error
|
||||
|
||||
// Encode appends itself to dst and returns the new buffer.
|
||||
Encode(dst []byte) []byte
|
||||
}
|
||||
|
||||
type FrontendMessage interface {
|
||||
Message
|
||||
Frontend() // no-op method to distinguish frontend from backend methods
|
||||
}
|
||||
|
||||
type BackendMessage interface {
|
||||
Message
|
||||
Backend() // no-op method to distinguish frontend from backend methods
|
||||
}
|
||||
|
||||
type AuthenticationResponseMessage interface {
|
||||
BackendMessage
|
||||
AuthenticationResponse() // no-op method to distinguish authentication responses
|
||||
}
|
||||
|
||||
type invalidMessageLenErr struct {
|
||||
messageType string
|
||||
expectedLen int
|
||||
actualLen int
|
||||
}
|
||||
|
||||
func (e *invalidMessageLenErr) Error() string {
|
||||
return fmt.Sprintf("%s body must have length of %d, but it is %d", e.messageType, e.expectedLen, e.actualLen)
|
||||
}
|
||||
|
||||
type invalidMessageFormatErr struct {
|
||||
messageType string
|
||||
}
|
||||
|
||||
func (e *invalidMessageFormatErr) Error() string {
|
||||
return fmt.Sprintf("%s body is invalid", e.messageType)
|
||||
}
|
||||
|
||||
// getValueFromJSON gets the value from a protocol message representation in JSON.
|
||||
func getValueFromJSON(v map[string]string) ([]byte, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if text, ok := v["text"]; ok {
|
||||
return []byte(text), nil
|
||||
}
|
||||
if binary, ok := v["binary"]; ok {
|
||||
return hex.DecodeString(binary)
|
||||
}
|
||||
return nil, errors.New("unknown protocol representation")
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type PortalSuspended struct{}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*PortalSuspended) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *PortalSuspended) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "PortalSuspended", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *PortalSuspended) Encode(dst []byte) []byte {
|
||||
return append(dst, 's', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src PortalSuspended) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "PortalSuspended",
|
||||
})
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
String string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Query) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Query) Decode(src []byte) error {
|
||||
i := bytes.IndexByte(src, 0)
|
||||
if i != len(src)-1 {
|
||||
return &invalidMessageFormatErr{messageType: "Query"}
|
||||
}
|
||||
|
||||
dst.String = string(src[:i])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Query) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'Q')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.String)+1))
|
||||
|
||||
dst = append(dst, src.String...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Query) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
String string
|
||||
}{
|
||||
Type: "Query",
|
||||
String: src.String,
|
||||
})
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type ReadyForQuery struct {
|
||||
TxStatus byte
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*ReadyForQuery) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *ReadyForQuery) Decode(src []byte) error {
|
||||
if len(src) != 1 {
|
||||
return &invalidMessageLenErr{messageType: "ReadyForQuery", expectedLen: 1, actualLen: len(src)}
|
||||
}
|
||||
|
||||
dst.TxStatus = src[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ReadyForQuery) Encode(dst []byte) []byte {
|
||||
return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src ReadyForQuery) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
TxStatus string
|
||||
}{
|
||||
Type: "ReadyForQuery",
|
||||
TxStatus: string(src.TxStatus),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *ReadyForQuery) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
TxStatus string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(msg.TxStatus) != 1 {
|
||||
return errors.New("invalid length for ReadyForQuery.TxStatus")
|
||||
}
|
||||
dst.TxStatus = msg.TxStatus[0]
|
||||
return nil
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
const (
|
||||
TextFormat = 0
|
||||
BinaryFormat = 1
|
||||
)
|
||||
|
||||
type FieldDescription struct {
|
||||
Name []byte
|
||||
TableOID uint32
|
||||
TableAttributeNumber uint16
|
||||
DataTypeOID uint32
|
||||
DataTypeSize int16
|
||||
TypeModifier int32
|
||||
Format int16
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (fd FieldDescription) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Name string
|
||||
TableOID uint32
|
||||
TableAttributeNumber uint16
|
||||
DataTypeOID uint32
|
||||
DataTypeSize int16
|
||||
TypeModifier int32
|
||||
Format int16
|
||||
}{
|
||||
Name: string(fd.Name),
|
||||
TableOID: fd.TableOID,
|
||||
TableAttributeNumber: fd.TableAttributeNumber,
|
||||
DataTypeOID: fd.DataTypeOID,
|
||||
DataTypeSize: fd.DataTypeSize,
|
||||
TypeModifier: fd.TypeModifier,
|
||||
Format: fd.Format,
|
||||
})
|
||||
}
|
||||
|
||||
type RowDescription struct {
|
||||
Fields []FieldDescription
|
||||
}
|
||||
|
||||
// Backend identifies this message as sendable by the PostgreSQL backend.
|
||||
func (*RowDescription) Backend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *RowDescription) Decode(src []byte) error {
|
||||
|
||||
if len(src) < 2 {
|
||||
return &invalidMessageFormatErr{messageType: "RowDescription"}
|
||||
}
|
||||
fieldCount := int(binary.BigEndian.Uint16(src))
|
||||
rp := 2
|
||||
|
||||
dst.Fields = dst.Fields[0:0]
|
||||
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
var fd FieldDescription
|
||||
|
||||
idx := bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "RowDescription"}
|
||||
}
|
||||
fd.Name = src[rp : rp+idx]
|
||||
rp += idx + 1
|
||||
|
||||
// Since buf.Next() doesn't return an error if we hit the end of the buffer
|
||||
// check Len ahead of time
|
||||
if len(src[rp:]) < 18 {
|
||||
return &invalidMessageFormatErr{messageType: "RowDescription"}
|
||||
}
|
||||
|
||||
fd.TableOID = binary.BigEndian.Uint32(src[rp:])
|
||||
rp += 4
|
||||
fd.TableAttributeNumber = binary.BigEndian.Uint16(src[rp:])
|
||||
rp += 2
|
||||
fd.DataTypeOID = binary.BigEndian.Uint32(src[rp:])
|
||||
rp += 4
|
||||
fd.DataTypeSize = int16(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
fd.TypeModifier = int32(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
fd.Format = int16(binary.BigEndian.Uint16(src[rp:]))
|
||||
rp += 2
|
||||
|
||||
dst.Fields = append(dst.Fields, fd)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *RowDescription) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'T')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Fields)))
|
||||
for _, fd := range src.Fields {
|
||||
dst = append(dst, fd.Name...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendUint32(dst, fd.TableOID)
|
||||
dst = pgio.AppendUint16(dst, fd.TableAttributeNumber)
|
||||
dst = pgio.AppendUint32(dst, fd.DataTypeOID)
|
||||
dst = pgio.AppendInt16(dst, fd.DataTypeSize)
|
||||
dst = pgio.AppendInt32(dst, fd.TypeModifier)
|
||||
dst = pgio.AppendInt16(dst, fd.Format)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src RowDescription) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Fields []FieldDescription
|
||||
}{
|
||||
Type: "RowDescription",
|
||||
Fields: src.Fields,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *RowDescription) UnmarshalJSON(data []byte) error {
|
||||
var msg struct {
|
||||
Fields []struct {
|
||||
Name string
|
||||
TableOID uint32
|
||||
TableAttributeNumber uint16
|
||||
DataTypeOID uint32
|
||||
DataTypeSize int16
|
||||
TypeModifier int32
|
||||
Format int16
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Fields = make([]FieldDescription, len(msg.Fields))
|
||||
for n, field := range msg.Fields {
|
||||
dst.Fields[n] = FieldDescription{
|
||||
Name: []byte(field.Name),
|
||||
TableOID: field.TableOID,
|
||||
TableAttributeNumber: field.TableAttributeNumber,
|
||||
DataTypeOID: field.DataTypeOID,
|
||||
DataTypeSize: field.DataTypeSize,
|
||||
TypeModifier: field.TypeModifier,
|
||||
Format: field.Format,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type SASLInitialResponse struct {
|
||||
AuthMechanism string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*SASLInitialResponse) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *SASLInitialResponse) Decode(src []byte) error {
|
||||
*dst = SASLInitialResponse{}
|
||||
|
||||
rp := 0
|
||||
|
||||
idx := bytes.IndexByte(src, 0)
|
||||
if idx < 0 {
|
||||
return errors.New("invalid SASLInitialResponse")
|
||||
}
|
||||
|
||||
dst.AuthMechanism = string(src[rp:idx])
|
||||
rp = idx + 1
|
||||
|
||||
rp += 4 // The rest of the message is data so we can just skip the size
|
||||
dst.Data = src[rp:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *SASLInitialResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = append(dst, []byte(src.AuthMechanism)...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendInt32(dst, int32(len(src.Data)))
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src SASLInitialResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
AuthMechanism string
|
||||
Data string
|
||||
}{
|
||||
Type: "SASLInitialResponse",
|
||||
AuthMechanism: src.AuthMechanism,
|
||||
Data: string(src.Data),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *SASLInitialResponse) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msg struct {
|
||||
AuthMechanism string
|
||||
Data string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.AuthMechanism = msg.AuthMechanism
|
||||
dst.Data = []byte(msg.Data)
|
||||
return nil
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type SASLResponse struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*SASLResponse) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *SASLResponse) Decode(src []byte) error {
|
||||
*dst = SASLResponse{Data: src}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *SASLResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src SASLResponse) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
Data string
|
||||
}{
|
||||
Type: "SASLResponse",
|
||||
Data: string(src.Data),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||
func (dst *SASLResponse) UnmarshalJSON(data []byte) error {
|
||||
var msg struct {
|
||||
Data string
|
||||
}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Data = []byte(msg.Data)
|
||||
return nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
const sslRequestNumber = 80877103
|
||||
|
||||
type SSLRequest struct {
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*SSLRequest) Frontend() {}
|
||||
|
||||
func (dst *SSLRequest) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("ssl request too short")
|
||||
}
|
||||
|
||||
requestCode := binary.BigEndian.Uint32(src)
|
||||
|
||||
if requestCode != sslRequestNumber {
|
||||
return errors.New("bad ssl request code")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *SSLRequest) Encode(dst []byte) []byte {
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendInt32(dst, sslRequestNumber)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src SSLRequest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ProtocolVersion uint32
|
||||
Parameters map[string]string
|
||||
}{
|
||||
Type: "SSLRequest",
|
||||
})
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
const ProtocolVersionNumber = 196608 // 3.0
|
||||
|
||||
type StartupMessage struct {
|
||||
ProtocolVersion uint32
|
||||
Parameters map[string]string
|
||||
}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*StartupMessage) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *StartupMessage) Decode(src []byte) error {
|
||||
if len(src) < 4 {
|
||||
return errors.New("startup message too short")
|
||||
}
|
||||
|
||||
dst.ProtocolVersion = binary.BigEndian.Uint32(src)
|
||||
rp := 4
|
||||
|
||||
if dst.ProtocolVersion != ProtocolVersionNumber {
|
||||
return fmt.Errorf("Bad startup message version number. Expected %d, got %d", ProtocolVersionNumber, dst.ProtocolVersion)
|
||||
}
|
||||
|
||||
dst.Parameters = make(map[string]string)
|
||||
for {
|
||||
idx := bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "StartupMesage"}
|
||||
}
|
||||
key := string(src[rp : rp+idx])
|
||||
rp += idx + 1
|
||||
|
||||
idx = bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "StartupMesage"}
|
||||
}
|
||||
value := string(src[rp : rp+idx])
|
||||
rp += idx + 1
|
||||
|
||||
dst.Parameters[key] = value
|
||||
|
||||
if len(src[rp:]) == 1 {
|
||||
if src[rp] != 0 {
|
||||
return fmt.Errorf("Bad startup message last byte. Expected 0, got %d", src[rp])
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *StartupMessage) Encode(dst []byte) []byte {
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
dst = pgio.AppendUint32(dst, src.ProtocolVersion)
|
||||
for k, v := range src.Parameters {
|
||||
dst = append(dst, k...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, v...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src StartupMessage) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
ProtocolVersion uint32
|
||||
Parameters map[string]string
|
||||
}{
|
||||
Type: "StartupMessage",
|
||||
ProtocolVersion: src.ProtocolVersion,
|
||||
Parameters: src.Parameters,
|
||||
})
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Sync struct{}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Sync) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Sync) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "Sync", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Sync) Encode(dst []byte) []byte {
|
||||
return append(dst, 'S', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Sync) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "Sync",
|
||||
})
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Terminate struct{}
|
||||
|
||||
// Frontend identifies this message as sendable by a PostgreSQL frontend.
|
||||
func (*Terminate) Frontend() {}
|
||||
|
||||
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
|
||||
// type identifier and 4 byte message length.
|
||||
func (dst *Terminate) Decode(src []byte) error {
|
||||
if len(src) != 0 {
|
||||
return &invalidMessageLenErr{messageType: "Terminate", expectedLen: 0, actualLen: len(src)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Terminate) Encode(dst []byte) []byte {
|
||||
return append(dst, 'X', 0, 0, 0, 4)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
func (src Terminate) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string
|
||||
}{
|
||||
Type: "Terminate",
|
||||
})
|
||||
}
|
|
@ -2,12 +2,14 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -24,7 +26,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
|
||||
basepb "google.golang.org/appengine/internal/base"
|
||||
logpb "google.golang.org/appengine/internal/log"
|
||||
|
@ -33,7 +34,6 @@ import (
|
|||
|
||||
const (
|
||||
apiPath = "/rpc_http"
|
||||
defaultTicketSuffix = "/default.20150612t184001.0"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -65,21 +65,22 @@ var (
|
|||
IdleConnTimeout: 90 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
defaultTicketOnce sync.Once
|
||||
defaultTicket string
|
||||
backgroundContextOnce sync.Once
|
||||
backgroundContext netcontext.Context
|
||||
)
|
||||
|
||||
func apiURL() *url.URL {
|
||||
func apiURL(ctx context.Context) *url.URL {
|
||||
host, port := "appengine.googleapis.internal", "10001"
|
||||
if h := os.Getenv("API_HOST"); h != "" {
|
||||
host = h
|
||||
}
|
||||
if hostOverride := ctx.Value(apiHostOverrideKey); hostOverride != nil {
|
||||
host = hostOverride.(string)
|
||||
}
|
||||
if p := os.Getenv("API_PORT"); p != "" {
|
||||
port = p
|
||||
}
|
||||
if portOverride := ctx.Value(apiPortOverrideKey); portOverride != nil {
|
||||
port = portOverride.(string)
|
||||
}
|
||||
return &url.URL{
|
||||
Scheme: "http",
|
||||
Host: host + ":" + port,
|
||||
|
@ -87,11 +88,16 @@ func apiURL() *url.URL {
|
|||
}
|
||||
}
|
||||
|
||||
func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
c := &context{
|
||||
// Middleware wraps an http handler so that it can make GAE API calls
|
||||
func Middleware(next http.Handler) http.Handler {
|
||||
return handleHTTPMiddleware(executeRequestSafelyMiddleware(next))
|
||||
}
|
||||
|
||||
func handleHTTPMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c := &aeContext{
|
||||
req: r,
|
||||
outHeader: w.Header(),
|
||||
apiURL: apiURL(),
|
||||
}
|
||||
r = r.WithContext(withContext(r.Context(), c))
|
||||
c.req = r
|
||||
|
@ -116,14 +122,18 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
|
||||
}
|
||||
|
||||
if logToLogservice() {
|
||||
// Start goroutine responsible for flushing app logs.
|
||||
// This is done after adding c to ctx.m (and stopped before removing it)
|
||||
// because flushing logs requires making an API call.
|
||||
go c.logFlusher(stopFlushing)
|
||||
}
|
||||
|
||||
executeRequestSafely(c, r)
|
||||
next.ServeHTTP(c, r)
|
||||
c.outHeader = nil // make sure header changes aren't respected any more
|
||||
|
||||
flushed := make(chan struct{})
|
||||
if logToLogservice() {
|
||||
stopFlushing <- 1 // any logging beyond this point will be dropped
|
||||
|
||||
// Flush any pending logs asynchronously.
|
||||
|
@ -133,7 +143,6 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
flushes++
|
||||
}
|
||||
c.pendingLogs.Unlock()
|
||||
flushed := make(chan struct{})
|
||||
go func() {
|
||||
defer close(flushed)
|
||||
// Force a log flush, because with very short requests we
|
||||
|
@ -141,6 +150,7 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
c.flushLog(true)
|
||||
}()
|
||||
w.Header().Set(logFlushHeader, strconv.Itoa(flushes))
|
||||
}
|
||||
|
||||
// Avoid nil Write call if c.Write is never called.
|
||||
if c.outCode != 0 {
|
||||
|
@ -149,20 +159,26 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
if c.outBody != nil {
|
||||
w.Write(c.outBody)
|
||||
}
|
||||
if logToLogservice() {
|
||||
// Wait for the last flush to complete before returning,
|
||||
// otherwise the security ticket will not be valid.
|
||||
<-flushed
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func executeRequestSafely(c *context, r *http.Request) {
|
||||
func executeRequestSafelyMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
c := w.(*aeContext)
|
||||
logf(c, 4, "%s", renderPanic(x)) // 4 == critical
|
||||
c.outCode = 500
|
||||
}
|
||||
}()
|
||||
|
||||
http.DefaultServeMux.ServeHTTP(c, r)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func renderPanic(x interface{}) string {
|
||||
|
@ -204,9 +220,9 @@ func renderPanic(x interface{}) string {
|
|||
return string(buf)
|
||||
}
|
||||
|
||||
// context represents the context of an in-flight HTTP request.
|
||||
// aeContext represents the aeContext of an in-flight HTTP request.
|
||||
// It implements the appengine.Context and http.ResponseWriter interfaces.
|
||||
type context struct {
|
||||
type aeContext struct {
|
||||
req *http.Request
|
||||
|
||||
outCode int
|
||||
|
@ -218,8 +234,6 @@ type context struct {
|
|||
lines []*logpb.UserAppLogLine
|
||||
flushes int
|
||||
}
|
||||
|
||||
apiURL *url.URL
|
||||
}
|
||||
|
||||
var contextKey = "holds a *context"
|
||||
|
@ -227,8 +241,8 @@ var contextKey = "holds a *context"
|
|||
// jointContext joins two contexts in a superficial way.
|
||||
// It takes values and timeouts from a base context, and only values from another context.
|
||||
type jointContext struct {
|
||||
base netcontext.Context
|
||||
valuesOnly netcontext.Context
|
||||
base context.Context
|
||||
valuesOnly context.Context
|
||||
}
|
||||
|
||||
func (c jointContext) Deadline() (time.Time, bool) {
|
||||
|
@ -252,94 +266,54 @@ func (c jointContext) Value(key interface{}) interface{} {
|
|||
|
||||
// fromContext returns the App Engine context or nil if ctx is not
|
||||
// derived from an App Engine context.
|
||||
func fromContext(ctx netcontext.Context) *context {
|
||||
c, _ := ctx.Value(&contextKey).(*context)
|
||||
func fromContext(ctx context.Context) *aeContext {
|
||||
c, _ := ctx.Value(&contextKey).(*aeContext)
|
||||
return c
|
||||
}
|
||||
|
||||
func withContext(parent netcontext.Context, c *context) netcontext.Context {
|
||||
ctx := netcontext.WithValue(parent, &contextKey, c)
|
||||
func withContext(parent context.Context, c *aeContext) context.Context {
|
||||
ctx := context.WithValue(parent, &contextKey, c)
|
||||
if ns := c.req.Header.Get(curNamespaceHeader); ns != "" {
|
||||
ctx = withNamespace(ctx, ns)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func toContext(c *context) netcontext.Context {
|
||||
return withContext(netcontext.Background(), c)
|
||||
func toContext(c *aeContext) context.Context {
|
||||
return withContext(context.Background(), c)
|
||||
}
|
||||
|
||||
func IncomingHeaders(ctx netcontext.Context) http.Header {
|
||||
func IncomingHeaders(ctx context.Context) http.Header {
|
||||
if c := fromContext(ctx); c != nil {
|
||||
return c.req.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReqContext(req *http.Request) netcontext.Context {
|
||||
func ReqContext(req *http.Request) context.Context {
|
||||
return req.Context()
|
||||
}
|
||||
|
||||
func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
|
||||
func WithContext(parent context.Context, req *http.Request) context.Context {
|
||||
return jointContext{
|
||||
base: parent,
|
||||
valuesOnly: req.Context(),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultTicket returns a ticket used for background context or dev_appserver.
|
||||
func DefaultTicket() string {
|
||||
defaultTicketOnce.Do(func() {
|
||||
if IsDevAppServer() {
|
||||
defaultTicket = "testapp" + defaultTicketSuffix
|
||||
return
|
||||
}
|
||||
appID := partitionlessAppID()
|
||||
escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
|
||||
majVersion := VersionID(nil)
|
||||
if i := strings.Index(majVersion, "."); i > 0 {
|
||||
majVersion = majVersion[:i]
|
||||
}
|
||||
defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
|
||||
})
|
||||
return defaultTicket
|
||||
}
|
||||
|
||||
func BackgroundContext() netcontext.Context {
|
||||
backgroundContextOnce.Do(func() {
|
||||
// Compute background security ticket.
|
||||
ticket := DefaultTicket()
|
||||
|
||||
c := &context{
|
||||
req: &http.Request{
|
||||
Header: http.Header{
|
||||
ticketHeader: []string{ticket},
|
||||
},
|
||||
},
|
||||
apiURL: apiURL(),
|
||||
}
|
||||
backgroundContext = toContext(c)
|
||||
|
||||
// TODO(dsymonds): Wire up the shutdown handler to do a final flush.
|
||||
go c.logFlusher(make(chan int))
|
||||
})
|
||||
|
||||
return backgroundContext
|
||||
}
|
||||
|
||||
// RegisterTestRequest registers the HTTP request req for testing, such that
|
||||
// any API calls are sent to the provided URL. It returns a closure to delete
|
||||
// the registration.
|
||||
// any API calls are sent to the provided URL.
|
||||
// It should only be used by aetest package.
|
||||
func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) {
|
||||
c := &context{
|
||||
req: req,
|
||||
apiURL: apiURL,
|
||||
}
|
||||
ctx := withContext(decorate(req.Context()), c)
|
||||
req = req.WithContext(ctx)
|
||||
c.req = req
|
||||
return req, func() {}
|
||||
func RegisterTestRequest(req *http.Request, apiURL *url.URL, appID string) *http.Request {
|
||||
ctx := req.Context()
|
||||
ctx = withAPIHostOverride(ctx, apiURL.Hostname())
|
||||
ctx = withAPIPortOverride(ctx, apiURL.Port())
|
||||
ctx = WithAppIDOverride(ctx, appID)
|
||||
|
||||
// use the unregistered request as a placeholder so that withContext can read the headers
|
||||
c := &aeContext{req: req}
|
||||
c.req = req.WithContext(withContext(ctx, c))
|
||||
return c.req
|
||||
}
|
||||
|
||||
var errTimeout = &CallError{
|
||||
|
@ -348,7 +322,7 @@ var errTimeout = &CallError{
|
|||
Timeout: true,
|
||||
}
|
||||
|
||||
func (c *context) Header() http.Header { return c.outHeader }
|
||||
func (c *aeContext) Header() http.Header { return c.outHeader }
|
||||
|
||||
// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status
|
||||
// codes do not permit a response body (nor response entity headers such as
|
||||
|
@ -365,7 +339,7 @@ func bodyAllowedForStatus(status int) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (c *context) Write(b []byte) (int, error) {
|
||||
func (c *aeContext) Write(b []byte) (int, error) {
|
||||
if c.outCode == 0 {
|
||||
c.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
@ -376,7 +350,7 @@ func (c *context) Write(b []byte) (int, error) {
|
|||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *context) WriteHeader(code int) {
|
||||
func (c *aeContext) WriteHeader(code int) {
|
||||
if c.outCode != 0 {
|
||||
logf(c, 3, "WriteHeader called multiple times on request.") // error level
|
||||
return
|
||||
|
@ -384,10 +358,11 @@ func (c *context) WriteHeader(code int) {
|
|||
c.outCode = code
|
||||
}
|
||||
|
||||
func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) {
|
||||
func post(ctx context.Context, body []byte, timeout time.Duration) (b []byte, err error) {
|
||||
apiURL := apiURL(ctx)
|
||||
hreq := &http.Request{
|
||||
Method: "POST",
|
||||
URL: c.apiURL,
|
||||
URL: apiURL,
|
||||
Header: http.Header{
|
||||
apiEndpointHeader: apiEndpointHeaderValue,
|
||||
apiMethodHeader: apiMethodHeaderValue,
|
||||
|
@ -396,14 +371,17 @@ func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error)
|
|||
},
|
||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
||||
ContentLength: int64(len(body)),
|
||||
Host: c.apiURL.Host,
|
||||
Host: apiURL.Host,
|
||||
}
|
||||
c := fromContext(ctx)
|
||||
if c != nil {
|
||||
if info := c.req.Header.Get(dapperHeader); info != "" {
|
||||
hreq.Header.Set(dapperHeader, info)
|
||||
}
|
||||
if info := c.req.Header.Get(traceHeader); info != "" {
|
||||
hreq.Header.Set(traceHeader, info)
|
||||
}
|
||||
}
|
||||
|
||||
tr := apiHTTPClient.Transport.(*http.Transport)
|
||||
|
||||
|
@ -444,7 +422,7 @@ func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error)
|
|||
return hrespBody, nil
|
||||
}
|
||||
|
||||
func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
|
||||
func Call(ctx context.Context, service, method string, in, out proto.Message) error {
|
||||
if ns := NamespaceFromContext(ctx); ns != "" {
|
||||
if fn, ok := NamespaceMods[service]; ok {
|
||||
fn(in, ns)
|
||||
|
@ -463,15 +441,11 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
}
|
||||
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
// Give a good error message rather than a panic lower down.
|
||||
return errNotAppEngineContext
|
||||
}
|
||||
|
||||
// Apply transaction modifications if we're in a transaction.
|
||||
if t := transactionFromContext(ctx); t != nil {
|
||||
if t.finished {
|
||||
return errors.New("transaction context has expired")
|
||||
return errors.New("transaction aeContext has expired")
|
||||
}
|
||||
applyTransaction(in, &t.transaction)
|
||||
}
|
||||
|
@ -487,20 +461,13 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
return err
|
||||
}
|
||||
|
||||
ticket := c.req.Header.Get(ticketHeader)
|
||||
// Use a test ticket under test environment.
|
||||
if ticket == "" {
|
||||
if appid := ctx.Value(&appIDOverrideKey); appid != nil {
|
||||
ticket = appid.(string) + defaultTicketSuffix
|
||||
}
|
||||
}
|
||||
// Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver.
|
||||
if ticket == "" {
|
||||
ticket = DefaultTicket()
|
||||
}
|
||||
ticket := ""
|
||||
if c != nil {
|
||||
ticket = c.req.Header.Get(ticketHeader)
|
||||
if dri := c.req.Header.Get(devRequestIdHeader); IsDevAppServer() && dri != "" {
|
||||
ticket = dri
|
||||
}
|
||||
}
|
||||
req := &remotepb.Request{
|
||||
ServiceName: &service,
|
||||
Method: &method,
|
||||
|
@ -512,7 +479,7 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
return err
|
||||
}
|
||||
|
||||
hrespBody, err := c.post(hreqBody, timeout)
|
||||
hrespBody, err := post(ctx, hreqBody, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -549,11 +516,11 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
return proto.Unmarshal(res.Response, out)
|
||||
}
|
||||
|
||||
func (c *context) Request() *http.Request {
|
||||
func (c *aeContext) Request() *http.Request {
|
||||
return c.req
|
||||
}
|
||||
|
||||
func (c *context) addLogLine(ll *logpb.UserAppLogLine) {
|
||||
func (c *aeContext) addLogLine(ll *logpb.UserAppLogLine) {
|
||||
// Truncate long log lines.
|
||||
// TODO(dsymonds): Check if this is still necessary.
|
||||
const lim = 8 << 10
|
||||
|
@ -575,18 +542,20 @@ var logLevelName = map[int64]string{
|
|||
4: "CRITICAL",
|
||||
}
|
||||
|
||||
func logf(c *context, level int64, format string, args ...interface{}) {
|
||||
func logf(c *aeContext, level int64, format string, args ...interface{}) {
|
||||
if c == nil {
|
||||
panic("not an App Engine context")
|
||||
panic("not an App Engine aeContext")
|
||||
}
|
||||
s := fmt.Sprintf(format, args...)
|
||||
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
|
||||
if logToLogservice() {
|
||||
c.addLogLine(&logpb.UserAppLogLine{
|
||||
TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3),
|
||||
Level: &level,
|
||||
Message: &s,
|
||||
})
|
||||
// Only duplicate log to stderr if not running on App Engine second generation
|
||||
}
|
||||
// Log to stdout if not deployed
|
||||
if !IsSecondGen() {
|
||||
log.Print(logLevelName[level] + ": " + s)
|
||||
}
|
||||
|
@ -594,7 +563,7 @@ func logf(c *context, level int64, format string, args ...interface{}) {
|
|||
|
||||
// flushLog attempts to flush any pending logs to the appserver.
|
||||
// It should not be called concurrently.
|
||||
func (c *context) flushLog(force bool) (flushed bool) {
|
||||
func (c *aeContext) flushLog(force bool) (flushed bool) {
|
||||
c.pendingLogs.Lock()
|
||||
// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious.
|
||||
n, rem := 0, 30<<20
|
||||
|
@ -655,7 +624,7 @@ const (
|
|||
forceFlushInterval = 60 * time.Second
|
||||
)
|
||||
|
||||
func (c *context) logFlusher(stop <-chan int) {
|
||||
func (c *aeContext) logFlusher(stop <-chan int) {
|
||||
lastFlush := time.Now()
|
||||
tick := time.NewTicker(flushInterval)
|
||||
for {
|
||||
|
@ -673,6 +642,12 @@ func (c *context) logFlusher(stop <-chan int) {
|
|||
}
|
||||
}
|
||||
|
||||
func ContextForTesting(req *http.Request) netcontext.Context {
|
||||
return toContext(&context{req: req})
|
||||
func ContextForTesting(req *http.Request) context.Context {
|
||||
return toContext(&aeContext{req: req})
|
||||
}
|
||||
|
||||
func logToLogservice() bool {
|
||||
// TODO: replace logservice with json structured logs to $LOG_DIR/app.log.json
|
||||
// where $LOG_DIR is /var/log in prod and some tmpdir in dev
|
||||
return os.Getenv("LOG_TO_LOGSERVICE") != "0"
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -17,20 +19,19 @@ import (
|
|||
basepb "appengine_internal/base"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var contextKey = "holds an appengine.Context"
|
||||
|
||||
// fromContext returns the App Engine context or nil if ctx is not
|
||||
// derived from an App Engine context.
|
||||
func fromContext(ctx netcontext.Context) appengine.Context {
|
||||
func fromContext(ctx context.Context) appengine.Context {
|
||||
c, _ := ctx.Value(&contextKey).(appengine.Context)
|
||||
return c
|
||||
}
|
||||
|
||||
// This is only for classic App Engine adapters.
|
||||
func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) {
|
||||
func ClassicContextFromContext(ctx context.Context) (appengine.Context, error) {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
return nil, errNotAppEngineContext
|
||||
|
@ -38,8 +39,8 @@ func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context {
|
||||
ctx := netcontext.WithValue(parent, &contextKey, c)
|
||||
func withContext(parent context.Context, c appengine.Context) context.Context {
|
||||
ctx := context.WithValue(parent, &contextKey, c)
|
||||
|
||||
s := &basepb.StringProto{}
|
||||
c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil)
|
||||
|
@ -50,7 +51,7 @@ func withContext(parent netcontext.Context, c appengine.Context) netcontext.Cont
|
|||
return ctx
|
||||
}
|
||||
|
||||
func IncomingHeaders(ctx netcontext.Context) http.Header {
|
||||
func IncomingHeaders(ctx context.Context) http.Header {
|
||||
if c := fromContext(ctx); c != nil {
|
||||
if req, ok := c.Request().(*http.Request); ok {
|
||||
return req.Header
|
||||
|
@ -59,11 +60,11 @@ func IncomingHeaders(ctx netcontext.Context) http.Header {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ReqContext(req *http.Request) netcontext.Context {
|
||||
return WithContext(netcontext.Background(), req)
|
||||
func ReqContext(req *http.Request) context.Context {
|
||||
return WithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
|
||||
func WithContext(parent context.Context, req *http.Request) context.Context {
|
||||
c := appengine.NewContext(req)
|
||||
return withContext(parent, c)
|
||||
}
|
||||
|
@ -83,11 +84,11 @@ func (t *testingContext) Call(service, method string, _, _ appengine_internal.Pr
|
|||
}
|
||||
func (t *testingContext) Request() interface{} { return t.req }
|
||||
|
||||
func ContextForTesting(req *http.Request) netcontext.Context {
|
||||
return withContext(netcontext.Background(), &testingContext{req: req})
|
||||
func ContextForTesting(req *http.Request) context.Context {
|
||||
return withContext(context.Background(), &testingContext{req: req})
|
||||
}
|
||||
|
||||
func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
|
||||
func Call(ctx context.Context, service, method string, in, out proto.Message) error {
|
||||
if ns := NamespaceFromContext(ctx); ns != "" {
|
||||
if fn, ok := NamespaceMods[service]; ok {
|
||||
fn(in, ns)
|
||||
|
@ -144,8 +145,8 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
return err
|
||||
}
|
||||
|
||||
func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
panic("handleHTTP called; this should be impossible")
|
||||
func Middleware(next http.Handler) http.Handler {
|
||||
panic("Middleware called; this should be impossible")
|
||||
}
|
||||
|
||||
func logf(c appengine.Context, level int64, format string, args ...interface{}) {
|
||||
|
|
|
@ -5,20 +5,26 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type ctxKey string
|
||||
|
||||
func (c ctxKey) String() string {
|
||||
return "appengine context key: " + string(c)
|
||||
}
|
||||
|
||||
var errNotAppEngineContext = errors.New("not an App Engine context")
|
||||
|
||||
type CallOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error
|
||||
type CallOverrideFunc func(ctx context.Context, service, method string, in, out proto.Message) error
|
||||
|
||||
var callOverrideKey = "holds []CallOverrideFunc"
|
||||
|
||||
func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Context {
|
||||
func WithCallOverride(ctx context.Context, f CallOverrideFunc) context.Context {
|
||||
// We avoid appending to any existing call override
|
||||
// so we don't risk overwriting a popped stack below.
|
||||
var cofs []CallOverrideFunc
|
||||
|
@ -26,10 +32,10 @@ func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Con
|
|||
cofs = append(cofs, uf...)
|
||||
}
|
||||
cofs = append(cofs, f)
|
||||
return netcontext.WithValue(ctx, &callOverrideKey, cofs)
|
||||
return context.WithValue(ctx, &callOverrideKey, cofs)
|
||||
}
|
||||
|
||||
func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netcontext.Context, bool) {
|
||||
func callOverrideFromContext(ctx context.Context) (CallOverrideFunc, context.Context, bool) {
|
||||
cofs, _ := ctx.Value(&callOverrideKey).([]CallOverrideFunc)
|
||||
if len(cofs) == 0 {
|
||||
return nil, nil, false
|
||||
|
@ -37,7 +43,7 @@ func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netconte
|
|||
// We found a list of overrides; grab the last, and reconstitute a
|
||||
// context that will hide it.
|
||||
f := cofs[len(cofs)-1]
|
||||
ctx = netcontext.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1])
|
||||
ctx = context.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1])
|
||||
return f, ctx, true
|
||||
}
|
||||
|
||||
|
@ -45,23 +51,35 @@ type logOverrideFunc func(level int64, format string, args ...interface{})
|
|||
|
||||
var logOverrideKey = "holds a logOverrideFunc"
|
||||
|
||||
func WithLogOverride(ctx netcontext.Context, f logOverrideFunc) netcontext.Context {
|
||||
return netcontext.WithValue(ctx, &logOverrideKey, f)
|
||||
func WithLogOverride(ctx context.Context, f logOverrideFunc) context.Context {
|
||||
return context.WithValue(ctx, &logOverrideKey, f)
|
||||
}
|
||||
|
||||
var appIDOverrideKey = "holds a string, being the full app ID"
|
||||
|
||||
func WithAppIDOverride(ctx netcontext.Context, appID string) netcontext.Context {
|
||||
return netcontext.WithValue(ctx, &appIDOverrideKey, appID)
|
||||
func WithAppIDOverride(ctx context.Context, appID string) context.Context {
|
||||
return context.WithValue(ctx, &appIDOverrideKey, appID)
|
||||
}
|
||||
|
||||
var apiHostOverrideKey = ctxKey("holds a string, being the alternate API_HOST")
|
||||
|
||||
func withAPIHostOverride(ctx context.Context, apiHost string) context.Context {
|
||||
return context.WithValue(ctx, apiHostOverrideKey, apiHost)
|
||||
}
|
||||
|
||||
var apiPortOverrideKey = ctxKey("holds a string, being the alternate API_PORT")
|
||||
|
||||
func withAPIPortOverride(ctx context.Context, apiPort string) context.Context {
|
||||
return context.WithValue(ctx, apiPortOverrideKey, apiPort)
|
||||
}
|
||||
|
||||
var namespaceKey = "holds the namespace string"
|
||||
|
||||
func withNamespace(ctx netcontext.Context, ns string) netcontext.Context {
|
||||
return netcontext.WithValue(ctx, &namespaceKey, ns)
|
||||
func withNamespace(ctx context.Context, ns string) context.Context {
|
||||
return context.WithValue(ctx, &namespaceKey, ns)
|
||||
}
|
||||
|
||||
func NamespaceFromContext(ctx netcontext.Context) string {
|
||||
func NamespaceFromContext(ctx context.Context) string {
|
||||
// If there's no namespace, return the empty string.
|
||||
ns, _ := ctx.Value(&namespaceKey).(string)
|
||||
return ns
|
||||
|
@ -70,14 +88,14 @@ func NamespaceFromContext(ctx netcontext.Context) string {
|
|||
// FullyQualifiedAppID returns the fully-qualified application ID.
|
||||
// This may contain a partition prefix (e.g. "s~" for High Replication apps),
|
||||
// or a domain prefix (e.g. "example.com:").
|
||||
func FullyQualifiedAppID(ctx netcontext.Context) string {
|
||||
func FullyQualifiedAppID(ctx context.Context) string {
|
||||
if id, ok := ctx.Value(&appIDOverrideKey).(string); ok {
|
||||
return id
|
||||
}
|
||||
return fullyQualifiedAppID(ctx)
|
||||
}
|
||||
|
||||
func Logf(ctx netcontext.Context, level int64, format string, args ...interface{}) {
|
||||
func Logf(ctx context.Context, level int64, format string, args ...interface{}) {
|
||||
if f, ok := ctx.Value(&logOverrideKey).(logOverrideFunc); ok {
|
||||
f(level, format, args...)
|
||||
return
|
||||
|
@ -90,7 +108,7 @@ func Logf(ctx netcontext.Context, level int64, format string, args ...interface{
|
|||
}
|
||||
|
||||
// NamespacedContext wraps a Context to support namespaces.
|
||||
func NamespacedContext(ctx netcontext.Context, namespace string) netcontext.Context {
|
||||
func NamespacedContext(ctx context.Context, namespace string) context.Context {
|
||||
return withNamespace(ctx, namespace)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
netcontext "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -23,7 +22,7 @@ var (
|
|||
|
||||
// AppID is the implementation of the wrapper function of the same name in
|
||||
// ../identity.go. See that file for commentary.
|
||||
func AppID(c netcontext.Context) string {
|
||||
func AppID(c context.Context) string {
|
||||
return appID(FullyQualifiedAppID(c))
|
||||
}
|
||||
|
||||
|
@ -35,7 +34,7 @@ func IsStandard() bool {
|
|||
return appengineStandard || IsSecondGen()
|
||||
}
|
||||
|
||||
// IsStandard is the implementation of the wrapper function of the same name in
|
||||
// IsSecondGen is the implementation of the wrapper function of the same name in
|
||||
// ../appengine.go. See that file for commentary.
|
||||
func IsSecondGen() bool {
|
||||
// Second-gen runtimes set $GAE_ENV so we use that to check if we're on a second-gen runtime.
|
||||
|
|
|
@ -2,21 +2,22 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"appengine"
|
||||
"context"
|
||||
|
||||
netcontext "golang.org/x/net/context"
|
||||
"appengine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
appengineStandard = true
|
||||
}
|
||||
|
||||
func DefaultVersionHostname(ctx netcontext.Context) string {
|
||||
func DefaultVersionHostname(ctx context.Context) string {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
panic(errNotAppEngineContext)
|
||||
|
@ -24,12 +25,12 @@ func DefaultVersionHostname(ctx netcontext.Context) string {
|
|||
return appengine.DefaultVersionHostname(c)
|
||||
}
|
||||
|
||||
func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() }
|
||||
func Datacenter(_ context.Context) string { return appengine.Datacenter() }
|
||||
func ServerSoftware() string { return appengine.ServerSoftware() }
|
||||
func InstanceID() string { return appengine.InstanceID() }
|
||||
func IsDevAppServer() bool { return appengine.IsDevAppServer() }
|
||||
|
||||
func RequestID(ctx netcontext.Context) string {
|
||||
func RequestID(ctx context.Context) string {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
panic(errNotAppEngineContext)
|
||||
|
@ -37,14 +38,14 @@ func RequestID(ctx netcontext.Context) string {
|
|||
return appengine.RequestID(c)
|
||||
}
|
||||
|
||||
func ModuleName(ctx netcontext.Context) string {
|
||||
func ModuleName(ctx context.Context) string {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
panic(errNotAppEngineContext)
|
||||
}
|
||||
return appengine.ModuleName(c)
|
||||
}
|
||||
func VersionID(ctx netcontext.Context) string {
|
||||
func VersionID(ctx context.Context) string {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
panic(errNotAppEngineContext)
|
||||
|
@ -52,7 +53,7 @@ func VersionID(ctx netcontext.Context) string {
|
|||
return appengine.VersionID(c)
|
||||
}
|
||||
|
||||
func fullyQualifiedAppID(ctx netcontext.Context) string {
|
||||
func fullyQualifiedAppID(ctx context.Context) string {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
panic(errNotAppEngineContext)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build appenginevm
|
||||
// +build appenginevm
|
||||
|
||||
package internal
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
netcontext "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// These functions are implementations of the wrapper functions
|
||||
|
@ -24,7 +24,7 @@ const (
|
|||
hDatacenter = "X-AppEngine-Datacenter"
|
||||
)
|
||||
|
||||
func ctxHeaders(ctx netcontext.Context) http.Header {
|
||||
func ctxHeaders(ctx context.Context) http.Header {
|
||||
c := fromContext(ctx)
|
||||
if c == nil {
|
||||
return nil
|
||||
|
@ -32,15 +32,15 @@ func ctxHeaders(ctx netcontext.Context) http.Header {
|
|||
return c.Request().Header
|
||||
}
|
||||
|
||||
func DefaultVersionHostname(ctx netcontext.Context) string {
|
||||
func DefaultVersionHostname(ctx context.Context) string {
|
||||
return ctxHeaders(ctx).Get(hDefaultVersionHostname)
|
||||
}
|
||||
|
||||
func RequestID(ctx netcontext.Context) string {
|
||||
func RequestID(ctx context.Context) string {
|
||||
return ctxHeaders(ctx).Get(hRequestLogId)
|
||||
}
|
||||
|
||||
func Datacenter(ctx netcontext.Context) string {
|
||||
func Datacenter(ctx context.Context) string {
|
||||
if dc := ctxHeaders(ctx).Get(hDatacenter); dc != "" {
|
||||
return dc
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func ServerSoftware() string {
|
|||
|
||||
// TODO(dsymonds): Remove the metadata fetches.
|
||||
|
||||
func ModuleName(_ netcontext.Context) string {
|
||||
func ModuleName(_ context.Context) string {
|
||||
if s := os.Getenv("GAE_MODULE_NAME"); s != "" {
|
||||
return s
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ func ModuleName(_ netcontext.Context) string {
|
|||
return string(mustGetMetadata("instance/attributes/gae_backend_name"))
|
||||
}
|
||||
|
||||
func VersionID(_ netcontext.Context) string {
|
||||
func VersionID(_ context.Context) string {
|
||||
if s1, s2 := os.Getenv("GAE_MODULE_VERSION"), os.Getenv("GAE_MINOR_VERSION"); s1 != "" && s2 != "" {
|
||||
return s1 + "." + s2
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ func partitionlessAppID() string {
|
|||
return string(mustGetMetadata("instance/attributes/gae_project"))
|
||||
}
|
||||
|
||||
func fullyQualifiedAppID(_ netcontext.Context) string {
|
||||
func fullyQualifiedAppID(_ context.Context) string {
|
||||
if s := os.Getenv("GAE_APPLICATION"); s != "" {
|
||||
return s
|
||||
}
|
||||
|
@ -130,5 +130,5 @@ func fullyQualifiedAppID(_ netcontext.Context) string {
|
|||
}
|
||||
|
||||
func IsDevAppServer() bool {
|
||||
return os.Getenv("RUN_WITH_DEVAPPSERVER") != ""
|
||||
return os.Getenv("RUN_WITH_DEVAPPSERVER") != "" || os.Getenv("GAE_ENV") == "localdev"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package internal
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package internal
|
||||
|
@ -29,7 +30,7 @@ func Main() {
|
|||
if IsDevAppServer() {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
if err := http.ListenAndServe(host+":"+port, http.HandlerFunc(handleHTTP)); err != nil {
|
||||
if err := http.ListenAndServe(host+":"+port, Middleware(http.DefaultServeMux)); err != nil {
|
||||
log.Fatalf("http.ListenAndServe: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ package internal
|
|||
// This file implements hooks for applying datastore transactions.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
|
||||
basepb "google.golang.org/appengine/internal/base"
|
||||
pb "google.golang.org/appengine/internal/datastore"
|
||||
|
@ -38,13 +38,13 @@ func applyTransaction(pb proto.Message, t *pb.Transaction) {
|
|||
|
||||
var transactionKey = "used for *Transaction"
|
||||
|
||||
func transactionFromContext(ctx netcontext.Context) *transaction {
|
||||
func transactionFromContext(ctx context.Context) *transaction {
|
||||
t, _ := ctx.Value(&transactionKey).(*transaction)
|
||||
return t
|
||||
}
|
||||
|
||||
func withTransaction(ctx netcontext.Context, t *transaction) netcontext.Context {
|
||||
return netcontext.WithValue(ctx, &transactionKey, t)
|
||||
func withTransaction(ctx context.Context, t *transaction) context.Context {
|
||||
return context.WithValue(ctx, &transactionKey, t)
|
||||
}
|
||||
|
||||
type transaction struct {
|
||||
|
@ -54,7 +54,7 @@ type transaction struct {
|
|||
|
||||
var ErrConcurrentTransaction = errors.New("internal: concurrent transaction")
|
||||
|
||||
func RunTransactionOnce(c netcontext.Context, f func(netcontext.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) {
|
||||
func RunTransactionOnce(c context.Context, f func(context.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) {
|
||||
if transactionFromContext(c) != nil {
|
||||
return nil, errors.New("nested transactions are not supported")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package urlfetch // import "google.golang.org/appengine/urlfetch"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -18,7 +19,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
pb "google.golang.org/appengine/internal/urlfetch"
|
||||
|
@ -44,11 +44,10 @@ type Transport struct {
|
|||
var _ http.RoundTripper = (*Transport)(nil)
|
||||
|
||||
// Client returns an *http.Client using a default urlfetch Transport. This
|
||||
// client will have the default deadline of 5 seconds, and will check the
|
||||
// validity of SSL certificates.
|
||||
// client will check the validity of SSL certificates.
|
||||
//
|
||||
// Any deadline of the provided context will be used for requests through this client;
|
||||
// if the client does not have a deadline then a 5 second default is used.
|
||||
// Any deadline of the provided context will be used for requests through this client.
|
||||
// If the client does not have a deadline, then an App Engine default of 60 second is used.
|
||||
func Client(ctx context.Context) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &Transport{
|
||||
|
|
|
@ -135,7 +135,7 @@ github.com/containerd/cgroups/v3/cgroup1
|
|||
github.com/containerd/cgroups/v3/cgroup1/stats
|
||||
github.com/containerd/cgroups/v3/cgroup2
|
||||
github.com/containerd/cgroups/v3/cgroup2/stats
|
||||
# github.com/coreos/go-oidc/v3 v3.6.0
|
||||
# github.com/coreos/go-oidc/v3 v3.7.0
|
||||
## explicit; go 1.19
|
||||
github.com/coreos/go-oidc/v3/oidc
|
||||
# github.com/coreos/go-systemd/v22 v22.3.2
|
||||
|
@ -319,22 +319,9 @@ github.com/hashicorp/hcl/json/token
|
|||
# github.com/inconshreveable/mousetrap v1.1.0
|
||||
## explicit; go 1.18
|
||||
github.com/inconshreveable/mousetrap
|
||||
# github.com/jackc/chunkreader/v2 v2.0.1
|
||||
## explicit; go 1.12
|
||||
github.com/jackc/chunkreader/v2
|
||||
# github.com/jackc/pgconn v1.14.1
|
||||
## explicit; go 1.12
|
||||
github.com/jackc/pgconn
|
||||
github.com/jackc/pgconn/internal/ctxwatch
|
||||
# github.com/jackc/pgio v1.0.0
|
||||
## explicit; go 1.12
|
||||
github.com/jackc/pgio
|
||||
# github.com/jackc/pgpassfile v1.0.0
|
||||
## explicit; go 1.12
|
||||
github.com/jackc/pgpassfile
|
||||
# github.com/jackc/pgproto3/v2 v2.3.2
|
||||
## explicit; go 1.12
|
||||
github.com/jackc/pgproto3/v2
|
||||
# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
|
||||
## explicit; go 1.14
|
||||
github.com/jackc/pgservicefile
|
||||
|
@ -914,7 +901,7 @@ golang.org/x/tools/internal/pkgbits
|
|||
golang.org/x/tools/internal/tokeninternal
|
||||
golang.org/x/tools/internal/typeparams
|
||||
golang.org/x/tools/internal/typesinternal
|
||||
# google.golang.org/appengine v1.6.7
|
||||
# google.golang.org/appengine v1.6.8
|
||||
## explicit; go 1.11
|
||||
google.golang.org/appengine/internal
|
||||
google.golang.org/appengine/internal/base
|
||||
|
|
Loading…
Reference in New Issue