vercel / next.js

The React Framework
https://nextjs.org
MIT License
127k stars 26.99k forks source link

Switch from nodejs16 to nodejs18 triggers Zlib error: Incorrect header check error #51871

Open ricardomatias opened 1 year ago

ricardomatias commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: x64
      Version: Darwin Kernel Version 22.5.0: Mon Apr 24 20:51:50 PDT 2023; root:xnu-8796.121.2~5/RELEASE_X86_64
    Binaries:
      Node: 18.16.0
      npm: 9.5.1
      Yarn: N/A
      pnpm: N/A
    Relevant packages:
      next: 13.4.5
      eslint-config-next: 13.4.5
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.1.3
     @apollo/client: 3.7.16

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue or a replay of the bug

null

To Reproduce

It's not trivial to create a reproducible example. All the details are in the description.

Describe the Bug

Issue Description

Summary

The following code used to work in NodeJS 16 (16.20.0 tested), but when trying to use it with NodeJS 18 there's the following error:

Error:

[http] [Network error]: TypeError: terminated
[http] Error: incorrect header check
[http]     at Zlib.zlibOnError [as onerror] (node:zlib:189:17) {
[http]   errno: -3,
[http]   code: 'Z_DATA_ERROR'
[http] }

And the header in question:

{
  method: 'POST',
  headers: [Object: null prototype] {
    accept: '*/*',
    'content-type': 'application/json',
    cookie: 'XXX_session=.....',
    authorization: 'Basic .....'
  },
  credentials: 'include',
  signal: AbortSignal { },
  body: '{"operationName":"getRoot","variables":{}"}'
}

This is the file that defines the authentication procedures and the only place I have any control regarding headers.

apollo.ts

let apolloClient: ApolloClient<NormalizedCacheObject> | undefined;

const errorLink = onError(({ graphQLErrors, networkError, response }) => {...);

const authLink = new ApolloLink((operation, forward) => {
    const AUTHENTICATION = toBase64(CREDENTIALS);

    operation.setContext(({ headers = {} }) => ({
        headers: {
            ...headers,
            authorization: `Basic ${AUTHENTICATION}`, // however you get your token
        },
    }));

    return forward(operation);
});

function createApolloClient(headers: IncomingHttpHeaders | null = null) {
    const cookie = headers?.cookie ?? '';
    const ssrMode = typeof window === 'undefined';

    return new ApolloClient({
        ssrMode,
        defaultOptions: {
            query: {
                errorPolicy: 'all',
            },
        },
        link: from([
            errorLink,
            authLink,
            createHttpLink({
                uri: `https://${API_ENDPOINT}/graphql`,
                credentials: 'include',
                headers: {
                    cookie,
                },
            }),
        ]),
        cache: new InMemoryCache({...}),
        connectToDevTools: true,
    });
}

type InitialState = NormalizedCacheObject | undefined;
interface IInitializeApollo {
    headers?: IncomingHttpHeaders | null;
    initialState?: InitialState | null;
}

export function initializeApollo(
    { headers, initialState }: IInitializeApollo = {
        headers: null,
        initialState: null,
    }
): ApolloClient<NormalizedCacheObject> {
    const _apolloClient = apolloClient ?? createApolloClient(headers);
    // If your page has Next.js data fetching methods that use Apollo Client, the initial state
    // get hydrated here
    if (initialState) {
        // Get existing cache, loaded during client side data fetching
        const existingCache = _apolloClient.extract();

        // Merge the existing cache into data passed from getStaticProps/getServerSideProps
        const data = merge(initialState, existingCache, {
            // combine arrays using object equality (like in sets)
            arrayMerge: (destinationArray, sourceArray) => [
                ...sourceArray,
                ...destinationArray.filter((d) => sourceArray.every((s) => !isEqual(d, s))),
            ],
        });

        // Restore the cache with the merged data
        _apolloClient.cache.restore(data);
    }

    // For SSG and SSR always create a new Apollo Client
    if (typeof window === 'undefined') return _apolloClient;
    // Create the Apollo Client once in the client
    if (!apolloClient) apolloClient = _apolloClient;

    return _apolloClient;
}

export function useApollo(initialState = {}): ApolloClient<NormalizedCacheObject> {
    const store = useMemo(() => initializeApollo({ headers: null, initialState }), [initialState]);
    return store;
}

I have a login page, that is able to fetch a cookie from the API endpoint, but then a subsequent request within getServerSideProps (more precisely, apolloClient.query) triggers the error.

Expected Behavior

That there are no zlib errors like with previous Node versions.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

JesseKoldewijn commented 1 year ago

What version of apollo client are you using? This error used to be caused by the lack of support for react 18.x but this got fixed in one of the recent'ish releases.

ricardomatias commented 1 year ago

@JesseKoldewijn I'm using the latest version 3.7.16

thien-do commented 1 year ago

In my case this happened when the server's content type header is not what actually is. For example the content type says it's gzip but the content is without compression, thus causing the decompression failed.

To exclude/verify if this is your case, you can add this header temporarily and try again:

"Accept-Encoding": "identity",