diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index 38e90dc1eb5..736b53db911 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -441,8 +441,8 @@ func (g *generator) insertDynamicRequestHeaders(m *descriptorpb.MethodDescriptor return nil } - g.printf(`routingHeaders := ""`) - g.printf("routingHeadersMap := make(map[string]string)") + g.printf(`var routingHeaders []string`) + g.printf("seen := make(map[string]bool)") for i := range headers { namedCaptureRegex := headers[i][0] field := headers[i][1] @@ -455,15 +455,14 @@ func (g *generator) insertDynamicRequestHeaders(m *descriptorpb.MethodDescriptor } // There could be an edge case where the request field is empty and the path template is a wildcard. In that case, we still don't want to send an empty header name. g.printf("if reg := regexp.MustCompile(%q); reg.MatchString(%s) && len(%s) > 0 {", namedCaptureRegex, accessor, regexHelper) - g.printf(" routingHeadersMap[%q] = %s", headerName, regexHelper) + g.printf(" if !seen[%q] {", headerName) + g.printf(" routingHeaders = append(routingHeaders, fmt.Sprintf(\"%%s=%%s\", %q, %s))", headerName, regexHelper) + g.printf(" seen[%q] = true", headerName) + g.printf(" }") g.printf("}") } - g.printf("for headerName, headerValue := range routingHeadersMap {") - g.printf(` routingHeaders = fmt.Sprintf("%%s%%s=%%s&", routingHeaders, headerName, headerValue)`) - g.printf("}") - g.printf(`routingHeaders = strings.TrimSuffix(routingHeaders, "&")`) + g.printf(`hds := []string{"x-goog-request-params", strings.Join(routingHeaders, "&")}`) g.imports[pbinfo.ImportSpec{Path: "strings"}] = true - g.printf(`hds := []string{"x-goog-request-params", routingHeaders}`) g.imports[pbinfo.ImportSpec{Path: "regexp"}] = true return nil } diff --git a/internal/gengapic/testdata/method_GetAnotherThing.want b/internal/gengapic/testdata/method_GetAnotherThing.want index 1849b02789a..098b5595e9d 100644 --- a/internal/gengapic/testdata/method_GetAnotherThing.want +++ b/internal/gengapic/testdata/method_GetAnotherThing.want @@ -1,32 +1,49 @@ func (c *fooGRPCClient) GetAnotherThing(ctx context.Context, req *mypackagepb.InputType, opts ...gax.CallOption) (*mypackagepb.OutputType, error) { - routingHeaders := "" - routingHeadersMap := make(map[string]string) + var routingHeaders []string + seen := make(map[string]bool) if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetOther()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1])) > 0 { - routingHeadersMap["other"] = url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]) + if !seen["other"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "other", url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]))) + seen["other"] = true + } } if reg := regexp.MustCompile("(?Pprojects/[^/]+)/foos"); reg.MatchString(req.GetOther()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1])) > 0 { - routingHeadersMap["name"] = url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]) + if !seen["name"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "name", url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]))) + seen["name"] = true + } } if reg := regexp.MustCompile("(?Pprojects/[^/]+)/bars/[^/]+(?:/.*)?"); reg.MatchString(req.GetAnother()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1])) > 0 { - routingHeadersMap["foo_name"] = url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]) + if !seen["foo_name"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "foo_name", url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]))) + seen["foo_name"] = true + } } if reg := regexp.MustCompile("(?Pprojects/[^/]+/foos/[^/]+)/bars/[^/]+(?:/.*)?"); reg.MatchString(req.GetAnother()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1])) > 0 { - routingHeadersMap["foo_name"] = url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]) + if !seen["foo_name"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "foo_name", url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]))) + seen["foo_name"] = true + } } if reg := regexp.MustCompile("(?P.*)"); reg.MatchString(req.GetAnother()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1])) > 0 { - routingHeadersMap["foo_name"] = url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]) + if !seen["foo_name"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "foo_name", url.QueryEscape(reg.FindStringSubmatch(req.GetAnother())[1]))) + seen["foo_name"] = true + } } if reg := regexp.MustCompile("(?P.*)"); reg.MatchString(req.GetFieldName().GetNested()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1])) > 0 { - routingHeadersMap["nested_name"] = url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1]) + if !seen["nested_name"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "nested_name", url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1]))) + seen["nested_name"] = true + } } if reg := regexp.MustCompile("(?Pprojects/[^/]+)/bars"); reg.MatchString(req.GetFieldName().GetNested()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1])) > 0 { - routingHeadersMap["part_of_nested"] = url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1]) + if !seen["part_of_nested"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "part_of_nested", url.QueryEscape(reg.FindStringSubmatch(req.GetFieldName().GetNested())[1]))) + seen["part_of_nested"] = true + } } - for headerName, headerValue := range routingHeadersMap { - routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue) - } - routingHeaders = strings.TrimSuffix(routingHeaders, "&") - hds := []string{"x-goog-request-params", routingHeaders} + hds := []string{"x-goog-request-params", strings.Join(routingHeaders, "&")} hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) diff --git a/internal/gengapic/testdata/rest_HttpBodyRPC.want b/internal/gengapic/testdata/rest_HttpBodyRPC.want index 691d0a492fc..57eba9716c6 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -12,16 +12,15 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. baseUrl.Path += fmt.Sprintf("/v1/foo") // Build HTTP headers from client and context metadata. - routingHeaders := "" - routingHeadersMap := make(map[string]string) + var routingHeaders []string + seen := make(map[string]bool) if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetOther()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1])) > 0 { - routingHeadersMap["other"] = url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]) - } - for headerName, headerValue := range routingHeadersMap { - routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue) + if !seen["other"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "other", url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]))) + seen["other"] = true + } } - routingHeaders = strings.TrimSuffix(routingHeaders, "&") - hds := []string{"x-goog-request-params", routingHeaders} + hds := []string{"x-goog-request-params", strings.Join(routingHeaders, "&")} hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index 1cc8d1d668c..180b7f52e20 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -12,16 +12,15 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt baseUrl.Path += fmt.Sprintf("/v1/foo") // Build HTTP headers from client and context metadata. - routingHeaders := "" - routingHeadersMap := make(map[string]string) + var routingHeaders []string + seen := make(map[string]bool) if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetOther()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1])) > 0 { - routingHeadersMap["other"] = url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]) - } - for headerName, headerValue := range routingHeadersMap { - routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue) + if !seen["other"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "other", url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]))) + seen["other"] = true + } } - routingHeaders = strings.TrimSuffix(routingHeaders, "&") - hds := []string{"x-goog-request-params", routingHeaders} + hds := []string{"x-goog-request-params", strings.Join(routingHeaders, "&")} hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want index dbf6297b6b9..92eababa2d8 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -20,16 +20,15 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga baseUrl.RawQuery = params.Encode() // Build HTTP headers from client and context metadata. - routingHeaders := "" - routingHeadersMap := make(map[string]string) + var routingHeaders []string + seen := make(map[string]bool) if reg := regexp.MustCompile("(.*)"); reg.MatchString(req.GetOther()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1])) > 0 { - routingHeadersMap["other"] = url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]) - } - for headerName, headerValue := range routingHeadersMap { - routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue) + if !seen["other"] { + routingHeaders = append(routingHeaders, fmt.Sprintf("%s=%s", "other", url.QueryEscape(reg.FindStringSubmatch(req.GetOther())[1]))) + seen["other"] = true + } } - routingHeaders = strings.TrimSuffix(routingHeaders, "&") - hds := []string{"x-goog-request-params", routingHeaders} + hds := []string{"x-goog-request-params", strings.Join(routingHeaders, "&")} hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json")