Windowsでmruby-http2を動かした
こんにちは、kindle fireでYouTubeで車モノ(knight riderやワイルドスピード)の映像を中毒患者の様に見まくる長男からタブレットを修理に出したといって、隔離してしばらくたち、今のところ、長男は隙を見て奥さんがおもに使っているChromebookでYouTubeを見ています。@kjunichiです。
mruby-http2 for win32
Xeonで昨年末久々にPC組んだ流れで、Windowsでの開発環境をあれこれ試している。 そんな中、今回はmrubyでhttp2をしゃべることが可能になるmrbgemのmruby-http2をWindowsで動かしてみた。
事前に必要なモノ
- openssl
- libevent
- nghttp2
- zlib
zlib以外は、
- Windows 10でMSVCでnghttp2のサンプルを動かす
の記事に少し書いてます。
ヘッダ無い問題
以下に挙げるヘッダがWindowsでは用意されていない模様。
- pwd.h
- unistd.h
- sys/socket.h
- sys/wait.h
- sys/resource.h
- sys/queue.h
- netdb.h
- netinet/in.h
- netinet/tcp.h
- poll.h
- signal.h
- pthread.h
- err.h
定数無い問題
mruby-http2をWindowsでビルドする際、以下の定数が未定義でエラーとなった。
- F_GETFL
- O_NONBLOCK
- SHUT_WR
型や構造体無い問題
mruby-http2をWindowsでビルドする際、以下の型が未定義でエラーとなった。
- ssize_t
- uid_t
- nfds_t
- struct pollfd
関数無い問題
mruby-http2をWindowsでビルドする際、以下の関数が未定義でエラーとなった。
- pipe
- fcntl
- alloca
- gmtime_r
- fork
- TAILQ_FOREACH
- kill
- daemon
pipe
_pipeなる関数が用意されており、これを用いることで対応出来た。 ただし、引数がUNIXのpipeと異なり3つ必要。
#ifndef _WIN32 rv = pipe(pipefd); #else rv = _pipe(pipefd, 65535, _O_BINARY); #endif
作成するパイプのバッファサイズの指定は決め打ちで暫定的に対処した。
fcntl
mruby-http2ではfcntlでソケットをノンブロッキング指定にする為に用いられていた。 Windowsではこのような用途にはioctlsocketを用いることで対応出来た。
#ifndef _WIN32 while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) ; if (flags == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "fcntl: %S", mrb_str_new_cstr(mrb, strerror(errno))); } while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) ; if (rv == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "fcntl: %S", mrb_str_new_cstr(mrb, strerror(errno))); } #else flags = 1; rv = ioctlsocket(fd, FIONBIO, &flags); if (rv != NO_ERROR) { mrb_raisef(mrb, E_RUNTIME_ERROR, "ioctlsocket: %S", mrb_str_new_cstr(mrb, "error")); // TODO } #endif
シグナルがWindowsでないと勘違いしてた問題
シグナル周りは一旦全部コメントアウトして無視することにして対応した。
- struct sigaction
- sigaction
関数自体を無効にしたものたち
単純にWindowsで動かすにには不要な機能はスパッと諦めて作業した。 その結果、以下の関数は、戻るだけだったり、固定値を返すだけにしたり、関数そのものをコメントアウトした。
- mrb_http2_get_uid
- mrb_http2_config_get_worker
- tune_rlimit
成果物
mrubyのbuild_config.rbに以下を記述することでwindowsでビルド可能なmruby-http2が利用できるハズ。
conf.gem :github => 'kjunichi/mruby-http2', :branch => 'support-for-win32'
まとめ
クライアントは、これらの問題を解決することで、動かせた。 サーバー側は、さらに対応が必要だった。これはまた、別の記事にするつもり。
ちなみに、mruby-cliでバイナリを作る場合は、今回のMSVCの対応とは別にmingw-w64での対応が必要となる。 もし、dockerを使わず直接Windows上でmruby-cliでワンバイナリをMSVCで作製すれば、今回のmrbgemが使えるかもしれません。
関連記事
- http2 ハッカソン #5に行ってきた
- libtrusterdでHTTP/2でphpを動かして分かったことのその後 #知見
- Windows 10でMSVCでnghttp2のサンプルを動かす
- Windows(MSVC)でmrubyからGPU対応のTensorflowを動かせた
- WindowsでDLLを作ろうとしてmrb_context_runの歴史を調べた
11年前の記事
10年前の記事
9年前の記事
8年前の記事
7年前の記事
6年前の記事
5年前の記事
Rust入門者向けハンズオン #2 に行ってきた
#
VS CodeだからRustどうだろと思ったが、ちゃんとVS Code向けにCargoと連携できることを知った。
— kjunichi (@kjunichi) 2017年1月15日
今日は面接じゃないから落とされる心配なしw (@ 渋谷ヒカリエ in 渋谷区, 東京都) https://t.co/moykmzhSxD
— kjunichi (@kjunichi) 2017年1月15日
#Rust_jp ヒカリエ17Fやっぱり11Fの受付で待つこに、無事、会場に上がれた。
— kjunichi (@kjunichi) 2017年1月15日
CSSのレンダリングもRustでの実装に置き換えようとしてるとのこと #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
Typeに近いんじゃにかと < cat #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
#Rust_jp Rustなら、ディレクトリも作らなくて、Hello Worldできるのか! https://t.co/Gs9eBWjaNe pic.twitter.com/jsqn6Fp9bf
— kjunichi (@kjunichi) 2017年1月15日
動いた! https://t.co/uSDAIwuPGL pic.twitter.com/KRT5X6plkY
— kjunichi (@kjunichi) 2017年1月15日
OCamlのletどうだっけかなぁとふと思った。 #Rust_jp
— kjunichi (@kjunichi) 2017年1月15日
「;」つけない関数末尾はreturn 文なのかなぁ #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
再帰呼び出しっていうと、末尾再帰が気にあるが、できるらしいが、何か制約があるような #Rust_jp
— kjunichi (@kjunichi) 2017年1月15日
行単位で文字列を取ってくることに苦戦中 #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
なんとかlinesで取得したイテレータを回せたが、今度は行番号が全部1で出力された #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
let num = num + 1;
では、numが加算されなかった。
OpenCVでカメラの画像を表示するやつが以前Rustで作っていたが、これ今のRustでも動いた もくもくタイムに少し進められると良いが、catコマンドでも力尽きそうw #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
linesで行単位で文字列を取り出せることがわかったが、スタックオーバーフローのコードのSomeがまさかRustの命令?とはおもわず、これでてこずった #rust_jp
— kjunichi (@kjunichi) 2017年1月15日
関連記事
- RustでFaceTime HDカメラやWebカメラを使う
- RustでOpenGLやGLSLやる準備
- macOS Sierraにしたらrustが暴走して、黒い画面には入れなくなった #解決済み
- Rustで多次元配列を扱うには
- libtrusterdをRustで動かした
- RustでWebカメラの映像をコマンドプロンプトに出すコマンドを作った
- 2018年版、Rustで多次元配列を使うには
13年前の記事
9年前の記事
8年前の記事
5年前の記事
4年前の記事
Windows 10でMSVCでnghttp2のサンプルを動かす
概要
nghttp2はMSVCでもライブラリ単体はビルド可能とgithubのリポジトリのREADMEにも書かれている通り、 cmakeで特に依存ライブラリは必要ともせず、容易にビルドできる。
しかし、いざ、nghttp2ライブラリを使おうとしたら、数日ハマった。
一日がかりで、nghttp2をMSVC環境でライブラリのみをビルドして、examplesのlibevent-server.cを動かそうとしたら、libevent側でbufferevent_openssl.cをWindowsは除いてビルドしているので、結局リンクエラー
— kjunichi (@kjunichi) 2017年1月7日
nodeはWindowsをサポートしており、http2をサポートする際はnghttp2を使う予定とのことで、この週末、windowsでnghttp2のサンプル動かそうとしてるが色々ハードル高くて勉強になる#と前向きなツイートしとくか
— kjunichi (@kjunichi) 2017年1月8日
何とかhghttp2のサンプルのlibevent-server.cをwindowsでビルド通したが、writecbが動いたあと、ブラウザに応答が戻らない
— kjunichi (@kjunichi) 2017年1月9日
WindowsのMSVC環境でlibeventのマスターをビルドしてそのサンプルを動かせ、libeventでHTTPサーバーは動かせた。しかし、httpsでのクライアントのコードはあり、これも動かせたが、サーバーのコードがないので、OpenSSLでサーバーができるのか現時点で不明
— kjunichi (@kjunichi) 2017年1月9日
MSVC環境でのnghttp2のサンプルが動かない問題。切り分けの為に、自前でビルドしたlibeventとOpenSSLの組み合わせでhttpsサーバーは動いた。 / “httpsserver.c · GitHub” https://t.co/Fqcm6ds3zO
— kjunichi (@kjunichi) 2017年1月11日
書いた人の背景
自分はWindows初心者だが、cygwin使えばこの手のものは動くのは分かっている。
2016年のMacBook ProがNVIDIAじゃないからCUDA使えないし、mrubyでクロスビルドでWinなバイナリを作った 経験も溜まってきたし、安定の開発者の天国のLinux環境では当たり前すぎるから、 最近Windows環境への定住を本気で考えている。
要約すると金が無いから新しいMacBook Pro買えないだけ。
nghttp2ライブラリを動かす為に必要な物
ホントにスキルがあれば、ライブラリ本体を使うだけでも動かせると思う。
が、一般人は、TLS周りにOpenSSL使ったり、非同期ライブラリ?としてlibeventを使ったりして、 これらを組み合わせてnghttp2ライブラリをつかってHTTP2が喋れるサーバーを組むことになる模様。
- OpenSSL
- libevent
OpenSSL、libeventともにWindowsで動くバージョンが複数あり、少しハマった。
OpenSSLのバージョン
1.1系で作業をはじめて、途中動かなかったから、OpenSSLのバージョンも疑ったが、1.0系でなくとも問題なさそうだった。
libeventのバージョン
これが問題だった。安定版と言われている2.0.x系はWindowsプラットフォーム、少なくともMSVC環境では、openssl周りの面倒を見てくれない。 ここはおとなしくgithubからcloneして最新版(2.1.x系)を使った。
OpenSSLのビルド
chocolateyでActivePerl、nasmを入れてから、公式のドキュメントに従い、インストール
libeventのビルド
git cloenしてcmakeでビルド、上でいれたOpenSSLを自動的に検出してくれたハズ。 VS 2015でビルドしてインストール
nghttp2のビルド
cmakeでビルドできるが、公式の指定だと、32ビットでビルドされてしまったので、 開発者向けプロンプトを開いて以下を実行した。
set PATH=%PATH%; cmake -G "Visual Studio 14 2015 Win64" . cmake --build .
nghttp2をビルド https://t.co/5lowomQDLi pic.twitter.com/QMwgR1AeVi
— kjunichi (@kjunichi) 2017年1月7日
管理者権限で開発者プロンプトを開いて
set PATH=%PATH%; cmake --build . --target install
これで、nghttp2のexample/libevent-server.cをビルドする準備が整った。
libevent-server.cを動かす
各種修正
ソケット周りをWinsock2使う修正
#ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif /* HAVE_NETINET_IN_H */ +#if defined(_MSC_VER) +#include <winsock2.h> +#include <ws2tcpip.h> +#else #include <netinet/tcp.h> +#endif
int main(int argc, char **argv) { + #if defined(_MSC_VER) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2,2), &wsa_data); + #else struct sigaction act; + #endif if (argc < 4) {
SSIZE_T問題の対応とエラー出力周辺の対応とU対応とO_RDONLY対応
#include <event2/listener.h> +#if defined(_MSC_VER) +void +_verrx(int eval, const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", "server"); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _verrx(eval, fmt, ap); + va_end(ap); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _verrx(0, fmt, ap); + va_end(ap); +} + +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; +typedef SIZE_T size_t; +#include <io.h> +#include <fcntl.h> +#define O_RDONLY _O_RDONLY +#define _U_ +#endif + #include <nghttp2/nghttp2.h>
pipe対応
Windowsにもpipeに似た_pipeがあり、とりあえずこれで今回は回避できた模様。
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; - rv = pipe(pipefd); + rv = _pipe(pipefd, strlen(ERROR_HTML), _O_BINARY); if (rv != 0) {
libevent 2.1.x対応
これが今回のキモ。 openssl統合されたlibeventをWindowsで使うには2.0系ではなく、2.1系のlibeventを使うのだが、 nghttp2のサンプルのlibevent-server.cはlibeventの2.0向けに書かれており、 以下の修正を行わないとサーバーが反応しない (たったの1行追加だけど)
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
+ bufferevent_enable(session_data->bev, EV_READ);
}
ビルド
INCLUDE,LIBの設定
gcc,clangのような-I,-Lのオプションを知らないので、cl.exeでは以下のように環境変数で指定した。
set INCLUDE=%INCLUDE%;C:\Program Files\OpenSSL\include;C:\Program Files\libevent\include;C:\Program Files\nghttp2\includes set LIB=%LIB%;C:\Program Files\nghttp2\lib;C:\Program Files\libevent\lib;C:\Program Files\OpenSSL\lib
ビルド
cl libevent-server.c nghttp2.lib event.lib msvcrt.lib ws2_32.lib libssl.lib libcrypto.lib Advapi32.lib /link /nodefaultlib:libcmt /nodefaultlib:msvcrtd
実行
set PATH=%PATH%;C:\Program Files\nghttp2\lib libevent-server 8080 key.pem cert.pem
ようやくWindowsでnghttp2 ライブラリ使ってHTTP2でサーバー動かせた! https://t.co/hRFaVx8DJZ pic.twitter.com/D4mnR04ocx
— kjunichi (@kjunichi) 2017年1月12日
IPv6が有効な環境だと、IPv6でのアクセスのみ受け付けるようだった。getaddrinfoの第一引数のNULLを"0.0.0.0"に変えたら、 IPv4で通信ができた。
まとめ
OpenCVのビルドやh2oのビルドで使うことになったcmakeがlibeventやnghttp2でも使われていることを知った。 いまだに64ビットがデフォルトではなく、明示的にビルド時に指定する場面があることが驚きだった。 libeventやOpenSSLのバージョンに少し詳しくなれた。
もしかするとzlibを入れておく必要があるかも。
関連記事
- Windowsでmruby-http2を動かした
- Windows(MSVC)でmrubyからGPU対応のTensorflowを動かせた
- Cygwinでh2oが動いた日
- 久しぶりに自作PCをリニューアルした