import "net/textproto"

HTTP、NNTP、SMTPといったテキストベースのリクエスト/レスポンスプロトコルへの包括的なサポートを実装しています。

このパッケージでは、次の機能を備えています:

  • Error。サーバからのエラーレスポンス値。
  • Pipeline。クライアントにおける、パイプライン化されたリクエストおよびレスポンスの管理。
  • Reader。レスポンスコード行の読み込み、キー:値形式のヘッダの読み込み、連続行を行頭のスペースにより一行として扱う行読み込み、ドット行で終わるテキストブロックの全体の読み込み。
  • Writer。ドットエンコードされたテキストブロックの書き込み。

パッケージファイル

pipeline.go reader.go textproto.go writer.go

CanonicalHeaderKey関数

func CanonicalHeaderKey(s string) string

CanonicalHeaderKeyは、MIMEヘッダキーsを正規フォーマットで返します。この正規化は、最初の文字とハイフン直後の文字を大文字に変換し、それ以外の文字を小文字に変換します。例えば、「accept-encoding”」の正規化したキーは、「Accept-Encoding」です。

Conn型

Connは、テキスト形式のネットワークプロトコル接続を表します。Connは、I/O管理を行うReaderとWriter、およびこの接続上の並列リクエストを順次処理するためのPipelineから構成されます。これら埋め込まれた型によりメソッドが提供されます。詳細は、それらの型のドキュメントを参照ください。

type Conn struct {
    Reader
    Writer
    Pipeline
    // contains unexported fields
}

Dial関数

func Dial(network, addr string) (*Conn, os.Error)

Dialは、指定したネットワーク上の指定したアドレスにnet.Dialを使用して接続し、その接続を持つ新しいConnを返します。

NewConn関数

func NewConn(conn io.ReadWriteCloser) *Conn

NewConnは、入出力にconnを使用する、新しいConnを返します。

(*Conn) Close関数

func (c *Conn) Close() os.Error

Closeは、接続をクローズします。

(*Conn) Cmd関数

func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err os.Error)

Cmdは、パイプラインに自分の順番が来るのを待った後、コマンドを送信するためのコンビニエンスメソッドです。送信されるコマンドは、formatをargsでフォーマットしたテキストで、後ろに\r\nが付加されます。Cmdは、StartResponseおよびEndResponseで使用するコマンドのidを返します。

下の例は、ドットエンコードされたボディを返すHELPコマンドをクライアント側で実行しています。

id, err := c.Cmd("HELP")
if err != nil {
	return nil, err
}

c.StartResponse(id)
defer c.EndResponse(id)

if _, _, err = c.ReadCodeLine(110); err != nil {
	return nil, err
}
text, err := c.ReadDotAll()
if err != nil {
	return nil, err
}
return c.ReadCodeLine(250)

Error型

Errorは、サーバから返される、エラーレスポンス値を表します。

type Error struct {
    Code int
    Msg  string
}

(*Error) String関数

func (e *Error) String() string

Pipeline型

Pipelineは、リクエスト/レスポンスのシーケンスの順番をパイプラインで管理します。

Pipeline pを使用して、同一接続上の複数のクライアントを管理するためには、各クライアントは、次のように処理を行う必要があります。

id := p.Next()	// 番号を得る

p.StartRequest(id)	// リクエストの送信の順番待ち
«send request»
p.EndRequest(id)	// リクエストが送信されたことをPipelineに通知

p.StartResponse(id)	// レスポンスの読み込みの順番待ち
«read response»
p.EndResponse(id)	// レスポンスが読み込まれたことをPipelineに通知

パイプライン化されたサーバは、並列処理で算出したレスポンスが、確実に正しい順番に書き込まれるようにするために同様の呼び出しを行うことができます。

type Pipeline struct {
    // contains unexported fields
}

(*Pipeline) EndRequest関数

func (p *Pipeline) EndRequest(id uint)

EndRequestは、このidのリクエストの送信(サーバの場合は受信)が完了したことをpに通知します。

(*Pipeline) EndResponse関数

func (p *Pipeline) EndResponse(id uint)

EndResponseは、このidのレスポンスの受信(サーバの場合は送信)が完了したことをpに通知します。

(*Pipeline) Next関数

func (p *Pipeline) Next() uint

Nextは、リクエスト/レスポンスの組に使う、次のidを返します。

(*Pipeline) StartRequest関数

func (p *Pipeline) StartRequest(id uint)

StartRequestは、指定したidのリクエストの送信(サーバの場合は受信)タイミングが来るまでブロックします。

(*Pipeline) StartResponse関数

func (p *Pipeline) StartResponse(id uint)

StartResponseは、指定したidのレスポンスの受信(サーバの場合は送信)タイミングが来るまでブロックします。

ProtocolError型

ProtocolErrorは、無効なレスポンスや接続断などのプロトコル違反を表します。

type ProtocolError string

(ProtocolError) String関数

func (p ProtocolError) String() string

Reader型

Readerは、テキストプロトコルネットワーク接続から、リクエストまたはレスポンスを読み込むためのコンビニエンスメソッドを実装しています。

type Reader struct {
    R *bufio.Reader
    // contains unexported fields
}

NewReader関数

func NewReader(r *bufio.Reader) *Reader

NewReaderは、rから読み込みを行う、新しいReaderを返します。

(*Reader) DotReader関数

func (r *Reader) DotReader() io.Reader

DotReaderは、ドットエンコードされたブロックのテキストをrから読み込んみ、デコードを行うReadメソッドを実装している新しいReaderを返します。返されるReaderが有効であるのは、r上のメソッドを次に呼び出すまでの間だけです。

ドットエンコーディングは、一般的なフレーミングで、SMTPのようなテキストプロトコルのデータブロックに使用されています。データは連続する行で構成され、各行は「\r\n」で終わります。この連続する行は、ドットだけを含んだ行(.\r\n)で終了します。ドットで始まる行は、この連続する行の終了であると見誤られるのを避けるために、さらにドットでエスケープされます。

ReaderのReadメソッドによって返されるデコードされた形式は、行末の「\r\n」を単に「\n」へと書き換え、行頭のドットエスケープがあれば、それを除去したものです。一連の最終行まで読み尽くした(最終行は破棄)あと、os.EOFエラーを返し、そこで終了します。

(*Reader) ReadCodeLine関数

func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.Error)

ReadCodeLineは、次の形式のレスポンスコード行を読み込みます。

code message

codeは、3桁のステータスコードで、残りの行末までがmessageです。下は、レスポンスコード行の例です。

220 plan9.bell-labs.com ESMTP

ステータスの先頭の数字が、expectCodeで指定した値と一致しないときは、ReadCodeLineは、&Error{code, message}をセットしたerrを返します。例えば、expectCodeが31のとき、ステータスの値が[310,319]の範囲内になければ、エラーが返されます。

レスポンスが複数行にわたるとき、ReadCodeLineはエラーを返します。

expectCode <= 0とすると、ステータスコードのチェックは無効になります。

(*Reader) ReadContinuedLine関数

func (r *Reader) ReadContinuedLine() (string, os.Error)

ReadContinuedLineは、rから一連の行を読み込み、行末のASCIIのホワイトスペースを除去します。先頭以降の行が、ホワイトスペースまたはタブ文字で始まるときは、前の行から継続しているとみなします。返されるデータ内の継続行は、その前の行とスペースひとつだけで区切られ、改行と先頭のホワイトスペースは除去されます。

例として、次のような入力があると仮定します。

Line 1
  continued...
Line 2

最初のReadContinuedLineの呼び出しでは、「Line 1 continued…」が返り、2番目の呼び出しでは、「Line 2」が返ります。

ホワイトスペースだけしか含まない行が継続されることはありません。

(*Reader) ReadContinuedLineBytes関数

func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error)

ReadContinuedLineBytesは、ReadContinuedLineと同じですが、文字列の代わりに[]byteを返します。

(*Reader) ReadDotBytes関数

func (r *Reader) ReadDotBytes() ([]byte, os.Error)

ReadDotBytesは、ドットエンコーディングを読み込み、デコードしたデータを返します。

ドットエンコーディングについての詳細は、DotReaderメソッドのドキュメントを参照ください。

(*Reader) ReadDotLines関数

func (r *Reader) ReadDotLines() ([]string, os.Error)

ReadDotLinesは、ドットエンコーディングを読み込み、デコードした行を格納したスライスを返します。各行の終わりにある\r\nもしくは\nは取り除かれます。

ドットエンコーディングについての詳細は、DotReaderメソッドのドキュメントを参照ください。

(*Reader) ReadLine関数

func (r *Reader) ReadLine() (string, os.Error)

ReadLineは、一行読み込みを行い、返却する文字列の終わりにある\nもしくは\r\nを取り除きます。

(*Reader) ReadLineBytes関数

func (r *Reader) ReadLineBytes() ([]byte, os.Error)

ReadLineBytesは、ReadLineと同じですが、文字列の代わりに[]byteを返します。

(*Reader) ReadMIMEHeader関数

func (r *Reader) ReadMIMEHeader() (map[string][]string, os.Error)

ReadMIMEHeaderは、rからMIME形式のヘッダを読み込みます。ヘッダは、「キー:値」形式の一連の行で、ブランク行で終了します。返されるマップmには、CanonicalHeaderKey(key)に対し、入力から得られたのと同じ順番で一連の値がマップされます。

例として、次のような入力があると仮定します。

My-Key: Value 1
Long-Key: Even
       Longer Value
My-Key: Value 2

この入力を与えたReadMIMEHeaderは、次のマップを返します。

map[string][]string{
	"My-Key": []string{"Value 1", "Value 2"},
	"Long-Key": []string{"Even Longer Value"},
}

(*Reader) ReadResponse関数

func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error)

ReadResponseは、次の形式の複数行のレスポンスを読み込みます。

code-message line 1
code-message line 2
...
code message line n

codeは、3桁のステータスコードです。各行は、同じコードでなければなりません。レスポンスは、コードとメッセージの間に、ハイフン(-)でなくスペースを使う行で終了します。レスポンス内の各行は、改行(\n)によって分割されます。

ステータスの先頭の 数字が、expectCodeで指定した値と一致しないときは、ReadResponseは、&Error{code, message}をセットしたerrを返します。例えば、expectCodeが31のとき、ステータスの値が[310,319]の範囲内になければ、エ ラーが返されます。

expectCode <= 0とすると、ステータスコードのチェックは無効になります。

Writer型

Writerは、テキストプロトコルのネットワーク接続へ、リクエストまたはレスポンスを書き込むためのコンビニエンスメソッドを実装しています。

type Writer struct {
    W *bufio.Writer
    // contains unexported fields
}

NewWriter関数

func NewWriter(w *bufio.Writer) *Writer

NewWriterは、wに書き込みを行う、新しいWriterを返します。

(*Writer) DotWriter関数

func (w *Writer) DotWriter() io.WriteCloser

DotWriterは、wにドットエンコーディングで書き込みができるWriterを返します。このWriterは、必要に応じて行頭にドットを挿入し、行末の\nは\r\nに変換し、DotWriterがクローズされたときは、最後に.\r\n行を加えるよう配慮します。呼び出し側は、次にwのメソッドを呼び出す前には、DotWriterをクローズしなければなりません。

ドットエンコーディングについての詳細は、ReaderのDotReaderメソッドのドキュメントを参照ください。

(*Writer) PrintfLine関数

func (w *Writer) PrintfLine(format string, args ...interface{}) os.Error

PrintfLineは、フォーマットした出力に\r\nを付加して書き込みます。

バグ

サービス不能攻撃にさらされることについて、使用者側でコントロールできるよう、接続から読み出されるバイト数の上限値をReaderに設定・リセットできるようにする必要があります。