1. yammerの日記
  2. gawkでHTTPリクエストを読み込む
2024-01-15 11:10

gawkでHTTPリクエストを読み込む

gawkでWebアプリケーションを作るとき、HTTPリクエストボディの読み込みが結構面倒で、その話をしました。

 

コードはこちら。果たして読む人はいるのか、もしいたら相当な物好きでしょう…

https://github.com/yammerjp/awkblog/blob/4b8c69581f37e83a2e814d8ee66a566d0f435091/src/lib/http.awk#L14

 

話の途中に出てくる、ボディの末尾を読み込む挙動のよく原因がわかっていないというあたり、詳しい方がいたら教えて欲しいです。

00:03
こんにちは。
前回はまた明日という風に言ったんですけど、数日経ってしまいました。
1日の中でうまく録音できる時間を見つけられなくて、まだ慣れていない感じがあります。
実は日中に録ったりしてみたりもしたんですけど、
結構ノイズが入ったりしてあんまり微妙だなとなって、
あとはなんかやっぱり一回、一発で録ってみてもうまくしゃべれてなかったりして、
やめてしまってまた録っているみたいな感じになってしまっています。
前回も同じだったんですけど、仕事の帰りに夜の道を歩いて録っています。
結構夜の道は静かで、公園とかよりも静かでですね、結構いいんじゃないかなと思っています。
今日も寒いですね。
前回はGeorkでネットワークプログラミング、ネットワーク通信の機能がサポートされているので、
ウェブアプリケーション作れるという話をしました。
今日はHTTPリクエストのヘッダとボディを読み込む話をしたいと思います。
今日の話ができれば、HTTPは大体実装できたということになるんじゃないかなと思います。
Georkのネットワークプログラミングは、ファイルや標準入力からの読み込みと同じように扱うことができます。
デフォルトでは業ごとで文字列を読み込むことができます。
なので、HTTPヘッダの読み込みは単純です。
HTTPヘッダ自体が業ごとに分かれているので、1行ずつ読んでいけば良いだけです。
一番最初にHTTPのリクエストメソッドなどが書かれているので、それを読んで変数に書くのをし、
2行目以降はヘッダが続くので、これを多くの配列にキーとバリューで入れておいて、
後で読み出せるようにすれば良いです。
例えばコンテンツタイプがアプリケーションJSONであるとか、そういったことをキーとバリューで入れていけば良いということになります。
空経まで読むとHTTPヘッダが終わりで、これでここまでは単純に読めた読めたとなるんですが、
03:00
ボディは結構面倒くさいです。
回用文字が来るかどうか分かりませんし、そもそも行単位になっているかどうか分からないですし、めちゃくちゃ長い可能性もあります。
こんなことにHTTPにはコンテントレングスというヘッダがありますので、これで長さは分かる。
なので何倍と読めば良いかということは分かります。
ジオークの私の実装ではこのヘッダーがなくていいポストリクエストは不正なものとして400系の411かなのステータスコードを返すことにしています。
なので指定された倍と数分読まなきゃいけないと。
これをどうするかですけれども、ジオークのネットワークプログラミングのドキュメントを読んでも、
HTTPのヘッダーを扱う実装は書いてあっても、リクエストボディを扱う実装は書いてなくてですね、困ったものだと。
ネットに実は先駆者の方がいて、その実装を真似させてもらいました。
ちょっと変わった面白い実装なんですけど、
多くの組み込み変数、RSというのがありまして、これは先ほどデフォルト開業といった区切り文字を指定することができる変数です。
開業文字以外に他の文字を入れると、その文字を区切り文字としてその前までを読むということができます。
例えば、Cという文字を入れたとすると、入力の中にCというのが含まれる前までを一回の読み込みで読み、Cを区切り文字として読まないというような処理になります。
ただ、どんな文字が来るかわからないので、開業もダメですし、Cもダメだと。
GEORGではこの組み込み変数RSに正規表現を使うことができるという拡張があります。
そこで正規表現にドット並括弧線並括弧と字みたいなことを書きます。
つまり2の文字が1000文字続くという区切り文字を受け入れることにします。
こうするとどうなるかというと、入力が来て、その入力を一文字もその読み込みの中身とは読み込まずに、一番最初から区切り文字として1000文字読むということになります。
なので、ゲットラインという関数で一行といいますか、一回分読み込むんですけど、ゲットラインした結果は空になっていて、
どんな区切り文字を使ったかというのが組み込み変数RTで取れるので、その中に1000バイト分の文字列が入っているという風になります。
06:04
なので、コンテンツレングス分、1000文字未満だったらそこまで読んで、1000文字以上だったら繰り返して何回か1000文字分割して取っていけば、
Httpのボディを読み込むことができるようになります。
何で1000文字かというと、ジオークの処理系で一度に扱える正規表現の最大の長さが1000文字のようで、
実装試してみると最初めちゃくちゃ長くしたら怒られて、1000文字だったら許容できるので1000文字にしています。
これでよしよしめちゃくちゃ長いし、最後まで読めたという風には実はならなくてですね。
またこれもクセものといいますか、次は原因がよく分かっていないんですが、最後まで読み込めないんですね。
最後まで読み込むと処理が止まってしまって、そのゲットラインの処理が終わらずに次の行が実行されないというような挙動をしてしまうんです。
困ったものですねということで、原因がよく分かっていないんですが、内部でバッファリングをしていてそれでバッファが埋まるまでは処理が返ってこないとかそんなようなことなのかなって疑ってるんですけど、
本質的ではないような解決策で回避しています。
最後の方が読めないんですね。
例えば最後の1文字とか、間に合ったら5文字とか数百文字とか読めない場合があるんで、数百文字読めないかちょっと試してないんですけど最後の方読めないっていうような100文字ぐらい読めない時もあったんで、
もう読めないものは読まないとしています。
なのでリクエストの最後の方読まないので欠けてしまうこともあると。
HTTPのボディが欠けてしまうとそれはそれでまずいので、どうしたかというと、
エンジェンクスのレバースプロキシを置いてリクエストの後ろの方に無駄な文字をたくさん512バイトとか足してですね、
その足した分を独自のHTTPヘッダーに何文字足したかというのを入れておいて、
アプリケーション層では、アプリケーション層と言いますか、
ジオークのプログラムからはその文字数分読み捨てるっていうような実装をしています。
なのでコンテントレングス引くその無駄な文字数分、
XBODYLEFTOVERっていうヘッダー名にしますけど、
その文字数分引いて、無駄な文字付けてそれ引いてっていうような処理しています。
これで無事最後まで読めるようになりました。
ちょっとリバースプロキシスカーの負けた感があるというか、
ジオークだけで解決できなくて悲しいところはあるんですけど、
09:00
動作するかだけで言うと、実はブラウザのフォームのリクエストのパラメーターの一番最後に、
読み飛ばす用のインプットタグとタイプヒデンで、
バリューに1000文字とか入れておいて、その1000文字も読み飛ばすとかすると、
一応動きはするんですね。
ただこのキーとバリューの順番が固定なのかっていうのもよく分からなくて、
ちょっとルールあるんですかね。
HTMLと言いますか、HTTPと言いますか。
あとはウェブアプリケーションでJSONも受け付けてるんですけど、
JSONもね、Javascriptでクライアントサイドのブラウザ内で動くことを自分で書いてるときは、
1000文字も後ろに空白出したりとかすればいいんですけど、
外部からのリクエストも来ないこともないと思うんで、
来ないんですけど、私が作ってるウェブアプリケーションなので、
私がリクエスト飛ばすぐらいしかないと思うんですけど、
でも、JSONの後ろの方が足りなくて、
パーセーラーとかされても微妙ですし、
ということで、しょうがなく、
NginxのReverse Proxyを置いて回避しています。
こうすることで、HTTPのリクエストのヘッダとボディを読むことができるようになりました。
ここまでできれば、レスポンスを返すのはただ出力するだけですので、
もうほぼHTTPが実装できたというふうに言えると思います。
というわけで、次回からはそれ以降のレイヤーの処理について話したいなというふうに思います。
では、今週もやっていきましょう。またお会いしましょう。
11:10

コメント

スクロール