diff --git a/client/proxy.go b/client/proxy.go index 1b9270e..604d1c7 100644 --- a/client/proxy.go +++ b/client/proxy.go @@ -21,6 +21,7 @@ import ( "net" "net/http" "net/http/httputil" + "strings" "github.com/golang/glog" "github.com/pkg/errors" @@ -30,7 +31,6 @@ import ( "golang.stackrox.io/grpc-http1/internal/grpcweb" "golang.stackrox.io/grpc-http1/internal/httputils" "golang.stackrox.io/grpc-http1/internal/pipeconn" - "golang.stackrox.io/grpc-http1/internal/stringutils" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" @@ -49,7 +49,7 @@ func modifyResponse(resp *http.Response) error { // Make sure headers do not get flushed, as otherwise the gRPC client will complain about missing trailers. resp.Header.Set(dontFlushHeadersHeaderKey, "true") } - contentType, contentSubType := stringutils.Split2(resp.Header.Get("Content-Type"), "+") + contentType, contentSubType, _ := strings.Cut(resp.Header.Get("Content-Type"), "+") if contentType != "application/grpc-web" { // No modification necessary if we aren't handling a gRPC web response. return nil diff --git a/internal/grpcweb/response_writer.go b/internal/grpcweb/response_writer.go index 5f7df64..ea740a8 100644 --- a/internal/grpcweb/response_writer.go +++ b/internal/grpcweb/response_writer.go @@ -18,10 +18,8 @@ import ( "bytes" "encoding/binary" "net/http" + "slices" "strings" - - "golang.stackrox.io/grpc-http1/internal/sliceutils" - "golang.stackrox.io/grpc-http1/internal/stringutils" ) type responseWriter struct { @@ -69,12 +67,12 @@ func (w *responseWriter) prepareHeadersIfNecessary() { } hdr := w.w.Header() - w.announcedTrailers = sliceutils.ShallowClone(hdr["Trailer"]) + w.announcedTrailers = slices.Clone(hdr["Trailer"]) // Trailers are sent in a data frame, so don't announce trailers as otherwise downstream proxies might get confused. hdr.Del("Trailer") // "Downgrade" response content type to grpc-web. - contentType, contentSubtype := stringutils.Split2(hdr.Get("Content-Type"), "+") + contentType, contentSubtype, _ := strings.Cut(hdr.Get("Content-Type"), "+") respContentType := "application/grpc-web" if contentType == "application/grpc" && contentSubtype != "" { diff --git a/internal/sliceutils/utils.go b/internal/sliceutils/utils.go deleted file mode 100644 index 1c89782..0000000 --- a/internal/sliceutils/utils.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2022 StackRox 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 sliceutils - -// ShallowClone clones a slice, creating a new slice -// and copying the contents of the underlying array. -// If `in` is a nil slice, a nil slice is returned. -// If `in` is an empty slice, an empty slice is returned. -func ShallowClone[T any](in []T) []T { - if in == nil { - return nil - } - if len(in) == 0 { - return []T{} - } - out := make([]T, len(in)) - copy(out, in) - return out -} - -// Find returns, given a slice and an element, the first index of elem in the slice, or -1 if the slice does -// not contain elem. -func Find[T comparable](slice []T, elem T) int { - for i, sliceElem := range slice { - if sliceElem == elem { - return i - } - } - return -1 -} diff --git a/internal/sliceutils/utils_test.go b/internal/sliceutils/utils_test.go deleted file mode 100644 index fad504f..0000000 --- a/internal/sliceutils/utils_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2022 StackRox 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 sliceutils - -import ( - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestShallowClone(t *testing.T) { - cases := []struct { - slice []string - expectedSlice []string - }{ - { - slice: nil, - expectedSlice: nil, - }, - { - slice: []string{}, - expectedSlice: []string{}, - }, - { - slice: []string{"A", "B", "C"}, - expectedSlice: []string{"A", "B", "C"}, - }, - } - for _, c := range cases { - t.Run(fmt.Sprintf("%s - %s", strings.Join(c.slice, " "), strings.Join(c.expectedSlice, " ")), func(t *testing.T) { - assert.Equal(t, c.expectedSlice, ShallowClone(c.slice)) - }) - } -} - -func TestFind(t *testing.T) { - cases := []struct { - slice []string - elem string - expectedIndex int - }{ - { - slice: nil, - elem: "A", - expectedIndex: -1, - }, - { - slice: []string{}, - elem: "A", - expectedIndex: -1, - }, - { - slice: []string{"A", "B", "C"}, - elem: "", - expectedIndex: -1, - }, - { - slice: []string{"A", "B", "C"}, - elem: "D", - expectedIndex: -1, - }, - { - slice: []string{"A", "B", "C"}, - elem: "B", - expectedIndex: 1, - }, - } - for _, c := range cases { - t.Run(fmt.Sprintf("%s in %s - %d", c.elem, strings.Join(c.slice, " "), c.expectedIndex), func(t *testing.T) { - assert.Equal(t, c.expectedIndex, Find(c.slice, c.elem)) - }) - } -} diff --git a/internal/stringutils/split.go b/internal/stringutils/split.go deleted file mode 100644 index b05db09..0000000 --- a/internal/stringutils/split.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2020 StackRox 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 stringutils - -import "strings" - -// Split2 splits the given string at the given separator, returning the part before and after the separator as two -// separate return values. -// If the string does not contain `sep`, the entire string is returned as the first return value. -func Split2(str string, sep string) (string, string) { - splitIdx := strings.Index(str, sep) - if splitIdx == -1 { - return str, "" - } - return str[:splitIdx], str[splitIdx+len(sep):] -} diff --git a/internal/stringutils/split_test.go b/internal/stringutils/split_test.go deleted file mode 100644 index 757dc17..0000000 --- a/internal/stringutils/split_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2020 StackRox 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 stringutils - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSplit2(t *testing.T) { - for _, testCase := range []struct { - s string - sep string - expected []string - }{ - {"Helo", "l", []string{"He", "o"}}, - {"Hello", "l", []string{"He", "lo"}}, - {"Hello", "ll", []string{"He", "o"}}, - {"", "a", []string{"", ""}}, - } { - c := testCase - t.Run(fmt.Sprintf("%+v", c), func(t *testing.T) { - first, second := Split2(c.s, c.sep) - assert.Equal(t, c.expected, []string{first, second}) - }) - } -} diff --git a/server/server.go b/server/server.go index 0edf739..65fff64 100644 --- a/server/server.go +++ b/server/server.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "net/http" + "slices" "strings" "sync" "unicode" @@ -27,8 +28,6 @@ import ( "golang.stackrox.io/grpc-http1/internal/grpcweb" "golang.stackrox.io/grpc-http1/internal/grpcwebsocket" "golang.stackrox.io/grpc-http1/internal/size" - "golang.stackrox.io/grpc-http1/internal/sliceutils" - "golang.stackrox.io/grpc-http1/internal/stringutils" "google.golang.org/grpc" ) @@ -108,11 +107,11 @@ func handleGRPCWeb(w http.ResponseWriter, req *http.Request, validPaths map[stri } acceptedContentTypes := strings.FieldsFunc(strings.Join(req.Header["Accept"], ","), spaceOrComma) - acceptGRPCWeb := sliceutils.Find(acceptedContentTypes, "application/grpc-web") != -1 + acceptGRPCWeb := slices.Index(acceptedContentTypes, "application/grpc-web") != -1 // The standard gRPC client doesn't actually send an `Accept: application/grpc` header, so always assume // the client accepts gRPC _unless_ it explicitly specifies an `application/grpc-web` accept header // WITHOUT an `application/grpc` accept header. - acceptGRPC := !acceptGRPCWeb || sliceutils.Find(acceptedContentTypes, "application/grpc") != -1 + acceptGRPC := !acceptGRPCWeb || slices.Index(acceptedContentTypes, "application/grpc") != -1 // Only consider sending a gRPC response if we are not told to prefer gRPC-Web or the client doesn't support // gRPC-Web. @@ -197,7 +196,7 @@ func CreateDowngradingHandler(grpcSrv *grpc.Server, httpHandler http.Handler, op } func isContentTypeValid(contentType string) bool { - ct, _ := stringutils.Split2(contentType, "+") + ct, _, _ := strings.Cut(contentType, "+") return ct == "application/grpc" || ct == "application/grpc-web" } diff --git a/server/websocket_writer.go b/server/websocket_writer.go index 37b803b..90b7eb4 100644 --- a/server/websocket_writer.go +++ b/server/websocket_writer.go @@ -18,11 +18,11 @@ import ( "bytes" "io" "net/http" + "slices" "strings" "github.com/golang/glog" "golang.stackrox.io/grpc-http1/internal/grpcproto" - "golang.stackrox.io/grpc-http1/internal/sliceutils" ) // wsResponseWriter is a http.ResponseWriter to be used for WebSocket connections. @@ -67,7 +67,7 @@ func (w *wsResponseWriter) WriteHeader(statusCode int) { } hdr := w.header - w.announcedTrailers = sliceutils.ShallowClone(hdr["Trailer"]) + w.announcedTrailers = slices.Clone(hdr["Trailer"]) // Trailers will be sent un-announced in non-Trailers-only responses. hdr.Del("Trailer")