V5 から会社プロキシ環境で同期できない

報告

V5 にしてから会社 PC での同期ができなくなりました。
どう切り分けていいのか分からないので相談させてください。

会社 Proxy に認証が必要なので
config.cson に proxy 設定をしています。

network:
  http_proxy: "http://david%40company.com:password@webproxy:8080/"
  https_proxy: "http://david%40company.com:password@webproxy:8080/"

ただ、V4 のときからですが、ログイン認証の期間が切れた場合は上記プロキシではログインできないため
goproxy (ローカルプロキシ)を介してログイン後に、再度上記の会社を設定して同期していました。
ローカルプロキシは下記実装で単純に通しているだけなので逆にこれでログインできていたのもよく分かりませんが・・・。

func main() {
  proxy := goproxy.NewProxyHttpServer()
  proxy.Verbose = true
  proxy.OnRequest().DoFunc(
    func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
      fmt.Println("---------------------------")
      return r, nil
    })
  log.Fatal(http.ListenAndServe(":8080", proxy))
}

ログインに関しては V5 でもローカルプロキシでログインできますが、
ローカルプロキシ、会社プロキシどちらでも同期ができなくなりました。

ローカルプロキシでは同期時、設定画面の install、update も読込中のまま反応なしです。
ローカルプロキシの出力内容をみても特に呼ばれた形跡がありません。

inkdrop 起動後の js コンソールに↓が出力されています。

Failed to load resource: the server responded with a status of 407 (Proxy Authentication Required)
sessions.bugsnag.com/:1 Failed to load resource: net::ERR_TUNNEL_CONNECTION_FAILED
C:\Users\___\AppData…g\src\common.js:114 app:error Error: Failed to check native build tools
    at file:///C:/Users/___/AppData/Local/inkdrop/app-5.0.0/resources/app.asar/browser-main.js:4:116207
    at exit (file:///C:/Users/___/AppData/Local/inkdrop/app-5.0.0/resources/app.asar/browser-main.js:4:112031)
    at r (file:///C:/Users/___/AppData/Local/inkdrop/app-5.0.0/resources/app.asar/browser-main.js:4:105683)
    at ChildProcess.<anonymous> (file:///C:/Users/___/AppData/Local/inkdrop/app-5.0.0/resources/app.asar/browser-main.js:4:105889)
    at ChildProcess.emit (events.js:203:13)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12) +0ms
C:\Users\___\AppData…g\src\common.js:114 app:error stdout: +2ms undefined
C:\Users\___\AppData…g\src\common.js:114 app:error stderr: +0ms undefined

なにか原因を特定する方法はあるでしょうか?
ダメな場合は会社PCでは同期しない設定にして使おうと思っています。

利用環境

  • Platform: Windows
  • Platform version: 10 64bit
  • App Version: 5.0.0

shimizuさん

ご連絡ありがとうございます。
それは奇妙ですね。ネットワークのライブラリは特にアップデートしていませんので、別の所に要因がありそうです。

Failed to load resource: the server responded with a status of 407 (Proxy Authentication Required)

上記エラーメッセージは、プロキシに向けてリクエストを投げたが認証に失敗した、あるいは何らかの理由で認証されなかったという意味と取れます。
ですので、まずはプロキシの認証設定を再度確認してみてください。

ローカルプロキシを介せばログイン出来るとの事ですが、まずはなぜローカルプロキシを介さなければならないのかという原因を突き止めたほうが根本解決につながると思います。
APIへのアクセスには axios というライブラリを使っています。
NodeJSで以下の検証コードを実行すれば、問題の詳細が分かるかもしれません:

const axios = require('axios')
process.env.http_proxy = YOUR_PROXY_HTTP_URL
process.env.https_proxy = YOUR_PROXY_HTTPS_URL

axios.get('https://api.inkdrop.app/')
  .then(function (response) {
    console.log(response.data)
  })
  .catch(function (error) {
    console.log(error)
  })

解決の糸口がつかめることを願っています。

提示頂いた検証コードでの調査まとめ

proxy scheme 結果
会社 proxy http
https
local proxy http
https
  • http は適当なサイトを指定して検証
  • local proxy は環境変数の会社 proxy で通しているだけ。
  • 会社 proxy http で、proxy の id/pass が正しくない場合は認証エラーが出るので設定は正しいはず。

会社 proy の https error

Error: Request failed with status code 400
    at createError (axios_test\node_modules\axios\lib\core\createError.js:16:15)
    at settle (axios_test\node_modules\axios\lib\core\settle.js:17:12)
    at IncomingMessage.handleStreamEnd (axios_test\node_modules\axios\lib\adapters\http.js:244:11)
    at IncomingMessage.emit (events.js:228:7)
    at endReadableNT (_stream_readable.js:1185:12)
    at processTicksAndRejections (internal/process/task_queues.js:81:21) {

・・・略・・・

  response: {
    status: 400,
    statusText: 'Bad Request',
    headers: {
      'cache-control': 'no-cache',
      pragma: 'no-cache',
      'content-type': 'text/html; charset=utf-8',
      'proxy-connection': 'close',
      connection: 'close',
      'content-length': '677'
    },
    config: {
      url: 'https://api.inkdrop.app/',
      method: 'get',
      headers: [Object],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      adapter: [Function: httpAdapter],
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      data: undefined
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [Socket],
      connection: [Socket],
      _header: 'GET https://api.inkdrop.app/ HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'User-Agent: axios/0.20.0\r\n' +
        'host: api.inkdrop.app\r\n' +
        'Proxy-Authorization: Basic 略=\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      path: 'https://api.inkdrop.app/',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      _redirectable: [Writable],
      [Symbol(kNeedDrain)]: false,
      [Symbol(isCorked)]: false,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: '<HTML><HEAD>\r\n' +
      '<TITLE>Request Error</TITLE>\r\n' +
      '</HEAD>\r\n' +
      '<BODY>\r\n' +
      '<FONT face="Helvetica">\r\n' +
      '<big><strong></strong></big><BR>\r\n' +
      '</FONT>\r\n' +
      '<blockquote>\r\n' +
      '<TABLE border=0 cellPadding=1 width="80%">\r\n' +
      '<TR><TD>\r\n' +
      '<FONT face="Helvetica">\r\n' +
      '<big>Request Error (invalid_request)</big>\r\n' +
      '<BR>\r\n' +
      '<BR>\r\n' +
      '</FONT>\r\n' +
      '</TD></TR>\r\n' +
      '<TR><TD>\r\n' +
      '<FONT face="Helvetica">\r\n' +
      'Your request could not be processed. Request could not be handled\r\n' +
      '</FONT>\r\n' +
      '</TD></TR>\r\n' +
      '<TR><TD>\r\n' +
      '<FONT face="Helvetica">\r\n' +
      'This could be caused by a misconfiguration, or possibly a malformed request.\r\n' +
      '</FONT>\r\n' +
      '</TD></TR>\r\n' +
      '<TR><TD>\r\n' +
      '<FONT face="Helvetica" SIZE=2>\r\n' +
      '<BR>\r\n' +
      '\r\n' +
      '</FONT>\r\n' +
      '</TD></TR>\r\n' +
      '</TABLE>\r\n' +
      '</blockquote>\r\n' +
      '</FONT>\r\n' +
      '</BODY></HTML>\r\n'
  },
  isAxiosError: true,
  toJSON: [Function: toJSON]
}

継続調査中・・・

httpsの場合だけ400になるのは奇妙ですね。
Inkdropサーバは基本的にSSLなので、こちらのケースに該当します。
しかしながらエラーメッセージからは具体的な原因が読み取れませんね。
Proxy-Authorizationヘッダも正しく付与されているようですし。
Wiresharkなどでlocal proxyを通した場合とのリクエストペイロードの違いを見るといいかもしれません。

今日わかったこと。
axios-https-proxy-fix を使って config で渡すと https が見えるようになる。

let config = {
  proxy: {
    host: "会社Proxy",
    port: 8080,
    auth: {
      username: "myname@my.domain.com",
      password: "password",
    },
  },
};

let axios = require("axios-https-proxy-fix");
axios
  .get("https://api.inkdrop.app/", config)
  .then(function (response) {
    console.log(response.data); //=> { ok: true }
  })
  .catch(function (error) {
    console.log(error);
  });

なるほど、とすると環境変数経由でのプロクシ設定はうまく動かない、axiosのバグである可能性がありますね。
axios-https-proxy-fixパッチを確認してみましたが、結局内部で HttpsProxyAgent を生成しているだけのようです。

こちらで言及されている方法だといかがでしょうか?

const HttpsProxyAgent = require('https-proxy-agent');

const const axiosDefaultConfig = {
    baseURL: 'https://api.inkdrop.app/',
    proxy: false,
    httpsAgent: new HttpsProxyAgent('http://PROXY_SERVER_URL/')
};

const axios = require ('axios').create(axiosDefaultConfig);
axios.get('/')
    .then(function (response) {
        console.log('Response with axios was ok: ' + response.status);
    })
    .catch(function (error) {
        console.log(error);
    });

プロキシは自分の環境では詳しくテスト出来ないので助かります!ご協力ありがとうございます。

いけました! (Response with axios was ok: 200)
PROXY_SERVER_URL にある id/pass を適当に変えるとプロキシブロックのメッセージが出るので、検証は大丈夫そうです。

逆に http なサイトは見れなくなりました。

Error: getaddrinfo ENOTFOUND www.mizkan.co.jp
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26) {
  errno: 'ENOTFOUND',
  code: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'www.mizkan.co.jp',
  config: {
    url: '/',
    method: 'get',
    headers: {
      Accept: 'application/json, text/plain, */*',
      'User-Agent': 'axios/0.20.0'
    },
    proxy: false,
    baseURL: 'http://www.mizkan.co.jp/',
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    adapter: [Function: httpAdapter],
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    httpsAgent: HttpsProxyAgent {
      _events: [Object: null prototype] {},
      _eventsCount: 0,
      _maxListeners: undefined,
      timeout: null,
      maxFreeSockets: 1,
      maxSockets: 1,
      sockets: {},
      requests: {},
      options: {},
      secureProxy: false,
      proxy: [Object]
    },
    validateStatus: [Function: validateStatus],
    data: undefined
  },
  request: Writable {
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: false,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: true,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      bufferedRequest: null,
      lastBufferedRequest: null,
      pendingcb: 0,
      prefinished: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: false,
      bufferedRequestCount: 0,
      corkedRequestsFree: [Object]
    },
    writable: true,
    _events: [Object: null prototype] {
      response: [Function: handleResponse],
      error: [Function: handleRequestError]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: 10485760,
      protocol: 'http:',
      path: '/',
      method: 'GET',
      headers: [Object],
      agent: undefined,
      agents: [Object],
      auth: undefined,
      hostname: 'www.mizkan.co.jp',
      port: null,
      nativeProtocols: [Object],
      pathname: '/'
    },
    _ended: true,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 0,
    _requestBodyBuffers: [],
    _onNativeResponse: [Function],
    _currentRequest: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [Socket],
      connection: [Socket],
      _header: 'GET / HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'User-Agent: axios/0.20.0\r\n' +
        'Host: www.mizkan.co.jp\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      path: '/',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      _redirectable: [Circular],
      [Symbol(kNeedDrain)]: false,
      [Symbol(isCorked)]: false,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    _currentUrl: 'http://www.mizkan.co.jp/'
  },
  response: undefined,
  isAxiosError: true,
  toJSON: [Function: toJSON]
}

お!ではアプリ側で修正できそうですね。
httpのサイトが見れないのは想定通りです。以下のようにhttp向けagentを追加してやると見れるはずです:

const HttpsProxyAgent = require('https-proxy-agent');
const HttpProxyAgent = require('http-proxy-agent');

const const axiosDefaultConfig = {
    baseURL: 'https://api.inkdrop.app/',
    proxy: false,
    httpsAgent: new HttpsProxyAgent('http://PROXY_SERVER_URL/'),
    httpAgent: new HttpProxyAgent('http://PROXY_SERVER_URL_FOR_HTTP_SITES/')
};

ただ仮に上記で動かなくても、Inkdropのサーバは全てSSLなので問題ないかと思われます。
ちょっとパッチをビルドしてみます。

次の課題は同期です。
axiosは api.inkdrop.app へのAPIリクエストにて使用しています。
実はデータ同期にはPouchDBというライブラリを使用しており、その内部では node-fetch という異なるライブラリでリクエストを投げています。
アプリではこのfetchのオプションにHttpsProxyAgentを指定しています。
なので理論的にはaxiosとやっていることは同じはずですが、うまく動かないでしょうか?
以下が検証コードです:

const fetch = require('node-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
const options = {
  agent: new HttpsProxyAgent('http://PROXY_SERVER_URL/')
}

fetch('https://api.inkdrop.app/', options)
    .then(function (response) {
        console.log('Response with axios was ok: ' + response.status);
    })
    .catch(function (error) {
        console.error(error);
    });

Response with axios was ok: 200

↑ となります!

プロキシの ID を適当にかえると → Response with axios was ok: 407

あ、console.log書き換え忘れていましたw
なるほど!!ではnode-fetchは問題ないのか。

パッチビルドしてみました!
https://inkdrop-dist.s3-ap-northeast-1.amazonaws.com/tmp/Inkdrop-5.0.0-Windows-patch-2.zip

キタ━━━━(゚∀゚)━━━━!!

ログイン&同期ができるようになりました!!!!!

最悪、同期はあきらめてローカル運用にして気長に調べようと思ってました。
めちゃくちゃうれしいです!!
ありがとうございます!!!

1 Like

やったー!
他の環境で副作用が無いことを祈ります笑
次のリリースから適用します。
ご協力ありがとうございました!
こちらは一旦解決済みとしますね。

他の環境で副作用が無いことを祈ります笑

revert されないことを願ってます!

こちらは一旦解決済みとしますね。

お願いします。

ありがとうございました!

v5.0.1にて修正版リリースしました🎉
Thanks again!

2 Likes