Node.js製のプロキシHoxyを使い、任意のURLでlocalhostにアクセスする

フロントエンド開発をする際、[Browsersync](http://www.browsersync.io/)などを使ってlocalhostサーバを立てることは多いかと思います。この記事は、サーバ側のAPIやCookieの都合から、本番環境のURLでそのローカルサーバにアクセスしたくなったとき向けのTipsです。

やり方はいくつもありそうですが、ここではHoxyというNode.js上で動作するプロキシサーバを、gulpと連携させて使う方法を紹介します。
Node製のプロキシを選んだのは、外部ツールのインストールや、hostsの書き換えをせずにサクッと環境構築できるようにするためです。

## 実現すること
- ブラウザで`http://example.com/`（任意のURL）にアクセスすると、ローカルのコンテンツが表示される
- 開発中はlivereloadなどBrowsersyncの各種機能が利用できる
- `npm install && gulp`だけで環境を共有できる（重要！）

## Hoxyとは
公式サイト : <http://greim.github.io/hoxy/>

[Fiddler](http://www.telerik.com/fiddler)や[Charles](http://www.charlesproxy.com/)のようなデバッグ用プロキシの一種です。
ただしGUIを持たず、Node.jsのAPIを使ってプロキシの設定や起動を行います。

今回はリクエスト先のドメインを置き換えるという簡単な使い方しかしませんが、レイテンシを再現したり、レスポンスbodyを改変するといった高度な使い方もサポートしています。

## プロキシサーバの設定
プロキシサーバは[Hoxy](http://greim.github.io/hoxy/)を使い、`localhost:8888`に立てることにします。

Hoxyの[`.intercept()`](http://greim.github.io/hoxy/#proxy-intercept)メソッドを使い、`example.com`へのリクエストが`localhost:9000`に向かうように置き換えます。もちろん下層ページや、Browsersyncが使うWebSocketの通信もこれで置き換えられます。

```js
var hoxy = require('hoxy');
var proxy = hoxy.createServer().listen(8888);
proxy.intercept({
  phase: 'request',
  hostname: 'example.com'
}, function (req) {
  req.hostname = 'localhost';
  req.port = '9000';
});
```

この例ではホスト名が`example.com`のすべてのリクエストを対象としていますが、`.intercept()`のオプションを変更することで、正規表現によるURLの絞り込みや、特定のMIMEタイプだけを対象にすることもできます。

対象以外のリクエストは、通常どおりインターネット上のコンテンツを参照します。本番環境からのレスポンスとローカルのファイルを混ぜてページを構成することが可能です。

## Webサーバの設定
`app`ディレクトリをルートとした[Browsersync](http://www.browsersync.io/)サーバを、`localhost:9000`に作ります。

そのまま`http://localhost:9000/`としてアクセスすることもできますが、さきほどのプロキシを通せば、同じコンテンツが`http://example.com/`でも閲覧できるようになります。

```js
var browserSync = require('browser-sync').create();
browserSync.init({
  notify: false,
  port: 9000,
  open: false,
  server: {
    baseDir: 'app'
  }
});
```

## gulpタスクとして実装
[gulp](http://gulpjs.com/)を使い、上記のサーバをコマンド1つで起動できるようにします。

**gulpfile.js**

```js
'use strict';

var gulp = require('gulp');
var $ = require('gulp-load-plugins');
var browserSync = require('browser-sync').create();
var hoxy = require('hoxy');
var opn = require('opn');

var hostname = 'example.com';

gulp.task('proxy', function (callback) {
  var proxy = hoxy.createServer().listen(8888, function () {
    callback();
  });
  proxy.intercept({
    phase: 'request',
    hostname: hostname
  }, function (req) {
    req.hostname = 'localhost';
    req.port = '9000';
  });
});

gulp.task('serve', ['proxy'], function () {
  browserSync.init({
    notify: false,
    port: 9000,
    open: false,
    server: {
      baseDir: 'app'
    }
  }, function () {
    opn('http://' + hostname);
  });
  gulp.watch([
    'app/**'
  ]).on('change', browserSync.reload);
});

gulp.task('default', ['serve']);
```

`gulp`（または`gulp serve`）コマンドで2つのサーバを順に起動し、さらに`http://example.com/`を自動的にブラウザで開くよう設定しています。
また、コンテンツ（`app`ディレクトリ配下のファイル）を更新すると、自動的にページがリロードされます。

## 動作確認
プロキシを使わずに`http://example.com/`へアクセス（→本番環境）
![本番環境](https://qiita-image-store.s3.amazonaws.com/0/67488/64f2b389-92c1-38ac-a273-284a0dad4213.png)

`localhost:8888`プロキシを設定して`http://example.com/`にアクセス（→ローカル環境）
![ローカル環境](https://qiita-image-store.s3.amazonaws.com/0/67488/c619e958-5b1b-8241-591d-c1b574aaa59e.png)

## デモプロジェクト
package.jsonを含んだ全ソースコードは、GitHubにて公開しています。

<https://github.com/htanjo/hoxy-browsersync-demo>

## おまけ: localhostを立てずに直接ファイルを差し替える
この記事では、Browsersyncのサーバを使いたかったため、`example.com`に対し`localhost:9000`を参照させるという方法を取りました。
しかし単純に特定のリクエストをローカルファイルに差し替えるだけなら、Hoxyの`cycle`インスタンスを使って実現できます。

```js
proxy.intercept({
  phase: 'request',
  fullUrl: 'http://example.com/main.js'
}, function (req, resp, cycle) {
  return cycle.serve(__dirname + '/main.js');
});
```

この方法については、[Hoxy - Class Cycle](http://greim.github.io/hoxy/#class-cycle)のドキュメントに詳しく載っています。