Thursday, December 28, 2017

CI 用 Docker イメージ作成におけるベストプラクティス

H2O の CI では長らく、秘伝のタレ的な .travis.yml を使い続けてきたのですが、なにぶん依存関係が多いもので、だいぶメンテナンスが辛い感じになってきていました。また、CI テストで発生したエラーの調査の度に、時間のかかる CI を回さなければならないことが、開発者のストレスとなっていました。

そこで一念発起して、Docker イメージを使った CI に切り替えることにしました(実行環境としては引き続き Travis を使います)。

その際に、要件として以下のようなことを考えました。
  • CI以外に、コミット前のテストにも使えるようなイメージにすること
  • コマンド一発でビルドとテストが動作すること
  • 無駄な処理をしないこと

その結果、以下のような実装になりました。
  • テストに必要なソフトウェア群をインストールしたイメージを Docker Hub にアップロードしておく
  • テストには、1. テスト用コンテナにコードのあるディレクトリをマウントし、2. アウトオブトリービルド注1とテストを実行するスクリプトを書く
  • CI環境では、そのスクリプトを実行する
  • 開発者がローカルでそのスクリプトを実行してテストしてもよい

Dockerfile および CI 実行用のスクリプトは、H2O のレポジトリにある以下のファイルです。

結果、Docker 導入前に90行あった .travis.yml は23行にまで短くなり、また、CI に必要な時間も2割ほど減少しました(CI を実行するたびに、必要なソフトウェアをビルドする手間が不要になったため)。

また、開発者が各個人の環境において make -f misc/docker-ci/check.mkmake を実行するだけで、いつでも(コミット前でも)CI 環境と同様のテストが実行できるようになりました。テストが失敗したら、コンテナを立ち上げっぱなしにして、ローカルでコード修正&特定のテスト再実行を繰り返すことも可能なので、バグ修正が捗ります。

まとめ:CI 用の Docker イメージを作るのではなく、テスト用の CI イメージを作り、それを CI に展開すべき

言わずもがなかもしれませんが、備忘録としてまとめておきます。


注1: ソースディレクトリ以外の場所に生成物を配置するビルド手法のこと。アウトオブトリービルドができない場合はaufsを使っても良いのかもしれない

Wednesday, December 27, 2017

git blameでプルリクエストの番号を表示する

GitHubでプルリクエスト前提の開発をしていると、git blameで「なぜ、このコードがこうなっているのか」調べる際に、commit idではなくプルリクエストの番号を表示してほしくなります。

というわけで書いたのが git-blame-pr.pl

以下のような感じで表示されるので、調査がはかどります。
$ git-blame-pr.pl lib/core/request.c
(中略)
PR #446   
PR #606   h2o_iovec_t h2o_get_redirect_method(h2o_iovec_t method, int status)
PR #606   {
PR #606       if (h2o_memis(method.base, method.len, H2O_STRLIT("POST")) && !(status == 307 || status == 308))
PR #606           method = h2o_iovec_init(H2O_STRLIT("GET"));
PR #606       return method;
PR #606   }
PR #606   
PR #1436  static void do_push_path(void *_req, const char *path, size_t path_len, int is_critical)
PR #1436  {
PR #1436      h2o_req_t *req = _req;
PR #1436  
PR #1436      if (req->conn->callbacks->push_path != NULL)
PR #1436          req->conn->callbacks->push_path(req, path, path_len, is_critical);
PR #1436  }
PR #1436  
PR #1169  h2o_iovec_t h2o_push_path_in_link_header(h2o_req_t *req, const char *value, size_t value_len)
PR #446   {
PR #1169      h2o_iovec_t ret = h2o_iovec_init(value, value_len);
PR #446   
PR #1436      h2o_extract_push_path_from_link_header(&req->pool, value, value_len, req->path_normalized, req->input.scheme,
PR #1436                                             req->input.authority, req->res_is_delegated ? req->scheme : NULL,
PR #1436                                             req->res_is_delegated ? &req->authority : NULL, do_push_path, req, &ret);
PR #446   
PR #1169      return ret;
PR #446   }

Friday, December 15, 2017

H2O version 2.2.4 released, incl. vulnerability fixes

Today, we have released H2O version 2.2.4.

This is a bug-fix release. Some of the fixes are security-related.

The details of the vulnerabilities being fixed can be found in the links below. Users are encouraged to upgrade to 2.2.4 if they are affected or unsure.
We would like to thank the people for reporting the issues.

Tuesday, November 7, 2017

最高速のfizzbuzzを実装する話

この前、Twitterで誰かが「コンパイラ言語でFizzbuzz書くなら、コンパイル時に全ての演算を済ませ、実行コストはI/O命令1個になるように最適化しないと」という話をしていた。いいこと言うな、と思ってスルーしていたのだが、体調不良で頭だけ動いている状態だったのでC++11でトライしてみることに。

案ずるより産むが易しというもので、割と簡単に綺麗に書けた。こんな感じ。

char配列を可変長のテンプレート引数として結合していって、文字列定数を生成するというテクニックは実際に使い所があるかもと思った。最近C++書いてないけど。
#include <cstdio>

template <typename LHS, int N> struct numstr {
    template <char... Args> struct append {
        typedef typename numstr<LHS, N / 10>::template append<'0' + N % 10, Args...>::result result;
    };
};

template <typename LHS> struct numstr<LHS, 0> {
    template <char... Args> struct append {
        typedef typename LHS::template append<Args...>::result result;
    };
};

template <int N, int Mod3 = N % 3, int Mod5 = N % 5> struct fizzbuzz {
    template <char... Args> struct append {
        typedef typename numstr<fizzbuzz<N - 1>, N>::template append<'\n', Args...>::result result;
    };
};

template <> struct fizzbuzz<0> {
    template <char... Args> struct append {
        struct result {
            const char data[sizeof...(Args)];
            constexpr result() : data{Args...} {}
        };
    };
};

template <int N> struct fizzbuzz<N, 0, 0> {
    template <char... Args> struct append {
        typedef typename fizzbuzz<N - 1>::template append<'F', 'i', 'z', 'z', 'B', 'u', 'z', 'z', '\n', Args...>::result result;
    };
};

template <int N, int Mod5> struct fizzbuzz<N, 0, Mod5> {
    template <char... Args> struct append {
        typedef typename fizzbuzz<N - 1>::template append<'F', 'i', 'z', 'z', '\n', Args...>::result result;
    };
};

template <int N, int Mod3> struct fizzbuzz<N, Mod3, 0> {
    template <char... Args> struct append {
        typedef typename fizzbuzz<N - 1>::template append<'B', 'u', 'z', 'z', '\n', Args...>::result result;
    };
};

int main() {
    constexpr fizzbuzz<100>::append<>::result s;
    fwrite(s.data, 1, sizeof(s.data), stdout);
    return 0;
}
コンパイルしてディスアセンブルすると、コンパイル時に生成された文字列定数をfwriteするだけのmain関数が生成されていることが確認できる。
$ clang++ -O2 -std=c++11 -pedantic -Wall fizzbuzz11.cc && otool -tV a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000dd0 pushq %rbp
0000000100000dd1 movq %rsp, %rbp
0000000100000dd4 movq 0x225(%rip), %rax ## literal pool symbol address: ___stdoutp
0000000100000ddb movq (%rax), %rcx
0000000100000dde leaq __ZZ4mainE1s(%rip), %rdi ## main::s
0000000100000de5 movl $0x1, %esi
0000000100000dea movl $0x19d, %edx
0000000100000def callq 0x100000df8 ## symbol stub for: _fwrite
0000000100000df4 xorl %eax, %eax
0000000100000df6 popq %rbp
0000000100000df7 retq

ちなみに、C++14だと、いたるところにconstexprがつけられるので、もっと普通のコードになる

Thursday, October 19, 2017

H2O version 2.2.3 released, incl. vulnerability fixes

Today, we have released H2O version 2.2.3.

This is a bug-fix release, including two security fixes and 14 bug fixes from 7 people. Please consult the release page for details.

The vulnerabilities being fixed are #1459 (CVE-2017-10868) and #1460 (CVE-2017-10869). Both are vulnerabilities against DoS attacks. It is recommended that the users of H2O update their deployments to the newest release.

We would like to thank the developers for working on the fixes and for users reporting the issues.

Wednesday, April 5, 2017

H2O version 2.2.0 released

Today I am happy to announce the release of H2O HTTP/2 server version 2.2.0.

The release includes over ten new features (show below) as well as bug fixes.

  • [core] add crash-handler.wait-pipe-close parameter #1092
  • [core] introduce an option to bypass the server header sent from upstream #1226
  • [access-log] add %{remote}p for logging the remote port #1166
  • [access-log] JSON logging #1208
  • [access-log] add specifier for logging per-request environment variables #1221
  • [access-log] add support for <, > modifiers for logging either the original or the final response #1238
  • [file] add directive for serving gzipped files, decompressing them on-the-fly #1140
  • [http2] recognize x-http2-push-only attribute on link header #1169
  • [http2] add optional timeout for closing connections upon graceful shutdown #1108
  • [proxy] add directives for tweaking headers sent to upstream #1126
  • [proxy] add directive for controlling the via request header #1225
  • [ssl] add directive for logging session ID #1164

Some notable changes are covered in separate blogposts: H2O version 2.2 beta released with TLS 1.3 support and other improvements, JSON logging support is added to H2O HTTP/2 server version 2.2.0-beta3.

Full list of changes can be found here.

The release also comes with the up-to-date version of mruby. Recently, a series of security defects have been reported for the language runtime. Our understanding is that many of the vulnerabilities rely on an attacker writing the script (a model that does not apply to how mruby is used in H2O). However, you can turn off mruby support by providing -DWITH_MRUBY=OFF as an argument to CMake, or update mruby to the latest version simply by replacing the contents of deps/mruby with that of github.com/mruby/mruby.

Thursday, March 23, 2017

JSON logging support is added to H2O HTTP/2 server version 2.2.0-beta3

Today I am happy to announce the release of H2O HTTP/2 server, version 2.2.0-beta3.

Among the new features you will be finding in 2.2, in this blogpost I would like to talk about our support for JSON logging.

Traditionally, the log file format of HTTP servers have followed the tradition set by NCSA httpd more than twenty years ago. But the more we try to deal in various ways with the logs, the more it makes sense to use a standardized and extensible format so that we can apply existing tools to the logs being collected. Hence JSON.

Our support for JSON is a smooth evolution from the NCSA- (and Apache-) style logging. Configuration for a JSON logging will look like below.
access-log:
  path: /path/to/access-log.json
  format: '{"remote": "%h:%{remote}p", "at": "%{%Y%m%d%H%M%S}t.%{msec_frac}t", "method": "%m",  "path": "%U%q", "status": %s, "body-size": %b, "referer": "%{referer}i"}'
  escape: json
The template specified by the format attribute uses the exact same specifiers as we use in NCSA-style logging. The only differences are that the non-substituted part of the template is JSON, and that another attributed named escape is set to json. The attribute instructs the logger to emit things in a JSON-compatible manner.

Specifically, the behavior of the logger is changed to be:
  • strings are escaped in JSON style (i.e. \u00nn) instead of \xnn
  • nulls are emitted as null instead of -

The format may seem a bit verbose, but gives you the power to name the elements of a JSON object as you like, and to choose whatever format you want to use for compound values (e.g. the date, as shown in the example above).

When accessed by a client, a log line like below will be emitted for the above configuration.
{"remote": "192.0.2.1:54389", "at": "20170322161623.023495", "method": "GET", "path": "/index.html", "status": 200, "body-size": 239, "referer": null}
One thing you may notice is that the value of the referer element is emitted as null without the surrounding double quotes that existed in the specified format. When escaping in JSON style, h2o removes the surrounding quotes if the sole value of the string literal is a single format specifier (i.e. %...) and if the format specifier evaluates to null. In other words, "%foo" evaluates to either a string literal or null, while %foo evaluates to a number or null.

If a string literal contains something more than just one format specifier, then the values are concatenated as strings to form a string literal. So "abc%foo" will evalutate to "abcnull".

The other thing that is worth noting is that the substituted values will always be escaped as ISO-8859-1. It is the responsibility of the user to convert the string literals found in the log to the correct character encoding. Such conversion cannot be done at HTTP server level since it requires the knowledge of the application being run. I would like to thank @nalsh for suggesting the approach.

Friday, March 3, 2017

コマンド一発でソースコード検索&表示できる「peco」改が凄い!

lestrratさんがやってくれました。

ずいぶん前から、ソースコードを検索して読みやすいコマンドはないかなーと思っていました。個人的にはackで検索して見つかったファイルをlessで開いて再びキーワードを入れて当該行までジャンプしていたのですが、毎回毎回めんどくさい感じでした。コマンド一発でインクリメンタル検索してキーワード周辺のソースコードを読めるツールが欲しいなぁって思ってたんです。

とあるslackでお昼時に、mattnさんと「ほしいですよねー」という話から始まって、vimにあるgrepとかも物色しながら「いいのないねー」とか言ってたらkanさんが「@lestrrat 案件だ」って言い出して牧さんが召喚されてついさっきpecoに必要な機能が追加されてました。速いw

ためしにpicotlsの開発ディレクトリでpecoの一行ラッパーperoを起動し、「EVP_Digest」を検索してみました。こんな感じ。


こんなに直感的にソースコードが読める。
コマンドラインになれた人ならイメージ湧くかと思います。インデックスを作るみたいな下準備も何も必要ありません。
ただコマンドを実行するだけで対話的に検索して表示できる。
すばらしい!
こういうのが欲しかったんです。
ただまだ、pecoの本機能はまだtopic/commandブランチからマージされていませんし、バグがあるかもしれません。また高機能にするつもりもないでしょうから使用目的を選ぶのが先決かと思います。

ちなみに、こんな感じの一行ラッパーを書いて使ってます。pero --cc とやれば C のコードが、pero --java とやれば Java のコードだけが検索対象になります。

#! /bin/sh

exec ack "$@" . | peco --exec 'awk -F : '"'"'{print "+" $2 " " $1}'"'"' | xargs less '

ありがたや。ありがたや。

あわせて読みたい:「Big Sky :: ヘッダファイルだけでC++から使えるJSONパーサ「picojson」が凄い!

Tuesday, February 28, 2017

H2O version 2.2 beta released with TLS 1.3 support and other improvements

Today I am happy to announce the release of H2O version 2.2.0-beta1.

The release includes 20 changes made by 10 people. It is great to see that the development effort has become a joint work of such a community.

Below are some of the big changes that went into the beta release.

Case preservation of header names under HTTP/1 #1194

Since the release of H2O, we have always used lowercased header names. This is acceptable from the specifications' standpoint since header names are defined to be case-insensitive. Also, HTTP/2 only allows transmission of the names in lowercase.

However, in practice, there are applications that rely on the case of the header names being preserved by a reverse proxy. And it is technically possible to preserve the case of the characters in HTTP/1.

@deweerdt came up with a pull request that preserves the case of the header names whenever possible. As of this writing, case of the chacacters are preserved between the reverse proxy handler and HTTP/1 clients. Header names transmitted through HTTP/2 will continue to be in lower-case due to how they are encoded in HTTP/2.

Pull requests for preserving the headers communicated through other handlers are welcome.

Directives to modify request headers sent through the reverse proxy handler #1126

@zlm2012 has added configuration directives that can be used to tweak the request headers sent to the application server through the reverse proxy handler.

This has been implemented by refactoring and generalizing the headers handler that has been used to modify the response headers; so now it is possible to modify the request headers in any way that is possible to modify the response headers!

Support for TLS 1.3 draft-18 #1204

Our in-house implementation of TLS 1.3 (named picotls) has landed to master. Picotls provides an efficient (zero-copy) and clean-cut API (designed as a codec rather than an an I/O abstraction) for the upcoming version 1.3 of the TLS protocol.

Thanks to the library, H2O now implements all the features that is necessary to run TLS 1.3 in production and for performance; including support for session resumption, 0-RTT data, OCSP stapling.

Use of picotls is enabled by default; to disable it, set max-version property of the ssl configuration directive to tlsv1.2.

Bug fixes thanks to code analysis #1174 #1110

@hbowden worked on integrating Coverity to H2O. The static analysis tool has found several issues and they have been fixed.

@jfoote and @deweerdt worked on integrating Google's continuous fuzzing to H2O. As a result of the integration, several issues were found and fixed in H2O.

Wednesday, January 18, 2017

H2O version 2.1.0 has been released

Hi, I am happy to announce that H2O version 2.1.0 has been released.

This major update has a long list of changes, but the introduction of the following features might be worth mentioning.


Also, there has been a lot of work done in the reverse proxy implementation to improve interoperability.

In the next major release, we plan to add support for TLS 1.3 as well as more knobs for logging. Stay tuned!

Thursday, January 12, 2017

Fastly に入社しました

Summary in English: Joined Fastly, will continue my work on H2O there as an open-source developer.

2017年1月1日付で、Fastly 社へ転職したので報告いたします。

過去5年間、DeNA では R&D 的な立場から、様々な基盤的ソフトウェア(オープンソースになったものもありますし、クローズドなものもあります)の開発に携わってきました。

最近2年間は、同社のゲーム用サーバに端を発するオープンソースの HTTP/2 サーバ「H2O」の開発に従事してきましたが、その実装品質が高く評価され、世界有数のコンテンツ配信ネットワーク(CDN)である Fastly で採用された他、大規模なウェブサービス事業者で採用にむけた動きが進むなどの成果が出つつあります。
また、H2O における実装経験をもとに、HTTP プロトコルの拡張をインターネットプロトコルの標準化機関である IETF に提案し、ワーキンググループでの検討が行われるという状況にもなってきています注1

ソフトウェアの技術開発で世の中を前に進めようとするならば、ただコードを書くだけでは不足です。使い手にとって便利なように、実地において効率良く動作するように、改善していくことが必要不可欠です。標準化プロセスにおいても、多様な実在するワークロードを元に効果を証明できることが、説得力の点で重要になります。

これらの点に鑑みると、H2O という育ちつつある芽を大きく花咲かせるために、自分が今、身を置くべき場所は、DeNA ではなく、H2O の世界最大の利用者であり、世界有数の規模の HTTP トラフィックを捌く事業者であり、HTTP を高度に運用することを事業のコアとしている Fastly なのではないかと考え注2、転職を決意するに至りました。

転職したといっても、このような経緯なので、職務の内容が何か変わるわけではありません。これまでと同様に、オープンソースソフトウェアである H2O の開発をリードしていくのが僕の役割になります。Fastly 社内でしか得られない知見や実験の成果もオープンソースとして H2O に還元され、あるいはプロトコルの拡張として標準化を提案していく予定です。

今後ともよろしくお願いいたします。引き続き東京ベースで活動しますので、何かありましたら気楽にお声がけください。

また、末筆になりますが、H2O の開発をこれまで支え、笑顔で送り出してくれた DeNA の上司と同僚にはありがとうを伝えたいと思います。開発は DeNA 社内でも引き続き行われます。

注1: Cache Digests for HTTP/2, Call for Adoption: Early Hints (103)
注2: HTTP サーバのどの側面に注力したいかによって摂るべき選択肢は変わると思いますが、僕の場合、最近の活動はサーバとアプリケーションの間よりも、サーバとクライアントの間の通信改善に関わるものが多くなってきていました。