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

headers set by the subgraph service should not be propagated from the client query by the headers plugin #4398

Closed
heathprovost opened this issue Dec 15, 2023 · 6 comments · Fixed by #4535
Assignees
Labels

Comments

@heathprovost
Copy link

heathprovost commented Dec 15, 2023

Describe the bug
Requests that already have the accept header set to the value application/json, application/graphql-response+json on the inbound request to the router are getting multiple accept headers appended to the request incorrectly.

In other words if the original request contains this:

accept: application/json, application/graphql-response+json

The router ends up sending this to the subgraph:

accept: application/json, application/graphql-response+json
accept: application/json
accept: application/graphql-response+json

Unfortunately, the default behavior of the C# GraphQL.Client is to set the accept headers exactly this way : https://github.com/graphql-dotnet/graphql-client/blob/fbf37dfff2830e36a45296079bac27ec60fad550/src/GraphQL.Client/GraphQLHttpRequest.cs#L40

This behavior started in v1.35.0 of the router, and seems to me to be attributable to this changeset: 9a44bed#diff-6ef5a208ca8622f30eef88f75c18566e0304d59856b66293dcd6811555e6382e which changed the method used to set the Accept header from an .insert() to an .append(). The older behavior of just using .insert() avoided the issue because it simply overwrote the entire accept header, which is probably not the desired behavior either.

To Reproduce

  1. Setup services normally (using router or cloud router v1.35.0)
  2. Submit a request including the header Accept: application/json, application/graphql-response+json
  3. View the request coming out of the router, it is incorrectly appending additional headers when it should not be.

Expected behavior
The router should see that the headers it is trying to set are already there and should do nothing in that scenario.

Additional context
This issue causes breakage with subgraphs running inside of lambdas behind ALBs in AWS because of how request headers are marshaled when they are converted from an http request to a lambda request. AWS lambda does not properly marshal the requests when they contain both multivalue headers AND multiple of the same header key (it works with either or, but not both) .

We are currently working around this issue by overriding the default behavior of the C# GraphQL.Client and stripping the Accept header off before the request goes out. Not a great solution obviously.

@Geal
Copy link
Contributor

Geal commented Dec 18, 2023

do you have any header propagation rules in the router's configuration? https://www.apollographql.com/docs/router/configuration/header-propagation

@heathprovost
Copy link
Author

heathprovost commented Dec 18, 2023

We are currently just doing this:

headers:
  all:
    request:
      - propagate:
          matching: .*

I realize this is another way we could probably work around the issue, but it did not seem like a solution.

@bnjjj bnjjj added the good first issue Good for newcomers label Dec 19, 2023
@Geal
Copy link
Contributor

Geal commented Dec 19, 2023

ok so the issue here is that headers that are set by the router as part of the subgraph request, like Accept or Accept-Encoding, should not be part of propagated headers

@Geal Geal changed the title Change introduced in v1.35.0 breaking accept header parsing in subgraph when run in AWS lambda headers set by the subgraph service should not be propagated from the client query by the headers plugin Dec 19, 2023
@heathprovost
Copy link
Author

heathprovost commented Dec 20, 2023

ok so the issue here is that headers that are set by the router as part of the subgraph request, like Accept or Accept-Encoding, should not be part of propagated headers

I dont think so no. They should be part of the propogated headers, they just should not be blindly added as well. In other words these headers should be forwarded if and when they exist, and when they do NOT exist they should be added to the outbound request.

What is happening now is that the router is doing both. It is propagating them from the inbound request AND adding them again.

What should happen (imo) is that the router should only add them if they do not already exist on the inbound request.

Alternately, don't try to set the Accept header at all inside the router and require the caller to do it.

@abernix
Copy link
Member

abernix commented Jan 8, 2024

This probably deserves some more investigation, and perhaps should be considered together with behaviors in #4420.

@Geal
Copy link
Contributor

Geal commented Jan 15, 2024

the Accept and Accept-Encoding headers must not be propagated from the client query, because they define what the router can accept in the subgraph response (The values are actually hardcoded:

request
.headers_mut()
.insert(CONTENT_TYPE, APPLICATION_JSON_HEADER_VALUE.clone());
request
.headers_mut()
.append(ACCEPT, APPLICATION_JSON_HEADER_VALUE.clone());
request
.headers_mut()
.append(ACCEPT, APP_GRAPHQL_JSON.clone());
request
.headers_mut()
.insert(ACCEPT_ENCODING, ACCEPTED_ENCODINGS.clone());
). In that matter the router cannot be considered like a proxy that forwards the client query with minimal modification to a backend, it is doing its own requests to the subgraphs.
And this allows us to handle cases like a client supporting compression while a subgraph does not.

do you see a case where it is necessary to override the router headers to forward those clients headers unchanged?

@Geal Geal closed this as completed in a0b459b Jan 25, 2024
This was referenced Feb 1, 2024
Geal added a commit that referenced this issue Feb 5, 2024
## 🚀 Features

### Specify Trace ID Formatting ([PR
#4530](#4530))

This adds the ability to specify the format of the trace ID in the
response headers of the supergraph service.

An example configuration making use of this feature is shown below:
```yaml
telemetry:
  apollo:
    client_name_header: name_header
    client_version_header: version_header
  exporters:
    tracing:
      experimental_response_trace_id:
        enabled: true
        header_name: trace_id
        format: decimal # Optional, defaults to hexadecimal
```

If the format is not specified, then the trace ID will continue to be in
hexadecimal format.

By [@nicholascioli](https://github.com/nicholascioli) in
#4530

### Introduce support for progressive `@override` ([PR
#4521](#4521))

The change brings support for progressive `@override`, which allows
dynamically overriding root fields and entity fields in the schema. This
feature is enterprise only and requires a license key to be used.

A new `label` argument is added to the `@override` directive in order to
indicate the field is dynamically overridden. Labels can come in two
forms:
1) String matching the form `percent(x)`: The router resolves these
labels based on the `x` value. For example, `percent(50)` will route 50%
of requests to the overridden field and 50% of requests to the original
field.
2) Arbitrary string matching the regex `^[a-zA-Z][a-zA-Z0-9_-:./]*$`:
These labels are expected to be resolved externally via coprocessor. A
coprocessor a supergraph request hook can inspect and modify the context
of a request in order to inform the router which labels to use during
query planning.

Please consult the docs for more information on how to use this feature
and how to implement a coprocessor for label resolution.

By [@trevorscheer](https://github.com/TrevorScheer) in
#4521

### Add a new selector to get all baggage key values in span attributes
([Issue #4425](#4425))

If you have several baggage items and would like to add all of them
directly as span attributes, for example `baggage: my_item=test,
my_second_item=bar` will add 2 span attributes `my_item=test` and
`my_second_item=bar`.

Example of configuration:

```yaml
telemetry:
  instrumentation:
    spans:
      router:
        attributes:
          baggage: true
```

By [@bnjjj](https://github.com/bnjjj) in
#4537

### Create a trace during router creation and plugin initialization
([Issue #4472](#4472))

When the router starts or reload, it will now generate a trace with
spans for query planner creation, schema parsing, plugin initialization
and request pipeline creation. This will help in debugging any issue
during startup, especially in plugins creation

By [@Geal](https://github.com/Geal) in
#4480

### Add static attribute on specific span in telemetry settings ([Issue
#4561](#4561))

Example of configuration:

```yaml
telemetry:
  instrumentation:
    spans:
      router:
        attributes:
          "my_attribute": "constant_value"
      supergraph:
        attributes:
          "my_attribute": "constant_value"
      subgraph:
        attributes:
          "my_attribute": "constant_value"
```

By [@bnjjj](https://github.com/bnjjj) in
#4566

## 🐛 Fixes

### Order HPA target so that kubernetes does not rewrite ([Issue
#4435](#4435))

This update addresses an OutOfSync issue in ArgoCD applications when
Horizontal Pod Autoscaler (HPA) is configured with both memory and CPU
limits.
Previously, the live and desired manifests within Kubernetes were not
consistently sorted, leading to persistent OutOfSync states in ArgoCD.
This change implements a sorting mechanism for HPA targets within the
Helm chart, ensuring alignment with Kubernetes' expected order.
This fix proactively resolves the sync discrepancies while using HPA,
circumventing the need to wait for Kubernetes' issue resolution
(kubernetes/kubernetes#74099).

By [@cyberhck](https://github.com/cyberhck) in
#4436

### Reactivate log events in traces ([PR
#4486](#4486))

This fixes a regression introduced in #2999, where events were not sent
with traces anymore due to too aggressive sampling

By [@Geal](https://github.com/Geal) in
#4486

### Fix inconsistency in environment variable parsing for telemetry
([Issue
#3203](https://github.com/apollographql/router/issues/ISSUE_NUMBER))

Previously, the router would complain when using the rover
recommendation of `APOLLO_TELEMETRY_DISABLED=1` environment
variable. Now any non-falsey value can be used, such as 1, yes, on,
etc..

By [@nicholascioli](https://github.com/nicholascioli) in
#4549

### Store static pages in `Bytes` structure to avoid expensive
allocation per request ([PR
#4528](#4528))

The `CheckpointService` created by the `StaticPageLayer` caused a
non-insignificant amount of memory to be allocated on every request. The
service stack gets cloned on every request, and so does the rendered
template.

The template is now stored in a `Bytes` struct instead which is cheap to
clone.

By [@xuorig](https://github.com/xuorig) in
#4528

### Fix header propagation issues ([Issue
#4312](#4312)), ([Issue
#4398](#4398))

This fixes two header propagation issues:
* if a client request header has already been added to a subgraph
request due to another header propagation rule, then it is only added
once
* `Accept`, `Accept-Encoding` and `Content-Encoding` were not in the
list of reserved headers that cannot be propagated. They are now in that
list because those headers are set explicitely by the Router in its
subgraph requests

There is a potential regression: if a router deployment was accidentally
relying on header propagation to compress subgraph requests, then it
will not work anymore because `Content-Encoding` is not propagated
anymore. Instead it should be set up from the `traffic_shaping` section
of the Router configuration:

```yaml
traffic_shaping:
  all:
    compression: gzip
  subgraphs: # Rules applied to requests from the router to individual subgraphs
    products:
      compression: identity
```

By [@Geal](https://github.com/Geal) in
#4535

## 🧪 Experimental

### Move cacheability metrics to the entity cache plugin ([Issue
#4253](#4253))

The metric was generated in the telemetry plugin before, but it was not
very convenient to keep it there. This adds more configuration:
- enable or disable the metrics
- set the metrics storage TTL (default is 60s)
- the metric's typename attribute is disabled by default. Activating it
can greatly increase the cardinality

This also includes some cleanup and performance improvements

By [@Geal](https://github.com/Geal) in
#4469

---------

Co-authored-by: Edward Huang <edward.huang@apollographql.com>
Co-authored-by: Jeremy Lempereur <jeremy.lempereur@iomentum.com>
Co-authored-by: Jesse Rosenberger <git@jro.cc>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants