symbol-sdk-typescript-javascriptをProxy環境下で使用する

## 目的

symbol-sdkで実行する際、会社内やセキュリティに厳しい場所ではProxyサーバがあるせいで直接繋がらないケースがある
これは困るので、sdkにProxy経由の設定を入れてみる

## こんな状況図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135107/351d160d-50eb-78e3-3fe1-e6fbab3401bc.png)

## 調査開始

symbol-sdk-typescript-javascriptはrestへの接続とか[OpenApi](https://github.com/nemtech/symbol-openapi-generator)で共通化されていて、どうやらnode-fetchを使ってるようだ。
最近の書き方(0.10.0.4)では大体、 `RepositoryFactoryHttp` を最初に使って自分は書き始めるので、ここらの引数で渡せないだろうかと考える。

https://docs.symbolplatform.com/symbol-sdk-typescript-javascript/0.21.0/classes/_infrastructure_repositoryfactoryhttp_.repositoryfactoryhttp.html

```js
const RepositoryFactoryHttp: new (url: string, configs?: RepositoryFactoryConfig) => RepositoryFactoryHttp
Constructor

@param url — the server url.

@param configs — optional repository factory configs
```

`RepositoryFactoryConfig ` なんていうものがあった

https://github.com/nemtech/symbol-sdk-typescript-javascript/blob/main/src/infrastructure/RepositoryFactoryConfig.ts



```typescript
export interface RepositoryFactoryConfig {

    /**
     * optional fetch function to be used when performing rest requests. The default value is:
     *  1) window.fetch if running on a browser
     *  2) or node-fetch if running on server (window.fetch not found)
     */
    fetchApi?: any;
```

`fetchApi` という値があったので、node-fetchの値をそのまま渡してあげればいけそうな気がする。
ただ、`node-fetch` ではProxyサーバの値を事前に渡せないので、環境変数から渡せるように `node-fetch-with-proxy` を使うことにした。

## 実際に動くかやってみる

### node-fetch-with-proxyのインストール

```shell
$ npm i node-fetch-with-proxy
```

### 環境変数をセットする

Proxyサーバ(192.168.96.135:3128)をsquidで作ってみた(構築は割愛)

```shell
$ export HTTP_PROXY=http://192.168.96.135:3128
$ env | grep HTTP_PROXY
HTTP_PROXY=http://192.168.96.135:3128
```

### スクリプトを書く

ポイントとしては以下の部分

```js
const fetch = require('node-fetch-with-proxy');

(async() => {
  const config = {
    fetchApi: fetch
  }
  // Proxyの設定は環境変数に依存するため、以下の設定を事前にする
  // export HTTP_PROXY=http://PROXY_IP:PORXY_PORT
  const repo = new RepositoryFactoryHttp(url, config);
```

<details><summary>転送トランザクションを送信するスクリプト</summary><div>

```js
const { 
  Account,
  Address,
  TransferTransaction,
  Deadline,
  PlainMessage,
  RepositoryFactoryHttp,
  TransactionService} = require('symbol-sdk');
const config = require('config');

const url = 'http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
const rawprivatekey = '送信元秘密鍵';
const rawsendadress = '送信先アドレス';
const sendaddress = Address.createFromRawAddress(rawsendadress);
const minfeemultiplier = 100;

const fetch = require('node-fetch-with-proxy');

(async() => {
  const config = {
    fetchApi: fetch
  }
  // Proxyの設定は環境変数に依存するため、以下の設定を事前にする
  // export HTTP_PROXY=http://PROXY_IP:PORXY_PORT
  const repo = new RepositoryFactoryHttp(url, config);
  

  //Networktypeをcatapultから取得
  const networkType = await repo.getNetworkType().toPromise()
  //GenerationHashをcatapultから取得
  const ghash = await repo.getGenerationHash().toPromise()

  console.log('network', networkType);
  console.log('GenerationHash', ghash);

  const epoch = await repo.getEpochAdjustment().toPromise();
  const currency = await repo.getCurrencies().toPromise();

  //トランザクションを作成するための内容を作る
  const transferTransaction = TransferTransaction.create(
    Deadline.create(epoch), // いつまでトランザクションをもたせるか
    sendaddress, //送信先アドレス
    [currency.currency.createRelative(100)], // xymの送信数
    PlainMessage.create('TEST KEIYOW'), //メッセージ
    networkType,
  ).setMaxFee(minfeemultiplier);; //最低手数料をつけます

  //送信元のアカウントをセット
  const account = Account.createFromPrivateKey(rawprivatekey, networkType);

  //作成したトランザクションに署名する
  const signedTransaction = account.sign(transferTransaction, ghash);

  //この中にトランザクションIDが入ってる
  console.log('------------------- signedTransaction ------------------------');
  console.log(signedTransaction);

  console.log('------------------- sendTransaction ------------------------');

  //websocketを作成する
  const listener = repo.createListener();
  const transactionService = new TransactionService(repo.createTransactionRepository(), repo.createReceiptRepository());

  // websocketをopenする
  await listener.open()

  // トランザクションを送信する
  transactionService.announce(signedTransaction, listener)
  .subscribe((transaction) => {
    console.log('------------------- Success --------------------------------')
    console.log('Success: Transaction', transaction)
    console.log('------------------------------------------------------------')
    listener.close()
  }, (err) => {
    console.log('Err', err)
    listener.close()
  })

})()
```
</div></details>

### トランザクション実行

http://explorer-0.10.0.x-01.symboldev.network/transactions/73C55E1EBFB4C76B95B4E3125EB4A28AAD00847174CFB4660F4FDA566B253A10

```shell
$ node symbol/transaction_behind_proxy.js
network 152
GenerationHash 6C1B92391CCB41C96478471C2634C111D9E989DECD66130C0430B5B8D20117CD
------------------- signedTransaction ------------------------
SignedTransaction {
  payload:
   'BC000000000000004ADD04CAB18384CB785B279BEA7DE3818AD8E381D601BCA92317FDF8527A169668528C5656296E0DD0EAFD86E3810EEC8979D65748D78664D480ADEBA02001096215F0EED3CD7DB144C2788F9C7BBEAAC3A53F7406B677448908F9DDE4D097AC000000000198544170490000000000008B1BFB080800000098DEDC169324E77AAFEF6183FB5560423BE8826CF431F7780C000100000000006008ADEC6BE7665B00E40B54020000000054455354204B4549594F57',
  hash:
   '73C55E1EBFB4C76B95B4E3125EB4A28AAD00847174CFB4660F4FDA566B253A10',
  signerPublicKey:
   '6215F0EED3CD7DB144C2788F9C7BBEAAC3A53F7406B677448908F9DDE4D097AC',
  type: 16724,
  networkType: 152 }
------------------- sendTransaction ------------------------
------------------- Success --------------------------------
Success: Transaction TransferTransaction {
  type: 16724,
  networkType: 152,
  version: 1,
  deadline: Deadline { adjustedValue: 34510412683 },
  maxFee: UInt64 { lower: 18800, higher: 0 },
  signature:
   '4ADD04CAB18384CB785B279BEA7DE3818AD8E381D601BCA92317FDF8527A169668528C5656296E0DD0EAFD86E3810EEC8979D65748D78664D480ADEBA0200109',
  signer:
   PublicAccount {
     publicKey:
      '6215F0EED3CD7DB144C2788F9C7BBEAAC3A53F7406B677448908F9DDE4D097AC',
     address:
      Address {
        address: 'TDIDPITIP63MO3MQIMUOBJZUX3WQZBGBKBWTELI',
        networkType: 152 } },
  transactionInfo:
   TransactionInfo {
     height: UInt64 { lower: 223811, higher: 0 },
     index: undefined,
     id: undefined,
     hash:
      '73C55E1EBFB4C76B95B4E3125EB4A28AAD00847174CFB4660F4FDA566B253A10',
     merkleComponentHash:
      '73C55E1EBFB4C76B95B4E3125EB4A28AAD00847174CFB4660F4FDA566B253A10' },
  payloadSize: undefined,
  recipientAddress:
   Address {
     address: 'TDPNYFUTETTXVL7PMGB7WVLAII56RATM6QY7O6A',
     networkType: 152 },
  mosaics: [ Mosaic { id: [MosaicId], amount: [UInt64] } ],
  message: PlainMessage { type: 0, payload: 'TEST KEIYOW' } }
------------------------------------------------------------
```

## Proxyサーバ側のSquidログ確認

Squid経由でトランザクション実行できた

```
1607933612.394    212 192.168.96.1 TCP_REFRESH_MODIFIED/200 605 GET http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/info - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.398    216 192.168.96.1 TCP_REFRESH_MODIFIED/200 605 GET http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/info - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.442     37 192.168.96.1 TCP_REFRESH_MODIFIED/200 2860 GET http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/network/properties - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.501     52 192.168.96.1 TCP_REFRESH_MODIFIED/200 2860 GET http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/network/properties - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.643    136 192.168.96.1 TCP_MISS/200 307 POST http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/namespaces/mosaic/names - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.643    136 192.168.96.1 TCP_MISS/200 485 POST http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/mosaics - HIER_DIRECT/xx.xx.xx.xx application/json
1607933612.883    118 192.168.96.1 TCP_MISS/202 307 PUT http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/transactions - HIER_DIRECT/xx.xx.xx.xx application/json
```