yuw27b’s blog

技術メモと雑記

CADDYで手軽にHTTP/2サーバを立てる

CADDYとは

公式サイト
Caddy - The HTTP/2 Web Server with Fully Managed TLS

GitHub
GitHub - mholt/caddy: Fast, cross-platform HTTP/2 web server with automatic HTTPS

公式サイトから抜粋:

  • Windows, Mac, Linuxと、Androidで動く
  • Apacheやnginxのような細かな設定はできないかわりに、設定に関する専門知識は不要
  • 静的なファイルのサーブを主目的にしていて、環境の移行も簡単

ローカルでちょっとテストするのに使えるかなー、と思って興味を持ったのですが、
もう少し本格的なサーバなのかも?という印象。
HTTP/2で動かすのは本当に簡単で、PHPもHTTP/2でサービスさせることができました。


以下、Macでの設定〜起動までの手順です。
※他のOSでは試していませんが、サーバ自体が実行ファイル形式になっているので、基本的には同じような方法で動かせるのではないかと思います。

ダウンロードとインストール

Download Caddy
本体の「Caddy Core」をダウンロードします。

f:id:yuw27b:20151108212649p:plain

ダウンロードしたフォルダ直下にある「caddy」が実行ファイルです。
好きな場所に置いて、シェルからコマンドを実行できるようにパスを通しておきます。

ターミナルから、サーバのドキュメントルートにしたいディレクトリに移動して、

$ caddy

でサーバが起動します。
デフォルトでは「2015」番ポートを使うようになっているので(来年になったら変わるとか?)、ブラウザで「http://localhost:2015/」にアクセスしてみます。
「index.html」があればその内容が表示されるはずです。なくても「404 Not Found」が出ていればサーバは動いています。
f:id:yuw27b:20151108212725p:plain
↑デフォルトの404画面はすごくシンプル。

HTTP/2でサービスさせるために、SSLの設定をする

HTTP/2に対応させるにはSSLの設定をして、HTTPSを使用する必要があります。

2017年2月追記:
CADDYに、自動的に自己証明書を生成するオプションが追加されましたので、テスト用途であれば以下の証明書の準備は不要になりました。便利です!
公式ページ:tls - Caddy Directives (追記ここまで)

今回は、ローカルのテストサーバということでセキュリティは考えずに、opensslで自己証明書を用意します。

※証明書について詳しいことは省きますが、以下が参考になりました:
オレオレ証明書をopensslで作る(詳細版) - ろば電子が詰まっている
OpenSSLコマンドによる公開鍵暗号、電子署名の方法 - Qiita

鍵ファイルを置きたいディレクトリに移動してopensslのコマンドを実行します。

$ openssl genrsa -aes128 -out server.key 2048
$ openssl req -new -key server.key -sha256 -out server.csr
$ openssl x509 -in server.csr -days 365 -req -signkey server.key -sha256 -out server.crt

※openssl reqのところで、いろいろ入力項目が出てきますが、パスワード以外はすべて空欄のままで問題ありませんでした。

これで鍵セットは完成ですが、暗号化されたままだとうまく動かなかったので、キーを復号したものも作りました。

$ openssl rsa -in server.key -out server.noencrypt.key

これでSSLの準備は完了です。
(※復号化しないと動かなかったのは、何かミスっているような気もするのですが、ローカルサーバなので追求していません。)

とりあえず、サーバもSSL keyもコンテンツもこんな感じで一ヶ所にまとめました。

f:id:yuw27b:20151108213106p:plain

もちろんどこに何を置いてもいいのですが、まとめておいたほうがパスの指定などがやりやすいかな、という印象です。

caddyfileを用意して細かな設定をする

CADDYでは「caddyfile」というテキストファイルに様々な設定を記述することができます。
設定可能な項目は公式サイトの以下のページにまとまっています。かなりたくさんあります。
The Caddyfile - Caddy

シンプルなHTTP/2(HTTPS)のローカルサーバの場合:

localhost:2015 {
  tls ./sslkey/server.crt ./sslkey/server.noencrypt.key
  root ./public/
}

毎回ドキュメントルートに移動してサーバを起動するのも面倒なので、「root」をcaddyfileに記述しました。
※caddyfile内に記述するパスは、カレントディレクトリからのパスです。上の例では相対パスになっていますが、もちろん絶対パスでも大丈夫です。

2017年2月追記:
CADDYに自己証明書を自動生成させる場合は、

  tls self_signed

と記述します。(追記ここまで)

caddyfileを保存したら、ファイルの内容をcaddyに渡して実行します:

$ caddy -conf="/path/to/Caddyfile"

もしくは、

$ cat Caddyfile | caddy

ブラウザから、今度は「https://localhost:2015」にアクセスしてみます。(証明書の警告が出ると思いますが、無視して接続を選択します。)
f:id:yuw27b:20151108213220p:plain

ブラウザが対応していれば、HTTP/2での接続になっているはずです。
ChromeのDevToolsで見てみると、「Protocol」のところが「h2」となっているのが確認できます。
f:id:yuw27b:20151108213238p:plain


HTTP/1.1 vs HTTP/2

簡単なテストとして、画像を10枚配置したHTMLを作ってアクセスしてみました。
上がHTTP/1.1で下がHTTP/2です。HTTP/2では10枚が並列に読み込まれています。

f:id:yuw27b:20151108213259p:plain
(ローカルサーバにしては時間がかかっていますが、差が分かりにくいのでブラウザのほうでわざと速度を落としています。)

FastCGIを使う

CADDYでは、そのままでは静的なファイルのサーブしかできませんが、リクエストをFastCGIにプロキシすることで、PHPの実行が可能になります。
fastcgi - Caddy Directives

まず、PHPFastCGIマネージャであるphp-fpmを起動しておきます。
php-fpmが9000番ポートでサービスしている場合、caddyfileを以下のように記述します。

localhost:2015 {
  tls ./sslkey/server.crt ./sslkey/server.noencrypt.key
  root ./public/
  fastcgi /api 127.0.0.1:9000
}

この設定では、「/api」以下のリクエストを、FastCGIにプロキシします。

phpinfo()を呼び出すと以下のようになりました。
f:id:yuw27b:20151108213315p:plain


おわりに

テストサーバとしてやりたかったことはこれで全部できたので、ひとまずここまで。

処理速度やどのくらいのトラフィックをさばけるのか、というパフォーマンス面と、まだいろいろなことができそう、という機能面を、もう少し調査できたらまた続編を書いてみたいと思います。

language-sparqlというAtomのPackageを作った

atom.io

AtomでSPARQL(RDFデータを検索する言語です)のシンタックスハイライトがなかったので、作りました。
「作った」と言っても、文法の定義はSublime Text用のパッケージから持ってきたので、大したことはしていません。


AtomのPackageを公開する手順は、オフィシャルのドキュメントでひととおり把握できました。
あとは、他の言語用の(シンタックスハイライトの)Packageのソースコードを見て参考にしたりもしました。


公開したのは1ヶ月前ですが、だいたい1日1回くらいのペースでダウンロードされているようです。
なんですが、ちょっとミスって最初のバージョンが「0.2.0」になっています・・・恥ずかしい・・・。
package.jsonに自分で「"version": "0.1.0"」って書いたのが原因です。
apm public minor」をしたときに、Atomのほうでバージョン上げてくれるので「0.0.0」のままで良かったんですね。


SPARQL自体は、今のところはお手伝いでちょっと書いたくらいです。
オープンデータを使ったwebアプリとか、何かアイデアが浮かんだらやってみたいです。

JavaScriptのPromiseとarray.reduceを合わせて使う

順番に非同期の処理をしたい要素たちを配列に入れて、各要素に対してPromiseを返す関数を実行する。

例)['a', 'b', 'c']という配列があったときに、それぞれの文字を2秒おきにコンソールに表示したい

//これだと2秒後にほぼ同時に「a」「b」「c」がコンソールに表示されるのでNG
function wait2sec (item) { //2秒後に引数itemをコンソールに表示するfunction
  return new Promise( function(resolve, reject) {
    window.setTimeout(function() {
      console.log(item);
      resolve();
    }, 2000);
  });
}

['a', 'b', 'c'].forEach(function (item) {
  wait2sec(item);
});
//2秒おきにconsole.log()される!
function wait2sec (item) { //このfunctionはさっきと同じ
  return new Promise(function (resolve, reject) {
    window.setTimeout(function () {
      console.log(item);
      resolve();
    }, 2000);
  });
}

['a', 'b', 'c'].reduce(function (prevValue, currentValue) {
  return prevValue.then(function () { ////直前の要素の処理(2秒かかる)が終わってから次の要素をwait2sec()に渡す
    return wait2sec(currentValue);
  });
}, Promise.resolve());


たまにIE8から対応とかあるのでjQueryだけでどうにかする(だいぶ無理矢理感が・・・)

function wait2sec (list, i) {
  var dfd = new $.Deferred();
  window.setTimeout(function () {
    console.log(list[i]);
    dfd.resolve(i + 1);
  }, 2000);
  return dfd.promise();
}

var list = ['a', 'b', 'c'];
var i = 0;
var prevValue = new $.Deferred().resolve(0);
for (i = 0; i < list.length; i += 1) {
  prevValue = prevValue.then( function (next_i) {
    return wait2sec(list, next_i);
  });
};


参考文献

こちらを参考にコード書きました!
JavaScript Promises: There and back again - HTML5 Rocks


Array.prototype.reduceについて

使ったことなかったですが、便利ですね!

Array.prototype.reduce() - JavaScript | MDN

reduce関数は結構有用っていうお話 - あと味

IE9以降で使えるそう。過去に書いたコードの中にも、これを使えばもう少し簡潔に書けたものがありそうな気する・・・。


使いどころ

Ajaxで、サーバ側APIからの応答を待って処理したいときにこんな感じで書いてます。
今のところ、

  • HTMLコンテンツをパーツごとに取得したいケース
  • ファイルを1つずつアップロードしたいケース

に遭遇しました。


非同期のループはまた使いそうなので、備忘録として。