For Development HEAD DRAFTSearch (procedure/syntax/module):

12.46 rfc.http - HTTPクライアント

Module: rfc.http

このモジュールは、RFC2616 "Hypertext Transfer Protocol – HTTP/1.1" で定義されているHTTP/1.1に対する簡単なクライアントAPIを提供します。 (https://www.ietf.org/rfc/rfc2616.txt).

現在のAPIは、HTTP/1.1のプロトコルの一部のみ実装されています。 より先進的な機能、例えば永続的接続などは将来のバージョンで追加されるでしょう。

HTTPサーバを書くためのライブラリを探しているなら、 Gauche-makikiを見てください (https://github.com/shirok/Gauche-makiki)。

Condition Type: <http-error>

{rfc.http} サーバから接続が切られた場合や、サーバの返したHTTPレスポンスのフォーマットが 正しくない場合に投げられるコンディションです。<error>を継承します。


12.46.1 HTTPクライアント中間レベルAPI

Function: http-get server request-uri :key sink flusher redirect-handler secure …
Function: http-head server request-uri :key redirect-handler secure …
Function: http-post server request-uri body :key sink flusher redirect-handler secure …
Function: http-put server request-uri body :key sink flusher redirect-handler secure …
Function: http-delete server request-uri :key sink flusher redirect-handler secure …

{rfc.http} serverに、それぞれHTTPのGET、HEAD、POST、PUT、DELETEリクエストを送り、 サーバの応答を返します。

デフォルトでは、 サーバがステータスコード 300, 301, 302, 303, 305 の応答を返し、 RFC2616による自動リダイレクトが許されている場合は、 これらの手続きは自動的に応答のメッセージヘッダの "location" で返されるURIに対して リクエストを再送します。 この動作はredirect-handlerキーワード引数でカスタマイズしたり 抑制したりできます。下の"キーワード引数"を参照してください。

必須の引数: server引数では、文字列でHTTPサーバ名を指定します。 サーバ名は、オプションでコロンに続いてポート番号を付加できます。 IPアドレスも使えます。IPv6アドレスは角括弧で囲んでください。

また、"unix:/path"という形式でUnixドメインソケットに接続することもできます。 /path部分にはソケットへの絶対パスを指定します。 Examples: "w3c.org", "mycompany.com:8080", "192.168.0.1:8000", "[::1]:8000"

request-uri引数は文字列かリストです。 文字列の場合、RFC2616で規定されているリクエストURIと解釈されます。 通常これはHTTP URLのパス部分です。 文字列はそのままサーバに渡されるので、呼び出し側で必要な 文字コード変換やurlエンコーディングを行う必要があります。

request-uriがリストの場合は、次の形式でなければなりません。

(path (name value) ...)

ここでpathはリクエストURIのパスコンポーネントまでを指定する 文字列です。与えられたnamevalueのalistから、 httpリクエスト手続きはHTML4で定められた application/x-www-form-urlencoded形式の クエリ文字列を構成し、pathにアペンドします。 例えば次のふたつのリクエストは同じ効果を持ちます。 二番目の呼び出しではurlエスケープが自動的に行われることに注目してください。

(http-get "example.com" "/search?q=foo%20bar&n=20")

(http-get "example.com" '("/search" (q "foo bar") (n 20)))

request-encodingキーワード引数が与えられた場合、 namevalueはまずその文字エンコーディングに変換されたのちに urlエスケープされます。そうでない場合はgaucheの内部 エンコーディングがそのまま使われます。

いくつかの手続きは、リクエストメッセージのボディを指定するbodyを第3引数として 取ります。bodyは文字列かリストで、文字列の場合はそのまま送られ、 リストの場合はmultipart/form-data形式にエンコードされて送られます。

bodyがリストの場合、それはパラメータ指定のリストです。 各パラメータ指定は、("submit" "OK")のような名前と値のリスト、 もしくは("upload" :file "logo.png" :content-type "image/png") のように名前の後にキーワード-値リストを付加したものです。

最初の形式は使うのが簡単で、またrequest-uriのクエリパラメータリストと 同じ形式なのでGETとPOSTでルーチンを共有したい場合にも便利でしょう。 この形式では、各値はMIMEパートにtext/plainとして置かれます。 文字コードは下に述べるrequest-encodingキーワード引数により変換されます。

二番目の形式では、MIMEパートの属性についてより細かな指定を行うことができます。 以下のキーワードが特別に扱われます。

:value

パラメータの値を指定します。簡潔な(name val)形式は (name :value val)の省略形です。

:file

指定された名前のファイルの中身をパラメータの値として挿入します。 ファイルのアップロードに便利です。このオプションは:valueより 優先されます。MIMEタイプは、指定が無ければ application/octet-streamとなります。

:content-type

MIMEタイプをオーバライドします。与えられた値にcharsetパラメータが ついていない場合は自動的に付加されます。

:content-transfer-encoding

content-transfer-encodingを 7bitbinaryquoted-printablebase64の いずれかで指定します。指定が無ければbinaryが使われます。

残りのキーワードはMIMEパートのヘッダにそのまま使われます。

戻り値: 全ての手続きは3つの値を返します。

1つ目は、RFC2616で定義されているステータスコードの文字列値(例えば、成功時の 200、"Not found"の404など)です。

2つ目は、パーズされたヘッダのリストで、リストの要素は(header-name value …)です。header-nameはヘッダの文字列名(例えば、 "content-type"や"location"など)で、valueは対応する値の文字列値です。 ヘッダ名は小文字に変換されます。値は、RFC2822で定義されている無指定行区切 (ソフト・ライン・ブレイク)が除かれる以外はそのままです。 サーバが同じ名前のヘッダを1つ以上返した場合は、 1つのリストに統合されます。それ以外では、2つ目の戻り値に おけるヘッダのリストの順番は、サーバの応答での順番と同じです。

3つ目の戻り値は、サーバの応答におけるメッセージボディです。 デフォルトでは、文字列で表現されたメッセージボディそのものです。 サーバの応答がボディを持たない場合、3つ目の戻り値は#fです。 キーワード引数によって、メッセージボディがどのように扱われるかを制御できます。 例えば、中間的な文字列を作らずに、返されたメッセージボディを直接ファイルに 格納することが出来ます。詳細は以下で説明しています。

キーワード引数: デフォルトで、これらの手続きはリクエストメッセージに"Host"ヘッダ・フィールドを 追加するだけです。他のヘッダ・フィールドを追加するためにキーワード引数を 与えることができます。

(http-get "foo.bar.com" "/index.html"
  :accept-language "ja"
  :user-agent "My Scheme Program/1.0")

以下のキーワード引数は手続きによって解釈され、リクエストヘッダには現れません。

request-encoding

request-uribodyがリストで与えられた場合、パラメータの 名前や値はまずこの引数で指定される文字エンコーディングへと変換され、 その後、application/x-www-form-urlencodedmultipart/form-data MIME形式にしたがったエンコーディングが行われます。 この引数が省略された場合はGaucheの内部文字エンコーディングが使われます。

multipart/form-dataについては、パラメータにcontent-typeヘッダを 与えることでパラメータごとに文字エンコーディングの設定をオーバライドできます。 詳しくは上のbody引数の説明を参照してください。

request-uribodyに文字列を与えた場合は、文字エンコーディング変換は 行われません。呼び出し側で望みの文字コードにあらかじめ変換しておいてください。

proxy

httpプロキシサーバを、hostnameまたはhostname:port形式の 文字列で指定します。省略された場合、パラメータhttp-proxyの値が 使われます。

redirect-handler

サーバが3xxステータスコードを返した場合のリダイレクトの処理を指定します。 #f, #tもしくは手続きを渡すことができます。省略時は#tとなります。

#fが渡された場合は、リダイレクトは処理されません。3xxステータスコードを 持つレスポンスもそのままhttp-*から返されます。

手続きが渡された場合は、サーバが3xxステータスコードを返すとその手続きが4つの引数で 呼ばれます。最初の引数はリクエストメソッド(シンボル、例:GET)、 次がレスポンスステータスコード(文字列、例:"302")、 次がパーズされたレスポンスヘッダ、そして最後がレスポンスボディです(レスポンスボディが あれば文字列、なければ#f)。

この手続きは、ペアか#fを返さねばなりません。ペアの場合、それは (method . url)という形で、methodがシンボルによるリクエストメソッド、 urlが文字列で次にリクエストすべきURLを表します。 ペアが返されれば、http-*手続きはそのURLへ、指定されたメソッドでリクエストを 再送します。(メソッドを返すことで、例えばPOSTリクエストのリダイレクトをGETリクエストに 置き換えることが可能です)。 手続きが#fを返した場合、リダイレクトは行われません。

redirect-handler#t(デフォルト値)の場合、 パラメータhttp-default-redirect-handlerの値が redirect-handlerに渡されたかのように振る舞います。 このパラメータの初期値は、標準的なリダイレクトの振る舞いをする手続きになっています。 下のhttp-default-redirect-handlerの項目を参照してください。

リダイレクトのループは自動的に検出され、<http-error>が投げられます。

no-redirect

これは互換性のためだけに残されている、古い引数です。真の値を与えると、 redirect-handler#fを渡したのと同じ効果を持ちます。

secure

真の値が与えられた場合、セキュアな接続が使われます。 値によってhttps接続につかうトランスポートエージェントを指定できます。 有効な値は#tもしくはシンボルのtlsstunnelです。 #fが与えられた場合(デフォルト)はセキュアでない通常のhttpが使われます。 詳しくは下の「セキュアな接続」の項を参照してください。

auth-user, auth-password

これらのキーワード引数が与えられた場合、Basic認証用のAuthorizationヘッダが リクエストに付加されます。将来はBasic認証以外の認証方式もサポートするかもしれません。

sink, flusher

これらのキーワード引数によりリプライメッセージ・ボディがどのように扱われるかを カスタマイズできます。sinkには出力ポートを、flusherには2引数を 取る手続きを渡さなければなりません。

手続きがメッセージ・ボディを受信し始めると、sinkへ受け取った データ片をフィードします。手続きがメッセージ・ボディを受信し終わると、 flusherに与えられた手続きが、sinkと(手続きからの2つ目の 戻り値と同じフォーマットの)メッセージ・ヘッダ・フィールドのリストとともに 呼び出されます。flusherの戻り値が、手続きからの3つ目の戻り値と なります。

したがって、sinkのデフォルト値は、新しく開かれた文字列ポートで、 flusherのデフォルト値は(lambda (sink headers) (get-output-string sink)) とも言えます。

以下のサンプルは、(とても大きい可能性のある)文字列バッファを作らずに、 メッセージ・ボディを直接ファイルに保存します。

(call-with-output-file "page.html"
  (lambda (out)
    (http-get "www.schemers.org" "/"
       :sink out :flusher (lambda _ #t))))

12.46.2 HTTPクライアントユーティリティ

The module also provides some utility procedures.

Parameter: http-user-agent :optional value

{rfc.http} user-agentヘッダに渡される値のデフォルト値を指定するパラメータです。 デフォルトの値はgauche.http/* (*部分はGaucheのバージョン) になっています。 各アプリケーションは適切な値を設定するようにしてください。

Parameter: http-proxy :optional value

{rfc.http} このパラメータの値がhttp-get等のhttpプロキシのデフォルトの値として 使われます。デフォルトの値は#f (プロキシを使用しない) です。

Parameter: http-default-redirect-handler :optional value

{rfc.http} http-*手続きにredirect-handlerキーワード引数が与えられなかった 場合のデフォルトの動作を指定します。この値を変える場合、それは redirect-handler引数のプロトコルに従う手続きでなければなりません。 上のhttp-*手続きの項目を参照してください。

デフォルトの動作は以下の通りです。

300, 301, 305, 307

元のリクエストがGETHEADの場合に限り、同じリクエストを使って locationヘッダに与えられたURLにリダイレクトします。

302

locationヘッダに与えられたURLにリダイレクトします。 元がHEADリクエストならHEADを、それ以外ならGETリクエストを 使います。

厳密に言えばこれはRFC2616違反ですが、RFC2616の注記にもあるように、 多くのユーザエージェントがこの振る舞いをするので、それに合わせてあります。 (将来は変えるかもしれません。)

303

locationヘッダに与えられたURLにリダイレクトします。 元がHEADリクエストならHEADを、それ以外ならGETリクエストを 使います。

other than above

リダイレクトしません。

次のコードは、デフォルトの振る舞いを特定のリクエストでインターセプトする例です。

(http-get server uri
  :redirect-handler
  (^[method status headers body]
    (if (and (equal? status "302")
             (not (member method '(GET HEAD))))
        #f
        ((http-default-request-handler) method status headers body))))
Function: http-compose-query path params :optional encoding

{rfc.http} クエリパラメータのリストからリクエストURIを生成する補助関数です。 encoding引数はクエリパラメータの文字エンコーディングを指定します。

(http-compose-query "/search" '((q "$foo") (n 20)))
 ⇒ "/search?q=%24foo&n=20"

(http-compose-query "" '((x "a b") (x 2)))
 ⇒ "?x=a%20b&x=2"

path#fの場合は、クエリパラメータの部分だけが返されます (次の例と直前の例を比べてみてください)。

(http-compose-query #f '((x "a b") (x 2)))
 ⇒ "x=a%20b&x=2"

これはrfc.uriモジュールのuri-compose-query手続きの上に 作られています (rfc.uri - URIの解析と作成参照)。

Function: http-compose-form-data params port :optional encoding

{rfc.http} multibyte/form-data形式にエンコードされたデータを パラメータのリストから組み立てるための補助手続きです。 paramsの形式はhttpリクエスト手続きのbody部にリストを渡す場合の形式と同様です。 結果は出力ポートportに書き出され、 MIMEメッセージを構築するのに必要なboundary stringが返されます。 port#fを渡した場合は、 boundary stringとデータをエンコードした文字列の二つの値が返されます。

encodingは文字エンコーディングを指定します。 省略時はGaucheのネイティブエンコーディングが使われます。

(define p (open-output-string))

(http-compose-form-data '((name "Preludes and Fugues")
                          (composer "Shostakovich, Dmitri")
                          (opus "87"))
                         p)
  ⇒ "boundary-fh87o52rp6zkubp2uhdmo"

(get-output-string p)
  ⇒
  "\r\n--boundary-fh87o52rp6zkubp2uhdmo\r\nContent-type: te
   xt/plain; charset=utf-8\r\nContent-transfer-encoding: bi
   nary\r\ncontent-disposition: form-data; name=title\r\n\r\n
   Preludes and Fugues\r\n--boundary-fh87o52rp6zkubp2uhdmo...
;; (以下省略)
Function: http-status-code->description code

{rfc.http} HTTPステータスコードcodeの簡単な説明を返します。 codeは整数か、整数を表す文字列です(例: "404")。 codeが知られているものでなかった場合は#fが返されます。

(http-status-code->description 404)
  ⇒ "Not Found"

12.46.3 セキュアhttpコネクション

http-get等httpリクエスト発行APIのsecureキーワード引数に、 真の値を渡した場合、セキュアなコネクションが使われます。 すなわち、httpではなくhttpsで接続されるということです。 実際にsecureキーワード引数に渡せる値は以下の通りです。

#t
tls

rfc.tlsモジュールを使ってセキュアな接続を行います。 詳しくはrfc.tls - トランスポート・レイヤ・セキュリティを参照してください。 CA証明書のパスを設定する必要があるかもしれません。

stunnel

stunnelプロセスをサブプロセスとして起動してそれを通じてセキュアな接続を作ります。

#f

セキュアな接続を行いません。

指定されたセキュア接続を行うサブシステムが実行中のGaucheでは使えない場合は、 エラーが報告されます。 サブシステムが使えるかどうかをチェックしたい場合は次の手続きを使ってください。

Function: http-secure-connection-available? :optional type

{rfc.http} type引数はtlsstunnelでなければなりません。 省略時はtlsとみなされます。 指定のセキュア接続を行うサブシステムが実行中のGaucheで使える場合は#tが、 使えない場合は#fが返されます。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT