xyzzy-022 / xyzzy

xyzzy 0.2.2 系列。有志により開発が継続中です。
http://xyzzy-022.github.com
MIT License
193 stars 44 forks source link

SSL接続時のEOFが検出されない #427

Open kosh04 opened 8 years ago

kosh04 commented 8 years ago

xyzzy 0.2.2.248 リリースノート に書かれている SSL クライアントのサンプルコードを実行すると、ストリームの終端の受信待ちから先に進まずプログラムが応答しなくなるようです。

(connect "www.google.co.jp" "http") に差し替えても同じ挙動になります。

(with-open-stream (stream (connect "www.google.co.jp" "https" :ssl t))
  (format stream "GET / HTTP/1.1\n")
  (format stream "Host: www.google.co.jp\n\n")
  (let (line)
    (while (setq line (read-line stream nil))
      (insert line "\n"))))
;;-> C-g を押すまで終了しない

ただし、HTTPS ポートを開いていない www.yahoo.co.jp の http 接続は問題ありませんでした。

(with-open-stream (stream (connect "www.yahoo.co.jp" "http"))
  (format stream "GET / HTTP/1.1\n")
  (format stream "Host: www.yahoo.co.jp\n\n")
  (let (line)
    (while (setq line (read-line stream nil))
      (insert line "\n"))))
;;=> nil
amuramatsu commented 8 years ago

xyzzyの実装は正しくて、コネクション自体が終わっていない(HTTP 1.1はデフォルトでkeep-aliveになっているから)だけではないでしょうか。 (yahoo はkeep-aliveを実装していない?)

(format stream "GET / HTTP/1.1\n")(format stream "GET / HTTP/1.0\n")にするか、GETの後に (format stream "Connection: close\n") を付ければ、リクエスト毎にコネクションが切断されるようになって、所望の結果が得られるのではないかと思います。

kosh04 commented 8 years ago

確かにコネクションを切断するように指定すれば上のコードはうまく動作するみたいですね。ありがとうございます。

ただ、サイトによっては実行中に別の問題が発生するみたいです。 例えば httpbin.org では

(with-open-stream (stream (connect "httpbin.org" "https" :ssl t))
  (format stream "GET /ip HTTP/1.1\n")
  (format stream "Host: httpbin.org\n")
  (format stream "Connection: close\n")
  (format stream "\n")
  (let (line)
    (while (setq line (read-line stream nil))
      (insert line "\n"))))
;;-> 通信内容が出力される
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 14 Jan 2016 18:49:00 GMT
Content-Type: application/json
Content-Length: 31
Connection: close
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "origin": "127.0.0.1"
}
;; エラーメッセージ: Disconnect: 指定されたハンドルが無効です 

*Trace Output* バッファを見ると「DecryptMessage (context expired): このコンテキストは、期限が切れているので使用できません。 」と出力されていて、read-line 関数の読み込み途中にSSLソケットストリームが無効になる(?)ようなエラーが発生してしまうため、解決したとは言いがたい状態です…。

amuramatsu commented 8 years ago

どうも、src/sockssl.cc 内の sockssl::decrypt_data() で、DecryptMessage()が返すSEC_I_CONTEXT_EXPIREDをエラーとして扱っているのが原因のようです。 他のソフトをいくつか見てみましたが、無視してセッションクローズしてしまうのが正しい?

件のサイトでもエラーが出ないようなパッチを作ってみましたが、Windowsはよく分かっていないのでこれでいいのかどうか。

https://gist.github.com/amuramatsu/8c3b63b05d8a23a70741

kosh04 commented 8 years ago

パッチを当てて再ビルドしてみたら通りました :+1:

改善の余地はありそうですが、一応このパッチはPull Requestとして投げてみてはどうでしょうか。