non vorrei lavorare

昔はおもにプログラミングやガジェット系、今は?

Windowsでmruby-http2を動かした

こんにちは、kindle fireYouTubeで車モノ(knight riderやワイルドスピード)の映像を中毒患者の様に見まくる長男からタブレットを修理に出したといって、隔離してしばらくたち、今のところ、長男は隙を見て奥さんがおもに使っているChromebookYouTubeを見ています。@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が使えるかもしれません。

関連記事

11年前の記事

10年前の記事

9年前の記事

8年前の記事

7年前の記事

6年前の記事

5年前の記事

Rust入門者向けハンズオン #2 に行ってきた

おはようございます。昨年末あたりから、次男も箸でご飯を食べると言い出し、エジソンの箸で次男も食事をしています。@kjunichiです。

#

Rust入門者向けハンズオン #2 - connpass

let num = num + 1;

では、numが加算されなかった。

関連記事

13年前の記事

9年前の記事

8年前の記事

5年前の記事

4年前の記事

Windows 10でMSVCでnghttp2のサンプルを動かす

おはようございます。2年連続で正月は多摩川の河川敷で子供たちと凧揚げをしたkjunichiです。

f:id:kjw_junichi:20170103150835j:plain

概要

nghttp2はMSVCでもライブラリ単体はビルド可能とgithubリポジトリのREADMEにも書かれている通り、 cmakeで特に依存ライブラリは必要ともせず、容易にビルドできる。

しかし、いざ、nghttp2ライブラリを使おうとしたら、数日ハマった。

書いた人の背景

自分は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のビルド

chocolateyActivePerlnasmを入れてから、公式のドキュメントに従い、インストール

libeventのビルド

git cloenしてcmakeでビルド、上でいれたOpenSSLを自動的に検出してくれたハズ。 VS 2015でビルドしてインストール

nghttp2のビルド

cmakeでビルドできるが、公式の指定だと、32ビットでビルドされてしまったので、 開発者向けプロンプトを開いて以下を実行した。

set PATH=%PATH%;
cmake -G "Visual Studio 14 2015 Win64" .
cmake --build .

管理者権限で開発者プロンプトを開いて

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

IPv6が有効な環境だと、IPv6でのアクセスのみ受け付けるようだった。getaddrinfoの第一引数のNULLを"0.0.0.0"に変えたら、 IPv4で通信ができた。

まとめ

OpenCVのビルドやh2oのビルドで使うことになったcmakeがlibeventやnghttp2でも使われていることを知った。 いまだに64ビットがデフォルトではなく、明示的にビルド時に指定する場面があることが驚きだった。 libeventやOpenSSLのバージョンに少し詳しくなれた。

もしかするとzlibを入れておく必要があるかも。

関連記事

4年前の記事