github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/net/http/fs.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // HTTP file system request handler
     6  
     7  package http
     8  
     9  import (
    10  	"github.com/shogo82148/std/io"
    11  	"github.com/shogo82148/std/io/fs"
    12  	"github.com/shogo82148/std/time"
    13  )
    14  
    15  // Dirは、特定のディレクトリツリーに制限されたネイティブファイルシステムを使用して [FileSystem] を実装します。
    16  //
    17  // [FileSystem.Open] メソッドは'/'で区切られたパスを取りますが、Dirの文字列値はURLではなくネイティブファイルシステム上のディレクトリーパスであるため、[filepath.Separator] で区切られます。これは必ずしも'/'ではありません。
    18  //
    19  // Dirは、機密ファイルやディレクトリを公開する可能性があります。Dirは、ディレクトリツリーから外部を指すシンボリックリンクを追跡します。これは、ユーザーが任意のシンボリックリンクを作成できるディレクトリからサービスを提供する場合に特に危険です。Dirは、ピリオドで始まるファイルやディレクトリにもアクセスを許可します。これには、.gitのような機密ディレクトリや.htpasswdのような機密ファイルが含まれます。ピリオドで始まるファイルを除外するには、ファイル/ディレクトリをサーバーから削除するか、カスタムFileSystem実装を作成してください。
    20  //
    21  // 空のDirは"."として扱われます。
    22  type Dir string
    23  
    24  // Openは、[os.Open] を使用して、ディレクトリdにルートされ、相対的なファイルを読み取るために [FileSystem] を実装します。
    25  func (d Dir) Open(name string) (File, error)
    26  
    27  // FileSystemは、名前付きファイルのコレクションへのアクセスを実装します。
    28  // ファイルパスの要素は、ホストオペレーティングシステムの規約に関係なく、スラッシュ('/'、U+002F)で区切られます。
    29  // FileSystemを [Handler] に変換するには、[FileServer] 関数を参照してください。
    30  //
    31  // このインターフェースは、[fs.FS] インターフェースより前に存在しており、代わりに使用できます。
    32  // [FS] アダプター関数は、fs.FSをFileSystemに変換します。
    33  type FileSystem interface {
    34  	Open(name string) (File, error)
    35  }
    36  
    37  // [FileSystem] のOpenメソッドによって返され、[FileServer] 実装によって提供されるファイルです。
    38  //
    39  // メソッドは、 [*os.File] と同じ動作をする必要があります。
    40  type File interface {
    41  	io.Closer
    42  	io.Reader
    43  	io.Seeker
    44  	Readdir(count int) ([]fs.FileInfo, error)
    45  	Stat() (fs.FileInfo, error)
    46  }
    47  
    48  // ServeContentは、提供されたReadSeekerの内容を使用してリクエストに応答します。
    49  // ServeContentの [io.Copy] に対する主な利点は、Rangeリクエストを適切に処理し、
    50  // MIMEタイプを設定し、If-Match、If-Unmodified-Since、If-None-Match、
    51  // If-Modified-Since、およびIf-Rangeリクエストを処理することです。
    52  //
    53  // レスポンスのContent-Typeヘッダーが設定されていない場合、ServeContentは
    54  // 最初にnameのファイル拡張子からタイプを推測し、それが失敗した場合は、
    55  // コンテンツの最初のブロックを読み取り、それを [DetectContentType] に渡すようにフォールバックします。
    56  // それ以外の場合、nameは使用されません。特に、nameは空にでき、レスポンスで送信されることはありません。
    57  //
    58  // modtimeがゼロ時またはUnixエポックでない場合、ServeContentは応答のLast-Modifiedヘッダーに含めます。
    59  // リクエストにIf-Modified-Sinceヘッダーが含まれている場合、ServeContentはmodtimeを使用して、コンテンツを送信する必要があるかどうかを決定します。
    60  //
    61  // コンテンツのSeekメソッドは動作する必要があります。ServeContentは、コンテンツのサイズを決定するために、コンテンツの末尾にシークを使用します。
    62  //
    63  // 呼び出し元がRFC 7232、セクション2.3に従ってフォーマットされたwのETagヘッダーを設定している場合、ServeContentはそれを使用して、If-Match、If-None-Match、またはIf-Rangeを使用するリクエストを処理します。
    64  //
    65  // [*os.File] は [io.ReadSeeker] インターフェースを実装していることに注意してください。
    66  func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)
    67  
    68  // ServeFileは、指定された名前の
    69  // ファイルまたはディレクトリの内容でリクエストに応答します。
    70  //
    71  // 提供されたファイル名またはディレクトリ名が相対パスの場合、それは
    72  // 現在のディレクトリに対して相対的に解釈され、親ディレクトリに昇格することができます。
    73  // 提供された名前がユーザー入力から構築されている場合、ServeFileを呼び出す前に
    74  // サニタイズする必要があります。
    75  //
    76  // 予防措置として、ServeFileはr.URL.Pathに".."パス要素が含まれているリクエストを拒否します。
    77  // これは、r.URL.Pathをサニタイズせずに [filepath.Join] で安全でなく使用し、
    78  // その結果をname引数として使用する可能性のある呼び出し元に対する保護です。
    79  //
    80  // 別の特殊なケースとして、ServeFileはr.URL.Pathが
    81  // "/index.html"で終わる任意のリクエストを、最後の
    82  // "index.html"なしで同じパスにリダイレクトします。そのようなリダイレクトを避けるためには、
    83  // パスを変更するか、[ServeContent] を使用します。
    84  //
    85  // それらの2つの特殊なケースの外では、ServeFileは
    86  // r.URL.Pathを使用して提供するファイルやディレクトリを選択しません。
    87  // 名前引数で提供されたファイルやディレクトリのみが使用されます。
    88  func ServeFile(w ResponseWriter, r *Request, name string)
    89  
    90  // ServeFileFSは、ファイルシステムfsysから指定されたファイルまたはディレクトリの内容でリクエストに応答します。
    91  //
    92  // 提供されたファイルまたはディレクトリ名が相対パスの場合、現在のディレクトリを基準に解釈され、親ディレクトリに移動することができます。
    93  // 提供された名前がユーザー入力から構築された場合、[ServeFile] を呼び出す前にサニタイズする必要があります。
    94  //
    95  // 予防措置として、ServeFileはr.URL.Pathに".."パス要素が含まれているリクエストを拒否します。
    96  // これにより、r.URL.Pathに [filepath.Join] を安全に使用せずにサニタイズせずに使用し、そのfilepath.Joinの結果を名前引数として使用する可能性がある呼び出し元を保護します。
    97  //
    98  // もう1つの特別な場合として、ServeFileはr.URL.Pathが"/index.html"で終わるリクエストを、最後の"index.html"を除いた同じパスにリダイレクトします。
    99  // そのようなリダイレクトを回避するには、パスを変更するか、ServeContentを使用してください。
   100  //
   101  // これら2つの特別な場合以外では、ServeFileはファイルまたはディレクトリを選択するためにr.URL.Pathを使用しません。
   102  // 名前引数で提供されたファイルまたはディレクトリのみが使用されます。
   103  func ServeFileFS(w ResponseWriter, r *Request, fsys fs.FS, name string)
   104  
   105  // FSはfsysを [FileSystem] の実装に変換します。
   106  // これは [FileServer] と [NewFileTransport] で使用するためのものです。
   107  // fsysによって提供されるファイルは [io.Seeker] を実装しなければなりません。
   108  func FS(fsys fs.FS) FileSystem
   109  
   110  // FileServerは、ルートでルートされたファイルシステムの内容でHTTPリクエストを処理するハンドラーを返します。
   111  //
   112  // 特別な場合として、返されたファイルサーバーは、"/index.html"で終わるリクエストを、最後の"index.html"を除いた同じパスにリダイレクトします。
   113  //
   114  // オペレーティングシステムのファイルシステム実装を使用するには、[http.Dir] を使用してください。
   115  //
   116  //	http.Handle("/", http.FileServer(http.Dir("/tmp")))
   117  //
   118  // [fs.FS] の実装を使用するには、代わりに [http.FileServerFS] を使用します。
   119  func FileServer(root FileSystem) Handler
   120  
   121  // FileServerFSは、ファイルシステムfsysの内容でHTTPリクエストを処理するハンドラを返します。
   122  //
   123  // 特別なケースとして、返されたファイルサーバーは、"/index.html"で終わる任意のリクエストを、
   124  // 最後の"index.html"なしの同じパスにリダイレクトします。
   125  //
   126  //	http.Handle("/", http.FileServerFS(fsys))
   127  func FileServerFS(root fs.FS) Handler