diff --git a/go.mod b/go.mod index 8ebc4f0..20a874b 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,21 @@ module github.com/datastax/cql-proxy -go 1.18 +go 1.23.0 + +toolchain go1.23.6 require ( github.com/alecthomas/kong v0.2.17 - github.com/datastax/astra-client-go/v2 v2.2.54 - github.com/datastax/go-cassandra-native-protocol v0.0.0-20220706104457-5e8aad05cf90 + github.com/datastax/go-cassandra-native-protocol v0.0.0-20211124104234-f6aea54fa801 github.com/hashicorp/golang-lru v0.5.4 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.7.0 go.uber.org/atomic v1.8.0 go.uber.org/zap v1.17.0 - gopkg.in/yaml.v2 v2.4.0 ) require ( - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deepmap/oapi-codegen v1.12.4 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/datastax/astra-client-go/v2 v2.2.9 // indirect go.uber.org/multierr v1.7.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index a7d1df6..1d643f3 100644 --- a/go.sum +++ b/go.sum @@ -1,45 +1,113 @@ -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0= github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/datastax/astra-client-go/v2 v2.2.54 h1:R2k9ek9zaU15cLD96np5gsj12oZhK3Z5/tSytjQagO8= -github.com/datastax/astra-client-go/v2 v2.2.54/go.mod h1:zxXWuqDkYia7PzFIL3T7RmjChc9LN81UnfI2yB4kE7M= -github.com/datastax/go-cassandra-native-protocol v0.0.0-20220706104457-5e8aad05cf90 h1:SiFe3gwoHPt95ly6HLjwyyItxROxCUJuxqqTnguR5ac= -github.com/datastax/go-cassandra-native-protocol v0.0.0-20220706104457-5e8aad05cf90/go.mod h1:6FzirJfdffakAVqmHjwVfFkpru/gNbIazUOK5rIhndc= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/datastax/astra-client-go/v2 v2.2.9 h1:UhsKot0om1OuVM6lbUq7X3k0Dl1QuuLi84+20JJHXeE= +github.com/datastax/astra-client-go/v2 v2.2.9/go.mod h1:6Ja4wfAU375DVpc+QTaAn6vm/yuTP0mUfV2NWq3qT80= +github.com/datastax/go-cassandra-native-protocol v0.0.0-20211124104234-f6aea54fa801 h1:cxrfSL9aswWmLXsL6g1q02gln8GwH3sb4Fh2rGzKP8Q= +github.com/datastax/go-cassandra-native-protocol v0.0.0-20211124104234-f6aea54fa801/go.mod h1:yFD0OKoVV9d1QW7Es58c1Gv6ijrqTGPcxgHv27wdC4Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= -github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= +github.com/deepmap/oapi-codegen v1.9.0 h1:qpyRY+dzjMai5QejjA53ebnBtcSvIcZOtYwVlsgdxOc= +github.com/deepmap/oapi-codegen v1.9.0/go.mod h1:7t4DbSxmAffcTEgrWvsPYEE2aOARZ8ZKWp3hDuZkHNc= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/getkin/kin-openapi v0.80.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +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/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= +github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= +github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= +github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pierrec/lz4/v4 v4.0.3 h1:vNQKSVZNYUEAvRY9FaUXAF1XPbSOHJtDTiP41kzDz2E= +github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +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/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4= go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -48,12 +116,123 @@ go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/proxy/codecs.go b/proxy/codecs.go deleted file mode 100644 index bdab69f..0000000 --- a/proxy/codecs.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) DataStax, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package proxy - -import ( - "encoding/hex" - "errors" - "fmt" - "io" - "io/ioutil" - - "github.com/datastax/go-cassandra-native-protocol/frame" - "github.com/datastax/go-cassandra-native-protocol/message" - "github.com/datastax/go-cassandra-native-protocol/primitive" -) - -var codec = frame.NewRawCodec(&partialQueryCodec{}, &partialExecuteCodec{}, &partialBatchCodec{}) - -type partialQueryCodec struct{} - -func (c *partialQueryCodec) Encode(_ message.Message, _ io.Writer, _ primitive.ProtocolVersion) error { - panic("not implemented") -} - -func (c *partialQueryCodec) EncodedLength(_ message.Message, _ primitive.ProtocolVersion) (int, error) { - panic("not implemented") -} - -func (c *partialQueryCodec) Decode(source io.Reader, _ primitive.ProtocolVersion) (message.Message, error) { - if query, err := primitive.ReadLongString(source); err != nil { - return nil, err - } else { - return &partialQuery{query}, nil - } -} - -func (c *partialQueryCodec) GetOpCode() primitive.OpCode { - return primitive.OpCodeQuery -} - -type partialQuery struct { - query string -} - -func (p *partialQuery) IsResponse() bool { - return false -} - -func (p *partialQuery) GetOpCode() primitive.OpCode { - return primitive.OpCodeQuery -} - -func (p *partialQuery) DeepCopyMessage() message.Message { - return &partialQuery{p.query} -} - -type partialExecute struct { - queryId []byte -} - -func (m *partialExecute) IsResponse() bool { - return false -} - -func (m *partialExecute) GetOpCode() primitive.OpCode { - return primitive.OpCodeExecute -} - -func (m *partialExecute) DeepCopyMessage() message.Message { - queryId := make([]byte, len(m.queryId)) - copy(queryId, m.queryId) - return &partialExecute{queryId} -} - -func (m *partialExecute) String() string { - return "EXECUTE " + hex.EncodeToString(m.queryId) -} - -type partialExecuteCodec struct{} - -func (c *partialExecuteCodec) Encode(_ message.Message, _ io.Writer, _ primitive.ProtocolVersion) error { - panic("not implemented") -} - -func (c *partialExecuteCodec) EncodedLength(_ message.Message, _ primitive.ProtocolVersion) (size int, err error) { - panic("not implemented") -} - -func (c *partialExecuteCodec) Decode(source io.Reader, _ primitive.ProtocolVersion) (msg message.Message, err error) { - execute := &partialExecute{} - if execute.queryId, err = primitive.ReadShortBytes(source); err != nil { - return nil, fmt.Errorf("cannot read EXECUTE query id: %w", err) - } else if len(execute.queryId) == 0 { - return nil, errors.New("EXECUTE missing query id") - } - return execute, nil -} - -func (c *partialExecuteCodec) GetOpCode() primitive.OpCode { - return primitive.OpCodeExecute -} - -type partialBatch struct { - queryOrIds []interface{} -} - -func (p partialBatch) IsResponse() bool { - return false -} - -func (p partialBatch) GetOpCode() primitive.OpCode { - return primitive.OpCodeBatch -} - -func (p partialBatch) DeepCopyMessage() message.Message { - queryOrIds := make([]interface{}, len(p.queryOrIds)) - copy(queryOrIds, p.queryOrIds) - return &partialBatch{queryOrIds} -} - -type partialBatchCodec struct{} - -func (p partialBatchCodec) Encode(msg message.Message, dest io.Writer, version primitive.ProtocolVersion) error { - panic("not implemented") -} - -func (p partialBatchCodec) EncodedLength(msg message.Message, version primitive.ProtocolVersion) (int, error) { - panic("not implemented") -} - -func (p partialBatchCodec) Decode(source io.Reader, version primitive.ProtocolVersion) (msg message.Message, err error) { - var queryOrIds []interface{} - var typ uint8 - if typ, err = primitive.ReadByte(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH type: %w", err) - } - if err = primitive.CheckValidBatchType(primitive.BatchType(typ)); err != nil { - return nil, err - } - var count uint16 - if count, err = primitive.ReadShort(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH query count: %w", err) - } - queryOrIds = make([]interface{}, count) - for i := 0; i < int(count); i++ { - var queryTyp uint8 - if queryTyp, err = primitive.ReadByte(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH query type for child #%d: %w", i, err) - } - var queryOrId interface{} - switch primitive.BatchChildType(queryTyp) { - case primitive.BatchChildTypeQueryString: - if queryOrId, err = primitive.ReadLongString(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH query string for child #%d: %w", i, err) - } - case primitive.BatchChildTypePreparedId: - if queryOrId, err = primitive.ReadShortBytes(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH query id for child #%d: %w", i, err) - } - default: - return nil, fmt.Errorf("unsupported BATCH child type for child #%d: %v", i, queryTyp) - } - if err = skipPositionalValues(source); err != nil { - return nil, fmt.Errorf("cannot read BATCH positional values for child #%d: %w", i, err) - } - queryOrIds[i] = queryOrId - } - return &partialBatch{queryOrIds}, nil -} - -func (p partialBatchCodec) GetOpCode() primitive.OpCode { - return primitive.OpCodeBatch -} - -func skipPositionalValues(source io.Reader) error { - if length, err := primitive.ReadShort(source); err != nil { - return fmt.Errorf("cannot read positional [value]s length: %w", err) - } else { - for i := uint16(0); i < length; i++ { - if err = skipValue(source); err != nil { - return fmt.Errorf("cannot read positional [value]s element %d content: %w", i, err) - } - } - return nil - } -} - -func skipValue(source io.Reader) error { - if length, err := primitive.ReadInt(source); err != nil { - return fmt.Errorf("cannot read [value] length: %w", err) - } else if length <= 0 { - return nil - } else { - if _, err = io.CopyN(ioutil.Discard, source, int64(length)); err != nil { - return fmt.Errorf("cannot read [value] content: %w", err) - } - return nil - } -} diff --git a/proxy/proxy.go b/proxy/proxy.go index fc585f4..119e136 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -52,7 +52,10 @@ var ErrProxyClosed = errors.New("proxy closed") var ErrProxyAlreadyConnected = errors.New("proxy already connected") var ErrProxyNotConnected = errors.New("proxy not connected") -const preparedIdSize = 16 +const ( + preparedIdSize = 16 + DefaultMaxFrameSize = 1 << 26 // 64MiB +) type PeerConfig struct { RPCAddr string `yaml:"rpc-address"` @@ -117,7 +120,7 @@ func (p *Proxy) OnEvent(event proxycore.Event) { p.eventClients.Range(func(key, _ interface{}) bool { cl := key.(*client) err := cl.conn.Write(proxycore.SenderFunc(func(writer io.Writer) error { - return codec.EncodeFrame(frm, writer) + return proxycore.EncodeFrame(frm, writer) })) cl.conn.LocalAddr() if err != nil { @@ -308,6 +311,7 @@ func (p *Proxy) handle(conn net.Conn) { ctx: p.ctx, proxy: p, preparedSystemQuery: make(map[[preparedIdSize]byte]interface{}), + codec: proxycore.UncompressedCodec{}, } p.addClient(cl) cl.conn = proxycore.NewConn(conn, cl) @@ -447,7 +451,7 @@ func (p *Proxy) buildLocalRow() { "key": p.encodeTypeFatal(datatype.Varchar, "local"), "data_center": p.encodeTypeFatal(datatype.Varchar, p.localNode.dc), "rack": p.encodeTypeFatal(datatype.Varchar, "rack1"), - "tokens": p.encodeTypeFatal(datatype.NewList(datatype.Varchar), p.localNode.tokens), + "tokens": p.encodeTypeFatal(datatype.NewListType(datatype.Varchar), p.localNode.tokens), "release_version": p.encodeTypeFatal(datatype.Varchar, p.cluster.Info.ReleaseVersion), "partitioner": p.encodeTypeFatal(datatype.Varchar, p.cluster.Info.Partitioner), "cluster_name": p.encodeTypeFatal(datatype.Varchar, "cql-proxy"), @@ -483,7 +487,7 @@ func (p *Proxy) isIdempotent(id []byte) bool { // This information is used by future "EXECUTE" requests when they need to be retried. func (p *Proxy) maybeStorePreparedIdempotence(raw *frame.RawFrame, msg message.Message) { if prepareMsg, ok := msg.(*message.Prepare); ok && raw.Header.OpCode == primitive.OpCodeResult { // Prepared result - frm, err := codec.ConvertFromRawFrame(raw) + frm, err := proxycore.ConvertFromRawFrame(raw) if err != nil { p.logger.Error("error attempting to decode prepared result message") } else if _, ok = frm.Body.Message.(*message.PreparedResult); !ok { // TODO: Use prepared type data to disambiguate idempotency @@ -533,10 +537,15 @@ type client struct { conn *proxycore.Conn keyspace string preparedSystemQuery map[[16]byte]interface{} + codec proxycore.CompressionCodec } func (c *client) Receive(reader io.Reader) error { - raw, err := codec.DecodeRawFrame(reader) + raw, err := c.codec.DecodeFrame(reader, DefaultMaxFrameSize) + if raw != nil && raw.Header != nil { + c.proxy.logger.Debug("received frame", zap.Any("msg", raw.Header.OpCode)) + } + if err != nil { if !errors.Is(err, io.EOF) { c.proxy.logger.Error("unable to decode frame", zap.Error(err)) @@ -551,19 +560,36 @@ func (c *client) Receive(reader io.Reader) error { return nil } - body, err := codec.DecodeBody(raw.Header, bytes.NewReader(raw.Body)) + body, err := proxycore.DecodeBody(raw) if err != nil { c.proxy.logger.Error("unable to decode body", zap.Error(err)) return err } + c.proxy.logger.Debug("handling message", + zap.String("client", c.conn.RemoteAddr().String()), + zap.String("msg", body.Message.GetOpCode().String())) + + raw.Header.Flags = 0 + switch msg := body.Message.(type) { case *message.Options: c.send(raw.Header, &message.Supported{Options: map[string][]string{ "CQL_VERSION": {c.proxy.cluster.Info.CQLVersion}, - "COMPRESSION": {}, + "COMPRESSION": proxycore.SupportedCompressionCodecs, }}) case *message.Startup: + if compression, ok := msg.Options["COMPRESSION"]; ok { + compression = strings.ToLower(compression) + if codec, ok := proxycore.CompressionCodecs[compression]; ok { + c.codec = codec + } else { + c.proxy.logger.Error("unsupported compression type used by client", zap.String("compression", compression)) + errMsg := fmt.Sprintf("Unsupported compression type: %s (supported compression types: %s)", + compression, strings.Join(proxycore.SupportedCompressionCodecs, ", ")) + c.send(raw.Header, &message.ProtocolError{ErrorMessage: errMsg}) + } + } c.send(raw.Header, &message.Ready{}) case *message.Register: for _, t := range msg.EventTypes { @@ -574,11 +600,11 @@ func (c *client) Receive(reader io.Reader) error { c.send(raw.Header, &message.Ready{}) case *message.Prepare: c.handlePrepare(raw, msg) - case *partialExecute: + case *proxycore.PartialExecute: c.handleExecute(raw, msg, body.CustomPayload) - case *partialQuery: + case *proxycore.PartialQuery: c.handleQuery(raw, msg, body.CustomPayload) - case *partialBatch: + case *proxycore.PartialBatch: c.execute(raw, notDetermined, c.keyspace, msg) default: c.send(raw.Header, &message.ProtocolError{ErrorMessage: "Unsupported operation"}) @@ -657,8 +683,8 @@ func (c *client) handlePrepare(raw *frame.RawFrame, msg *message.Prepare) { } } -func (c *client) handleExecute(raw *frame.RawFrame, msg *partialExecute, customPayload map[string][]byte) { - id := preparedIdKey(msg.queryId) +func (c *client) handleExecute(raw *frame.RawFrame, msg *proxycore.PartialExecute, customPayload map[string][]byte) { + id := preparedIdKey(msg.QueryID) if stmt, ok := c.preparedSystemQuery[id]; ok { c.interceptSystemQuery(raw.Header, stmt) } else { @@ -666,10 +692,12 @@ func (c *client) handleExecute(raw *frame.RawFrame, msg *partialExecute, customP } } -func (c *client) handleQuery(raw *frame.RawFrame, msg *partialQuery, customPayload map[string][]byte) { - handled, stmt, err := parser.IsQueryHandled(parser.IdentifierFromString(c.keyspace), msg.query) +func (c *client) handleQuery(raw *frame.RawFrame, msg *proxycore.PartialQuery, customPayload map[string][]byte) { + c.proxy.logger.Debug("handling query", zap.String("query", msg.Query), zap.Int16("stream", raw.Header.StreamId)) + + handled, stmt, err := parser.IsQueryHandled(parser.IdentifierFromString(c.keyspace), msg.Query) + if handled { - c.proxy.logger.Debug("Query handled by proxy", zap.String("query", msg.query), zap.Int16("stream", raw.Header.StreamId)) if err != nil { c.proxy.logger.Error("error parsing query to see if it's handled", zap.Error(err)) c.send(raw.Header, &message.Invalid{ErrorMessage: err.Error()}) @@ -677,7 +705,6 @@ func (c *client) handleQuery(raw *frame.RawFrame, msg *partialQuery, customPaylo c.interceptSystemQuery(raw.Header, stmt) } } else { - c.proxy.logger.Debug("Query not handled by proxy, forwarding", zap.String("query", msg.query), zap.Int16("stream", raw.Header.StreamId)) c.execute(raw, c.getDefaultIdempotency(customPayload), c.keyspace, msg) } } @@ -733,7 +760,7 @@ func (c *client) filterSystemPeerValues(stmt *parser.SelectStatement, filtered [ } else if name == "host_id" { return proxycore.EncodeType(datatype.Uuid, c.proxy.cluster.NegotiatedVersion, nameBasedUUID(peer.addr.String())) } else if name == "tokens" { - return proxycore.EncodeType(datatype.NewList(datatype.Varchar), c.proxy.cluster.NegotiatedVersion, peer.tokens) + return proxycore.EncodeType(datatype.NewListType(datatype.Varchar), c.proxy.cluster.NegotiatedVersion, peer.tokens) } else if name == "peer" { return proxycore.EncodeType(datatype.Inet, c.proxy.cluster.NegotiatedVersion, peer.addr.IP) } else if name == "rpc_address" { @@ -812,22 +839,10 @@ func (c *client) interceptSystemQuery(hdr *frame.Header, stmt interface{}) { } case *parser.UseStatement: if _, err := c.proxy.maybeCreateSession(hdr.Version, s.Keyspace); err != nil { - errMsg := "Proxy unable to create new session for keyspace" - var cqlError *proxycore.CqlError - if errors.As(err, &cqlError) { - // copy detailed error reason from downstream message - errMsg = cqlError.Message.GetErrorMessage() - } - c.send(hdr, &message.ServerError{ErrorMessage: errMsg}) + c.send(hdr, &message.ServerError{ErrorMessage: "Proxy unable to create new session for keyspace"}) } else { c.keyspace = s.Keyspace - // We might have received a quoted keyspace name in the UseStatement so remove any - // quotes before sending back this result message. This keeps us consistent with - // how Cassandra implements the same functionality and avoids any issues with - // drivers sending follow-on "USE" requests after wrapping the keyspace name in - // quotes. - ks := parser.IdentifierFromString(s.Keyspace) - c.send(hdr, &message.SetKeyspaceResult{Keyspace: ks.ID()}) + c.send(hdr, &message.SetKeyspaceResult{Keyspace: s.Keyspace}) } default: c.send(hdr, &message.ServerError{ErrorMessage: "Proxy attempted to intercept an unhandled query"}) @@ -836,7 +851,7 @@ func (c *client) interceptSystemQuery(hdr *frame.Header, stmt interface{}) { func (c *client) send(hdr *frame.Header, msg message.Message) { _ = c.conn.Write(proxycore.SenderFunc(func(writer io.Writer) error { - return codec.EncodeFrame(frame.NewFrame(hdr.Version, hdr.StreamId, msg), writer) + return proxycore.EncodeFrame(frame.NewFrame(hdr.Version, hdr.StreamId, msg), writer) })) } diff --git a/proxy/request.go b/proxy/request.go index 9114b5f..6331f0d 100644 --- a/proxy/request.go +++ b/proxy/request.go @@ -16,6 +16,7 @@ package proxy import ( "errors" + "github.com/datastax/cql-proxy/proxycore/codecs" "io" "reflect" "sync" @@ -80,14 +81,14 @@ func (r *request) executeInternal(next bool) { func (r *request) send(msg message.Message) { _ = r.client.conn.Write(proxycore.SenderFunc(func(writer io.Writer) error { - return codec.EncodeFrame(frame.NewFrame(r.raw.Header.Version, r.stream, msg), writer) + return codecs.codec.EncodeFrame(frame.NewFrame(r.raw.Header.Version, r.stream, msg), writer) })) } func (r *request) sendRaw(raw *frame.RawFrame) { raw.Header.StreamId = r.stream _ = r.client.conn.Write(proxycore.SenderFunc(func(writer io.Writer) error { - return codec.EncodeRawFrame(raw, writer) + return codecs.codec.EncodeRawFrame(raw, writer) })) } @@ -101,11 +102,11 @@ func (r *request) checkIdempotent() bool { var err error if r.msg != nil { switch msg := r.msg.(type) { - case *partialQuery: + case *codecs.partialQuery: idempotent, err = parser.IsQueryIdempotent(msg.query) - case *partialExecute: + case *codecs.partialExecute: idempotent = r.client.proxy.isIdempotent(msg.queryId) - case *partialBatch: + case *codecs.partialBatch: idempotent, err = r.isBatchIdempotent(msg) default: r.client.proxy.logger.Error("invalid message type encountered when checking for idempotence", @@ -158,7 +159,7 @@ func (r *request) handleErrorResult(raw *frame.RawFrame) (retried bool) { logger := r.client.proxy.logger decision := ReturnError - frm, err := codec.ConvertFromRawFrame(raw) + frm, err := codecs.codec.ConvertFromRawFrame(raw) if err != nil { logger.Error("unable to decode error frame for retry decision", zap.Error(err)) } else { @@ -237,7 +238,7 @@ func (r *request) handleErrorResult(raw *frame.RawFrame) (retried bool) { return retried } -func (r *request) isBatchIdempotent(batch *partialBatch) (idempotent bool, err error) { +func (r *request) isBatchIdempotent(batch *codecs.partialBatch) (idempotent bool, err error) { for _, queryOrId := range batch.queryOrIds { switch q := queryOrId.(type) { case string: diff --git a/proxycore/clientconn.go b/proxycore/clientconn.go index be92774..372fe11 100644 --- a/proxycore/clientconn.go +++ b/proxycore/clientconn.go @@ -257,7 +257,7 @@ func (c *ClientConn) Receive(reader io.Reader) error { if raw.Header.OpCode == primitive.OpCodeEvent { if c.eventHandler != nil { - frm, err := codec.ConvertFromRawFrame(raw) + frm, err := ConvertFromRawFrame(raw) if err != nil { return err } diff --git a/proxycore/codecs.go b/proxycore/codecs.go index b93e1b2..808ac10 100644 --- a/proxycore/codecs.go +++ b/proxycore/codecs.go @@ -15,69 +15,201 @@ package proxycore import ( + "encoding/hex" + "errors" "fmt" + "io" + "io/ioutil" - "github.com/datastax/go-cassandra-native-protocol/datacodec" - "github.com/datastax/go-cassandra-native-protocol/datatype" + lz4compress "github.com/datastax/go-cassandra-native-protocol/compression/lz4" + snappycompress "github.com/datastax/go-cassandra-native-protocol/compression/snappy" "github.com/datastax/go-cassandra-native-protocol/frame" + "github.com/datastax/go-cassandra-native-protocol/message" "github.com/datastax/go-cassandra-native-protocol/primitive" ) -var codec = frame.NewRawCodec() - -var primitiveCodecs = map[datatype.DataType]datacodec.Codec{ - datatype.Ascii: datacodec.Ascii, - datatype.Bigint: datacodec.Bigint, - datatype.Blob: datacodec.Blob, - datatype.Boolean: datacodec.Boolean, - datatype.Counter: datacodec.Counter, - datatype.Decimal: datacodec.Decimal, - datatype.Double: datacodec.Double, - datatype.Float: datacodec.Float, - datatype.Inet: datacodec.Inet, - datatype.Int: datacodec.Int, - datatype.Smallint: datacodec.Smallint, - datatype.Varchar: datacodec.Varchar, - datatype.Timeuuid: datacodec.Timeuuid, - datatype.Tinyint: datacodec.Tinyint, - datatype.Uuid: datacodec.Uuid, - datatype.Varint: datacodec.Varint, -} - -func EncodeType(dt datatype.DataType, version primitive.ProtocolVersion, val interface{}) ([]byte, error) { - c, err := codecFromDataType(dt) - if err != nil { +var codec = frame.NewRawCodec(&partialQueryCodec{}, &partialExecuteCodec{}, &partialBatchCodec{}) +var snappyCodec = frame.NewRawCodecWithCompression(&snappycompress.Compressor{}, &partialQueryCodec{}, &partialExecuteCodec{}, &partialBatchCodec{}) +var lz4Codec = frame.NewRawCodecWithCompression(&lz4compress.Compressor{}, &partialQueryCodec{}, &partialExecuteCodec{}, &partialBatchCodec{}) + +type partialQueryCodec struct{} + +func (c *partialQueryCodec) Encode(_ message.Message, _ io.Writer, _ primitive.ProtocolVersion) error { + panic("not implemented") +} + +func (c *partialQueryCodec) EncodedLength(_ message.Message, _ primitive.ProtocolVersion) (int, error) { + panic("not implemented") +} + +func (c *partialQueryCodec) Decode(source io.Reader, _ primitive.ProtocolVersion) (message.Message, error) { + if query, err := primitive.ReadLongString(source); err != nil { return nil, err + } else { + return &PartialQuery{query}, nil + } +} + +func (c *partialQueryCodec) GetOpCode() primitive.OpCode { + return primitive.OpCodeQuery +} + +type partialQuery struct { + query string +} + +func (p *partialQuery) IsResponse() bool { + return false +} + +func (p *partialQuery) GetOpCode() primitive.OpCode { + return primitive.OpCodeQuery +} + +func (p *partialQuery) Clone() message.Message { + return &partialQuery{p.query} +} + +type partialExecute struct { + queryId []byte +} + +func (m *partialExecute) IsResponse() bool { + return false +} + +func (m *partialExecute) GetOpCode() primitive.OpCode { + return primitive.OpCodeExecute +} + +func (m *partialExecute) Clone() message.Message { + return &partialExecute{ + queryId: primitive.CloneByteSlice(m.queryId), + } +} + +func (m *partialExecute) String() string { + return "EXECUTE " + hex.EncodeToString(m.queryId) +} + +type partialExecuteCodec struct{} + +func (c *partialExecuteCodec) Encode(_ message.Message, _ io.Writer, _ primitive.ProtocolVersion) error { + panic("not implemented") +} + +func (c *partialExecuteCodec) EncodedLength(_ message.Message, _ primitive.ProtocolVersion) (size int, err error) { + panic("not implemented") +} + +func (c *partialExecuteCodec) Decode(source io.Reader, _ primitive.ProtocolVersion) (msg message.Message, err error) { + execute := &partialExecute{} + if execute.queryId, err = primitive.ReadShortBytes(source); err != nil { + return nil, fmt.Errorf("cannot read EXECUTE query id: %w", err) + } else if len(execute.queryId) == 0 { + return nil, errors.New("EXECUTE missing query id") } - return c.Encode(val, version) + return execute, nil +} + +func (c *partialExecuteCodec) GetOpCode() primitive.OpCode { + return primitive.OpCodeExecute +} + +type partialBatch struct { + queryOrIds []interface{} +} + +func (p partialBatch) IsResponse() bool { + return false +} + +func (p partialBatch) GetOpCode() primitive.OpCode { + return primitive.OpCodeBatch +} + +func (p partialBatch) Clone() message.Message { + queryOrIds := make([]interface{}, len(p.queryOrIds)) + copy(queryOrIds, p.queryOrIds) + return &partialBatch{queryOrIds} +} + +type partialBatchCodec struct{} + +func (p partialBatchCodec) Encode(msg message.Message, dest io.Writer, version primitive.ProtocolVersion) error { + panic("not implemented") } -func DecodeType(dt datatype.DataType, version primitive.ProtocolVersion, bytes []byte) (interface{}, error) { - c, err := codecFromDataType(dt) - if err != nil { +func (p partialBatchCodec) EncodedLength(msg message.Message, version primitive.ProtocolVersion) (int, error) { + panic("not implemented") +} + +func (p partialBatchCodec) Decode(source io.Reader, version primitive.ProtocolVersion) (msg message.Message, err error) { + var queryOrIds []interface{} + var typ uint8 + if typ, err = primitive.ReadByte(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH type: %w", err) + } + if err = primitive.CheckValidBatchType(primitive.BatchType(typ)); err != nil { return nil, err } - var dest interface{} - _, err = c.Decode(bytes, &dest, version) - return dest, err -} - -func codecFromDataType(dt datatype.DataType) (datacodec.Codec, error) { - switch dt.Code() { - case primitive.DataTypeCodeList: - listType := dt.(*datatype.List) - return datacodec.NewList(datatype.NewList(listType.ElementType)) - case primitive.DataTypeCodeSet: - setType := dt.(*datatype.Set) - return datacodec.NewSet(datatype.NewSet(setType.ElementType)) - case primitive.DataTypeCodeMap: - mapType := dt.(*datatype.Map) - return datacodec.NewMap(datatype.NewMap(mapType.KeyType, mapType.ValueType)) - default: - codec, ok := primitiveCodecs[dt] - if !ok { - return nil, fmt.Errorf("no codec for data type %v", dt) + var count uint16 + if count, err = primitive.ReadShort(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH query count: %w", err) + } + queryOrIds = make([]interface{}, count) + for i := 0; i < int(count); i++ { + var queryTyp uint8 + if queryTyp, err = primitive.ReadByte(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH query type for child #%d: %w", i, err) + } + var queryOrId interface{} + switch primitive.BatchChildType(queryTyp) { + case primitive.BatchChildTypeQueryString: + if queryOrId, err = primitive.ReadLongString(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH query string for child #%d: %w", i, err) + } + case primitive.BatchChildTypePreparedId: + if queryOrId, err = primitive.ReadShortBytes(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH query id for child #%d: %w", i, err) + } + default: + return nil, fmt.Errorf("unsupported BATCH child type for child #%d: %v", i, queryTyp) + } + if err = skipPositionalValues(source); err != nil { + return nil, fmt.Errorf("cannot read BATCH positional values for child #%d: %w", i, err) + } + queryOrIds[i] = queryOrId + } + return &partialBatch{queryOrIds}, nil +} + +func (p partialBatchCodec) GetOpCode() primitive.OpCode { + return primitive.OpCodeBatch +} + +func skipPositionalValues(source io.Reader) error { + if length, err := primitive.ReadShort(source); err != nil { + return fmt.Errorf("cannot read positional [value]s length: %w", err) + } else { + for i := uint16(0); i < length; i++ { + if err = skipValue(source); err != nil { + return fmt.Errorf("cannot read positional [value]s element %d content: %w", i, err) + } + } + return nil + } +} + +func skipValue(source io.Reader) error { + if length, err := primitive.ReadInt(source); err != nil { + return fmt.Errorf("cannot read [value] length: %w", err) + } else if length <= 0 { + return nil + } else { + if _, err = io.CopyN(ioutil.Discard, source, int64(length)); err != nil { + return fmt.Errorf("cannot read [value] content: %w", err) } - return codec, nil + return nil } } diff --git a/proxycore/compressor.go b/proxycore/compressor.go new file mode 100644 index 0000000..cc2e7d9 --- /dev/null +++ b/proxycore/compressor.go @@ -0,0 +1,144 @@ +package proxycore + +import ( + "encoding/binary" + "fmt" + "github.com/datastax/go-cassandra-native-protocol/frame" + "github.com/datastax/go-cassandra-native-protocol/primitive" + "github.com/golang/snappy" + "github.com/pierrec/lz4/v4" + "golang.org/x/exp/maps" + "io" + "slices" +) + +const sizeOfUint32 = 4 + +var CompressionCodecs = map[string]CompressionCodec{ + "snappy": SnappyCompressionCodec{}, + "lz4": LZ4CompressionCodec{}, +} + +var SupportedCompressionCodecs = buildSupportedCompressionCodecs() + +// CompressionCodec is a wrapper around frame.RawCodec that handles frame compression and decompression. It's useful because +// it avoids decompressing the frame body twice on the way in; once for the raw frame to be sent to the coordinator and +// once for the "partial" types (PartialQuery, PartialExecute, etc.) that we use for internal inspection. +type CompressionCodec interface { + DecodeFrame(reader io.Reader, maxBodyLen int32) (*frame.RawFrame, error) + EncodeFrame(frm *frame.Frame, writer io.Writer) error +} + +type UncompressedCodec struct{} + +func (u UncompressedCodec) DecodeFrame(reader io.Reader, maxBodyLen int32) (*frame.RawFrame, error) { + return decodeRawFrame(reader, maxBodyLen) +} + +func (u UncompressedCodec) EncodeFrame(frm *frame.Frame, writer io.Writer) error { + return codec.EncodeFrame(frm, writer) +} + +type SnappyCompressionCodec struct{} + +func (s SnappyCompressionCodec) DecodeFrame(reader io.Reader, maxBodyLen int32) (*frame.RawFrame, error) { + raw, err := decodeRawFrame(reader, maxBodyLen) + if err != nil { + return nil, err + } + + if raw.Header.BodyLength == 0 || // Nothing to decompress + !raw.Header.Flags.Contains(primitive.HeaderFlagCompressed) { // Not compressed + return raw, nil + } + + decompressedLen, err := snappy.DecodedLen(raw.Body) + if err != nil { + return nil, fmt.Errorf("unable to determine decompressed length: %w", err) + } + + if int32(decompressedLen) > maxBodyLen { + return nil, fmt.Errorf("decompressed body length %d exceeds maximum allowed %d", decompressedLen, maxBodyLen) + } + + if raw.Body, err = snappy.Decode(nil, raw.Body); err != nil { + return nil, fmt.Errorf("unable decompress frame body: %w", err) + } + raw.Header.Flags = raw.Header.Flags.Remove(primitive.HeaderFlagCompressed) + return raw, nil +} + +func (s SnappyCompressionCodec) EncodeFrame(frm *frame.Frame, writer io.Writer) error { + return snappyCodec.EncodeFrame(frm, writer) +} + +type LZ4CompressionCodec struct{} + +func (l LZ4CompressionCodec) DecodeFrame(reader io.Reader, maxBodyLen int32) (*frame.RawFrame, error) { + raw, err := decodeRawFrame(reader, maxBodyLen) + if err != nil { + return nil, err + } + + if raw.Header.BodyLength == 0 || // Nothing to decompress + !raw.Header.Flags.Contains(primitive.HeaderFlagCompressed) { // Not compressed + return raw, nil + } + + if len(raw.Body) < sizeOfUint32 { + return nil, fmt.Errorf("unable to read compressed data length; expected at least %d bytes, got %d", + sizeOfUint32, len(raw.Body)) + } + + decompressedLen := int32(binary.BigEndian.Uint32(raw.Body)) + + if decompressedLen < 0 { + return nil, fmt.Errorf("decompressed body length %d is invalid", decompressedLen) + } + + if decompressedLen > maxBodyLen { + return nil, fmt.Errorf("decompressed body length %d exceeds maximum allowed %d", decompressedLen, maxBodyLen) + } + + decompressed := make([]byte, decompressedLen) + + // Skip decompression if the length is zero; otherwise, this can cause lzr.UncompressBlock to return an error. + // Version 3.x of the java-driver has sent empty lz4-compressed bodies, so we need to handle this case. + if decompressedLen > 0 { + if _, err = lz4.UncompressBlock(raw.Body[sizeOfUint32:], decompressed); err != nil { + return nil, fmt.Errorf("unable decompress frame body: %w", err) + } + } + + raw.Body = decompressed + raw.Header.Flags = raw.Header.Flags.Remove(primitive.HeaderFlagCompressed) + return raw, nil +} + +func (l LZ4CompressionCodec) EncodeFrame(frm *frame.Frame, writer io.Writer) error { + return lz4Codec.EncodeFrame(frm, writer) +} + +func decodeRawFrame(reader io.Reader, maxBodyLen int32) (*frame.RawFrame, error) { + header, err := codec.DecodeHeader(reader) + if err != nil { + return nil, fmt.Errorf("cannot decode frame header: %w", err) + } + + if header.BodyLength > maxBodyLen { + return nil, fmt.Errorf("frame body length %d exceeds maximum allowed %d", header.BodyLength, maxBodyLen) + } + + body, err := codec.DecodeRawBody(header, reader) // Doesn't handle compression + if err != nil { + return nil, fmt.Errorf("cannot decode frame body: %w", err) + } + + return &frame.RawFrame{Header: header, Body: body}, nil +} + +func buildSupportedCompressionCodecs() []string { + supported := maps.Keys(CompressionCodecs) + slices.Sort(supported) + return supported +} diff --git a/proxycore/types.go b/proxycore/types.go new file mode 100644 index 0000000..aed273a --- /dev/null +++ b/proxycore/types.go @@ -0,0 +1,58 @@ +package proxycore + +import ( + "github.com/datastax/go-cassandra-native-protocol/message" + "github.com/datastax/go-cassandra-native-protocol/primitive" +) + +type PartialQuery struct { + Query string +} + +func (p *PartialQuery) IsResponse() bool { + return false +} + +func (p *PartialQuery) GetOpCode() primitive.OpCode { + return primitive.OpCodeQuery +} + +func (p *PartialQuery) Clone() message.Message { + return &PartialQuery{p.Query} +} + +type PartialExecute struct { + QueryID []byte +} + +func (m *PartialExecute) IsResponse() bool { + return false +} + +func (m *PartialExecute) GetOpCode() primitive.OpCode { + return primitive.OpCodeExecute +} + +func (m *PartialExecute) Clone() message.Message { + return &PartialExecute{ + QueryID: primitive.CloneByteSlice(m.QueryID), + } +} + +type PartialBatch struct { + QueryOrIDs []interface{} +} + +func (p PartialBatch) IsResponse() bool { + return false +} + +func (p PartialBatch) GetOpCode() primitive.OpCode { + return primitive.OpCodeBatch +} + +func (p PartialBatch) Clone() message.Message { + queryOrIds := make([]interface{}, len(p.QueryOrIDs)) + copy(queryOrIds, p.QueryOrIDs) + return &PartialBatch{queryOrIds} +} diff --git a/proxycore/utils.go b/proxycore/utils.go new file mode 100644 index 0000000..40ee82e --- /dev/null +++ b/proxycore/utils.go @@ -0,0 +1,70 @@ +package proxycore + +import ( + "bytes" + "fmt" + "github.com/datastax/go-cassandra-native-protocol/datacodec" + "github.com/datastax/go-cassandra-native-protocol/datatype" + "github.com/datastax/go-cassandra-native-protocol/frame" + "github.com/datastax/go-cassandra-native-protocol/primitive" + "io" +) + +func DecodeBody(raw *frame.RawFrame) (*frame.Body, error) { + return codec.DecodeBody(raw.Header, bytes.NewReader(raw.Body)) +} + +func ConvertFromRawFrame(raw *frame.RawFrame) (*frame.Frame, error) { + return codec.ConvertFromRawFrame(raw) +} + +func ConvertToRawFrame(f *frame.Frame) (*frame.RawFrame, error) { + return codec.ConvertToRawFrame(f) +} + +func EncodeFrame(f *frame.Frame, writer io.Writer) error { + return codec.EncodeFrame(f, writer) +} + +func EncodeRawFrame(f *frame.RawFrame, writer io.Writer) error { + return codec.EncodeRawFrame(f, writer) +} + +var primitiveCodecs = map[datatype.DataType]datacodec.Codec{ + datatype.Ascii: datacodec.Ascii, + datatype.Bigint: datacodec.Bigint, + datatype.Blob: datacodec.Blob, + datatype.Boolean: datacodec.Boolean, + datatype.Counter: datacodec.Counter, + datatype.Decimal: datacodec.Decimal, + datatype.Double: datacodec.Double, + datatype.Float: datacodec.Float, + datatype.Inet: datacodec.Inet, + datatype.Int: datacodec.Int, + datatype.Smallint: datacodec.Smallint, + datatype.Varchar: datacodec.Varchar, + datatype.Timeuuid: datacodec.Timeuuid, + datatype.Tinyint: datacodec.Tinyint, + datatype.Uuid: datacodec.Uuid, + datatype.Varint: datacodec.Varint, +} + +func CodecFromDataType(dt datatype.DataType) (datacodec.Codec, error) { + switch dt.GetDataTypeCode() { + case primitive.DataTypeCodeList: + listType := dt.(datatype.ListType) + return datacodec.NewList(datatype.NewListType(listType.GetElementType())) + case primitive.DataTypeCodeSet: + setType := dt.(datatype.SetType) + return datacodec.NewSet(datatype.NewListType(setType.GetElementType())) + case primitive.DataTypeCodeMap: + mapType := dt.(datatype.MapType) + return datacodec.NewMap(datatype.NewMapType(mapType.GetKeyType(), mapType.GetValueType())) + default: + codec, ok := primitiveCodecs[dt] + if !ok { + return nil, fmt.Errorf("no codec for data type %v", dt) + } + return codec, nil + } +}