首页 > 解决方案 > 为什么我收到握手失败 Apollo 客户端错误(带有 EPROTO errno)?

问题描述

我正在使用apollo-boost: ^0.4.3next.js。我最近在我的网站上添加了 cookie 身份验证。最大的变化是添加标题和credentials: 'include'构造ApolloClient函数。此外,在server.jswhich starts express server for next.js 我添加了这个块:

const getServer = (isDevHttps, expressApp) => {
  if (isDevHttps) {
    process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
    const httpsServerOptions = {
      key: fs.readFileSync(path.resolve('cert', 'localhost.key')),
      cert: fs.readFileSync(path.resolve('cert', 'localhost.crt'))
    };

    console.info('creating https server');
    return createServer(httpsServerOptions, expressApp);
  }

  return expressApp;
};

因为我需要在开发中运行 https。一切都在开发中工作(我使用https://localhost:3000作为客户端,使用https://localhost:443作为 graphql 后端服务器)。但是,当我将代码推送到 nginx 提供静态文件和 AWS 托管实例的暂存位置时,我开始收到此错误:

ApolloError: Network error: request to https://beta.leafreport.com/graphql/ failed, reason: write EPROTO 140426009438016:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1536:SSL alert number 40

    at new ApolloError (/src/node_modules/apollo-client/bundle.umd.js:92:26)
    at /src/node_modules/apollo-client/bundle.umd.js:1588:34
    at /src/node_modules/apollo-client/bundle.umd.js:2008:15
    at Set.forEach (<anonymous>)
    at /src/node_modules/apollo-client/bundle.umd.js:2006:26
    at Map.forEach (<anonymous>)
    at QueryManager.broadcastQueries (/src/node_modules/apollo-client/bundle.umd.js:2004:20)
    at /src/node_modules/apollo-client/bundle.umd.js:1483:29
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  graphQLErrors: [],
  networkError: FetchError: request to https://staging.mysite.com/graphql/ failed, reason: write EPROTO 140426009438016:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1536:SSL alert number 40

我第一次推送代码isDevHttps时是true错误的,NODE_TLS_REJECT_UNAUTHORIZED被设置为 0。后来我确定了isDevHttpsfalse并重新部署。我们使用的 SSL 证书由 AWS Cloudfront 提供。

这是 Apollo 客户端的初始化代码:

import withApollo from 'next-with-apollo';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { graphql_url } from 'my-config';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../fragmentTypes.json';

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData
});

function createClient({ ctx, headers, initialState }) {
  return new ApolloClient({
    credentials: 'include',
    uri: graphql_url,
    cache: new InMemoryCache({ fragmentMatcher }).restore(initialState || {}),
    headers
  });
}

export default withApollo(createClient, { getDataFromTree: 'ssr' });

这是我的 package.json:

  "dependencies": {
    "@babel/runtime-corejs2": "^7.5.1",
    "apollo-boost": "^0.4.3",
    "apollo-mocked-provider": "^3.0.1",
    "babel-eslint": "^10.0.2",
    "body-parser": "^1.19.0",
    "clean-css": "^4.2.1",
    "compression": "^1.7.4",
    "dotenv-load": "^2.0.0",
    "graphql": "^14.3.1",
    "graphql-tag": "^2.10.1",
    "helmet": "^3.18.0",
    "morgan": "^1.9.1",
    "next": "^9.0.1",
    "next-plugin-custom-babel-config": "^1.0.2",
    "next-transpile-modules": "^2.3.1",
    "next-with-apollo": "^4.2.0",
    "nprogress": "^0.2.0",
    "path": "^0.12.7",
    "react-apollo": "^3.0.0",
    "react-cookie-consent": "^2.5.0",
    "react-google-login": "^3.2.1",
    "react-facebook-login": "^4.0.1",
    "react-minimal-pie-chart": "^4.2.0",
    "react-motion-drawer": "^3.1.0",
    "supports-webp": "^2.0.1",
    "express-device": "^0.4.2"
  }

这是nginx中的dev.conf(因为证书来自云端,所以ssl被注释掉是可以的):

server {
    listen       80 default_server;

    server_name staging.mysite.com;

    include not_found.inc;
    include static_resources.inc;
    include ads.inc;
    #include ssl.inc;

    location / {
        rewrite_log off;
        include redirects/redirects.conf;

        access_log /var/log/nginx/com-access.log combined;
        error_log /var/log/nginx/com-error.log notice;
        include graylog.logging;

        include proxy_pass.inc;
        auth_basic           "closed site";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
}

在我的网站中,只有主页可以从服务器端运行,但其他页面都不能从服务器端运行,并从上面返回握手错误。主页是来自 Cloudfront 的更新。当点击链接客户端时,有些页面会打开,但是有些页面不会打开并导致 500 内部服务器错误(握手失败)。所以服务器端永远不会工作,但客户端有时会工作。

到目前为止,我尝试的任何方法都没有奏效,包括更改 AWS 实例、从 apollo 客户端构造函数中删除标头/凭据、多次重新部署。

我从错误堆栈中看到错误来自这里(node_modules/apollo-client/bundle.umd.js:92:26):

QueryManager.prototype.broadcastQueries = function () {
  var _this = this;

  this.onBroadcast();
  this.queries.forEach(function (info, id) {
    if (info.invalidated) {
      info.listeners.forEach(function (listener) {
        if (listener) {
          listener(_this.queryStore.get(id), info.newData);
        }
      });
    }
  });
};

为什么客户端不能广播查询?

标签: authenticationsslnext.jsapollo-client

解决方案


推荐阅读