Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow URL encoded dataset in libhoney endpoint paths #1199

Merged
merged 9 commits into from
Jun 17, 2024
25 changes: 21 additions & 4 deletions route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ func (r *Router) LnS(incomingOrPeer string) {

// require an auth header for events and batches
authedMuxxer := muxxer.PathPrefix("/1/").Methods("POST").Subrouter()
authedMuxxer.UseEncodedPath()
authedMuxxer.Use(r.apiKeyChecker)

// handle events and batches
Expand Down Expand Up @@ -388,8 +389,10 @@ func (r *Router) requestToEvent(req *http.Request, reqBod []byte) (*types.Event,
sampleRate = 1
}
eventTime := getEventTime(req.Header.Get(types.TimestampHeader))
vars := mux.Vars(req)
dataset := vars["datasetName"]
dataset, err := getDatasetFromRequest(req)
if err != nil {
return nil, err
}

apiHost, err := r.Config.GetHoneycombAPI()
if err != nil {
Expand Down Expand Up @@ -678,8 +681,10 @@ func (r *Router) batchedEventToEvent(req *http.Request, bev batchedEvent, apiKey
eventTime := bev.getEventTime()
// TODO move the following 3 lines outside of this loop; they could be done
// once for the entire batch instead of in every event.
vars := mux.Vars(req)
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
dataset := vars["datasetName"]
dataset, err := getDatasetFromRequest(req)
if err != nil {
return nil, err
}
apiHost, err := r.Config.GetHoneycombAPI()
if err != nil {
return nil, err
Expand Down Expand Up @@ -969,3 +974,15 @@ func (r *Router) AddOTLPMuxxer(muxxer *mux.Router) {
otlpMuxxer.HandleFunc("/logs", r.postOTLPLogs).Name("otlp_logs")
otlpMuxxer.HandleFunc("/logs/", r.postOTLPLogs).Name("otlp_logs")
}

func getDatasetFromRequest(req *http.Request) (string, error) {
dataset := mux.Vars(req)["datasetName"]
if dataset == "" {
return "", fmt.Errorf("missing dataset name")
}
dataset, err := url.PathUnescape(dataset)
if err != nil {
return "", err
}
return dataset, nil
}
107 changes: 107 additions & 0 deletions route/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -612,3 +613,109 @@ func TestGRPCHealthProbeWatch(t *testing.T) {
sentMessage := mockServer.GetSentMessages()[0]
assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING, sentMessage.Status)
}

func TestGetDatasetFromRequest(t *testing.T) {
testCases := []struct {
name string
datasetName string
expectedDatasetName string
expectedError error
}{
{
name: "empty dataset name",
datasetName: "",
expectedError: fmt.Errorf("missing dataset name"),
},
{
name: "dataset name with invalid URL encoding",
datasetName: "foo%2",
expectedError: url.EscapeError("%2"),
},
{
name: "normal dataset name",
datasetName: "foo",
expectedDatasetName: "foo",
},
{
name: "dataset name with numbers",
datasetName: "foo123",
expectedDatasetName: "foo123",
},
{
name: "dataset name with hyphen",
datasetName: "foo-bar",
expectedDatasetName: "foo-bar",
},
{
name: "dataset name with underscore",
datasetName: "foo_bar",
expectedDatasetName: "foo_bar",
},
{
name: "dataset name with tilde",
datasetName: "foo~bar",
expectedDatasetName: "foo~bar",
},
{
name: "dataset name with period",
datasetName: "foo.bar",
expectedDatasetName: "foo.bar",
},
{
name: "dataset name with URL encoded hyphen",
datasetName: "foo%2Dbar",
expectedDatasetName: "foo-bar",
},
{
name: "dataset name with URL encoded underscore",
datasetName: "foo%5Fbar",
expectedDatasetName: "foo_bar",
},
{
name: "dataset name with URL encoded tilde",
datasetName: "foo%7Ebar",
expectedDatasetName: "foo~bar",
},
{
name: "dataset name with URL encoded period",
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
datasetName: "foo%2Ebar",
expectedDatasetName: "foo.bar",
},
{
name: "dataset name with URL encoded forward slash",
datasetName: "foo%2Fbar",
expectedDatasetName: "foo/bar",
},
{
name: "dataset name with URL encoded colon",
datasetName: "foo%3Abar",
expectedDatasetName: "foo:bar",
},
{
name: "dataset name with URL encoded square brackets",
datasetName: "foo%5Bbar%5D",
expectedDatasetName: "foo[bar]",
},
{
name: "dataset name with URL encoded parentheses",
datasetName: "foo%28bar%29",
expectedDatasetName: "foo(bar)",
},
{
name: "dataset name with URL encoded curly braces",
datasetName: "foo%7Bbar%7D",
expectedDatasetName: "foo{bar}",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req, _ := http.NewRequest("GET", "/1/events/dataset", nil)
req = mux.SetURLVars(req, map[string]string{"datasetName": tc.datasetName})

dataset, err := getDatasetFromRequest(req)
assert.Equal(t, tc.expectedError, err)
assert.Equal(t, tc.expectedDatasetName, dataset)
})
}
}
Loading