some points for HTTP client on native applications (1)
はじめに
HTTP は利用頻度が高いネットワークプロトコルの一つだと思います。HTTP サーバを書くことはそんなに多くないと思いますが、HTTP クライアントは何らかの形で利用したり実装したりしたことがある人は多いのではないでしょうか。今回は、ネイティブアプリでの HTTP クライアントを利用・実装することに焦点を絞ります。
多少経験のあるプログラマなら、HTTP クライアントを自分で実装したいとはあまり思わないはずです*1。HTTP は、仕組みとしてはシンプルですがいざ実装しようとすると中々大変です。ソケットを使ってデータ転送をする処理や、HTTP リクエストの妥当性検証*2や生成、レスポンスの解析、ステータスコードの正しいハンドリング、データのバッファリング・・・。さらに、国際化ドメイン名、キープアライブ、リダイレクト、認証*3、プロキシ解決*4、プロキシ越え、HTTPS なんかも考え始めると頭が爆発しそうになるからです。フルスタックの HTTP クライアントを実装するのは非常に大変です。
ライブラリを使おう
そうなると、偉大なる先達が作ってきたものを利用しようという方向に頭が働きます。幸い、HTTP クライアントのライブラリは数多く存在します。
- プラットフォーム標準搭載のライブラリ
- Windows
- WinInet
- WinHTTP
- Cocoa
- NSURLConnection
- NSURLDownload
- Core Foundation
- Windows
- プラットフォーム非依存のライブラリ
libcurl を使おう
大抵の場合、libcurl で事足ります。マルチプラットフォーム対応している優れものです。ただし、libcurl には以下の機能・問題がないので注意が必要です。
- プロキシに Digest 認証が必要な場合、HTTPS 通信がエラーコード CURLE_RECV_ERROR ( 56 ) で失敗する
- curl-Bugs-845941 fetching https, curl returns code 56 on 407 from proxy
- 結構古くから存在するバグだが、最新の libcurl でも修正されていない
- バージョン 7.21.3 で確認
- プロキシ解決機能がない
- Unix 系 OS 以外でのルート証明書のインポート機能がない
というわけで、プロキシに Digest 認証が必要な環境で HTTPS 通信がしたい、という場合、現状の libcurl を利用することはできません。上記で挙げたプラットフォーム標準搭載のライブラリは、どれもうまく処理してくれます。
プロキシ解決
プロキシ環境をサポートする場合、まず最初にやるべきことはプロキシ解決です。プロキシ解決は、どのプロキシサーバを利用するかを決定します。ないならないで利用しません。Internet Explorer や Google Chrome を利用している方なら、ステータスバーに「プロキシを解決しています」というメッセージが表示されていることに気づいている方もいると思いますが、あれのことですね。
プロキシ解決は、アプリケーション固有のプロキシ設定を用いる場合と、プラットフォームが持つ設定を踏襲する場合があります。前者はここでは特に触れず、後者に焦点を絞ります。
Windows や Mac OS X では、プロキシ解決を以下の手順で行うことができます。
- プロキシの自動検出
- プロキシの自動構成
プロキシの自動検出は、Web Proxy Autodiscovery Protocol ( WPAD ) に基づいて行われます。大雑把にいうと、DHCP または DNS サーバに置いてある自動構成スクリプトファイルを持ってくる感じです。
WinINet なら、DetectAutoProxyUrl() を呼び出すことでこれが行えます。オプションとして、DHCP を用いるか、DNS の A レコードを用いるか、どちらもかを選択できます。呼び出すと、以下のような通信が発生します。
- DHCP を用いる場合
DHCP サーバがこれに応答するように構成されていない場合、 この API の実行に10秒以上かかることがあります。エラーコードは ERROR_NO_TOKEN ( 1008 ) などです。また、1 秒程度で完了することもあります。
DNS サーバがこれに応答するように構成されていない場合、 この API の実行に 5 秒程度かかることがあります。エラーコードは WSAHOST_NOT_FOUND ( 11001 ) などです。
自力で実装するなら、ちょっとした DHCP クライアントを書く必要があるってことですね。また、自動構成スクリプトのダウンロード自体にも HTTP が必要という罠があります。
こうして、自動検出が完了したら次は自動構成スクリプトを解釈します。
次回へ続きます。
apache のミラーサイトに facebook が!
-
-
- > Attempting to fetch apr-1.4.2.tar.bz2 from http://mirror.facebook.net/apache/apr
-
大工のようなプログラマ
私の父は大工である。二級建築士で、大抵は二階建ての木造住宅を建てている。
私が学生のころ、たまに小遣い稼ぎとして父の手伝いをしていた。建前、床張り、ベニヤ張り、天井張りなどのハードワークがメインだ。特に天井張りはよく覚えている。普段はひとりでこなしているというのだが、どうやって二本の手ででかい石膏ボードを天井に添え、電動ドリルを持ってビスを打ち込んでいくのだろうと不思議に思ったものである。
少なくとも、二級建築士免許だけを持っていても絶対にできないだろう。ある程度の筋力・技術・経験が不可欠だと思う。これは、基本情報技術者などの試験の合格していてもプログラミングができるという訳ではない、ということに似ていると思う。
私は仕事をはじめてから手伝いをしなくなったが、少し前に建前の手伝いにいった。真夏の炎天下での肉体労働は過酷であったが、私は手伝いを終えてビールを飲みながら、大工のようなプログラマになりたいと思った。
これは、企画・見積・設計・実装・試験といった全工程に関わり、チームへの指示を出し、自らも各作業を行いながら、小中規模の価値のあるソフトウェアを、出来る限り作り続けていきたい、ということである。私の感触では、大規模開発だとこういった振る舞い・細部に至る丁寧な作り込み・めまぐるしい市場の変化への対応が難しそうなので、今はあまり興味が湧いていない。あとグラフィックデザイナーとしては作業できない。大工の人だって、電気・水道工事などは別の人が担当するからそういうのがあってもいいよね。他にも、企画・見積・設計・試験・効果の高いチームプレーなど苦手はことはたくさんある。
中学三年のときにPCに触れ、ゲームプログラミングをはじめてからもうすぐ15年になる。大工のようなプログラマになるにはもう少し時間がかかりそうだ。だが不可能ではないと感じているし、もうすぐそうなれるという予感はしている。
今回の手伝いは、学生のころと違い小遣いはもらえなかったが、自分を見つめ直すいい機会になったし体も少し鍛えられたので大儲けだったと思う。
ナイスガイから教わった C でコードを書くときのほにゃらら 01
二年前に出会ったナイスガイから C でコードを書くときに大事なことをいくつか教わった。
教わったことは、何かしら自分でまとめて誰かに伝えていかねばと思っているのでぼちぼちまとめていく。
移植性を高くする
以前の私は環境限定のコードを書くことが多かった。Windows アプリケーション・ライブラリを書くことがほとんどで、
しかも多種多様な Windows API を叩くことがほとんどだったということもあり、あまり移植性に配慮していなかった。時間もかかるし。
ナイスガイから教わったのは、こういったシステム固有処理をとにかく外に追い出すということだった。実際にナイスガイが書いたコードを見せてもらうと、ソケットをはじめ、メモリハンドリングなど CRT の一部も外に出していた。
一年と少し前、そういった Windows クライアントアプリケーションをモバイルデバイスに対応させようという話が出た。そこで、ナイスガイから教わったことを実際に自分でも試してみた。
結果として、Android、Windows Mobile 6、Windows、Mac OS X で動作するアプリケーションを作ることができた。それぞれのシステム固有処理と、アプリケーションのエントリポイントを少しいじるだけで普通に動く。しかも、ベースができた後に移植するときは他の人と分業してすすめることができたのだ。
また、移植性を高くしたことで十分な抽象化がなされ、インターフェースが整い、単体テストが奥までよく通せるコードになった。メモリハンドリングなども外に出しているので、単体テストではそれ向けにスタブを用意して C1 を達成することができた。
まとめ
- 移植性を高くすることで次の効果が得られる
- モジュールの抽象化が高まる
- モジュールのインターフェースが整う
- 並行開発・分業しやすくなる
- 単体テストしやすいコードになる
- コードの品質が高まる
具体性に欠ける内容ですが、またぼちぼち書いていきます。
epoll と kqueue について思ったことの垂れ流し
C10K Problem では、epoll/kqueue は速く、select/poll などは遅い、というのをよく見かける。実際ベンチマーク結果を見ると、そのように見える。計算量的には、epoll/kqueue は O(log2N)、select/poll は O(N) のように見える。ベンチマーク結果は libevent のページに掲載されているものなどが参考になる。epoll に関してはおそらく epoll_wait() のみで、epoll_ctl() は入っていないと思われる。
- libevent
計算量から考えると、select/poll は線形な実装で、epoll/kqueue は何かしら木を使った実装なのかな、と予想できる。木構造を使っていれば、要素の追加・削除も大抵は O(log2N) で済むだろう。イベントが発生したときに通知する処理は、配列に対して行われるので O(N) になるだろう。ただ、select/poll は変化がないものも返すので遅いらしい。
- epollのなかみ - muddy brown thang
この辺を考えていてふと思ったんだけど、epoll/kqueue はファイル記述子をキーにしたハッシュを使えばもう少し速くなるんじゃないかな。ハッシュは基本的に計算量が O(1) で済むし。よく分かってないが、Linux や BSD などでは、ファイル記述子は 0 が標準入力、1 が標準出力、2 が標準エラー出力となっていて、プロセスでファイル記述子を使うと 3 以降が使われる。これは使うたびに 4、5、6・・・となっていって、5 を閉じて新たにファイル記述子を使うと、7 ではなく 5 が再び利用出来る。この挙動については open() の manpage に記載されている。POSIX の仕様?
なので、キーの範囲は正の整数かつ小さい値から順に使われるというとてもナイスな特性を持つ。最大接続数を制限してやれば、単純にファイル記述子をキーにしたテーブルを作ってしまえばいい。65536 個の接続を捌くにしても、64K x N バイト程度で済むので大した問題ではないように思える。コードもすごくシンプルになる。このままだと使用されているファイル記述子をたどるのが面倒なので、逆引きできるようにしておく。
epoll も kqueue もそうだが、結果を配列に入れて返すというのがイマイチだと思う。コールバック指定するからそれを呼び出してよ、と思う。そっちの方が効率いい気がする。つうことは、I/O 多重化よりも 非同期 I/O の方が効率いいのかな。この辺は、KLab さんの資料も参考になる。
そういう意味では、Windows の I/O 完了ポートが最強なのかな。
Android のソースコードの一部をサクっと入手する
Android のソースコードの一部をさくっと入手する方法を見つけたので紹介しておきます。
http://android.git.kernel.org/?p=platform/external/skia.git;a=snapshot;h=donut;sf=tgz
こんな風に git レポジトリとヘッドを指定してやると、tar.gz がダウンロードできます。
簡単にパラメータの説明を。
- p
- ダウンロードしたいプロジェクトを指定する
- 以下のページで指定できるプロジェクトを確認できる
- a
- ダウンロードする場合、snapshot で OK
- h
- ダウンロードしたいヘッドを指定する
- sf
- ダウンロードする形式を指定する
- tgz 以外にもいろいろあるかも?
ちなみに、ブラウザからじゃないと動作しません。
wget や curl だとリダイレクト中の HTML がダウンロードできるだけです。