github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/http/filetransport.go (about) 1 // Copyright 2011 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 package http 6 7 import ( 8 "fmt" 9 "io" 10 "io/fs" 11 "net/http" 12 ) 13 14 // fileTransport implements RoundTripper for the 'file' protocol. 15 type fileTransport struct { 16 fh fileHandler 17 } 18 19 // NewFileTransport returns a new [RoundTripper], serving the provided 20 // [FileSystem]. The returned RoundTripper ignores the URL host in its 21 // incoming requests, as well as most other properties of the 22 // request. 23 // 24 // The typical use case for NewFileTransport is to register the "file" 25 // protocol with a [Transport], as in: 26 // 27 // t := &http.Transport{} 28 // t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) 29 // c := &http.Client{Transport: t} 30 // res, err := c.Get("file:///etc/passwd") 31 // ... 32 func NewFileTransport(fs FileSystem) RoundTripper { 33 return fileTransport{fileHandler{fs}} 34 } 35 36 // NewFileTransportFS returns a new [RoundTripper], serving the provided 37 // file system fsys. The returned RoundTripper ignores the URL host in its 38 // incoming requests, as well as most other properties of the 39 // request. 40 // 41 // The typical use case for NewFileTransportFS is to register the "file" 42 // protocol with a [Transport], as in: 43 // 44 // fsys := os.DirFS("/") 45 // t := &http.Transport{} 46 // t.RegisterProtocol("file", http.NewFileTransportFS(fsys)) 47 // c := &http.Client{Transport: t} 48 // res, err := c.Get("file:///etc/passwd") 49 // ... 50 func NewFileTransportFS(fsys fs.FS) RoundTripper { 51 return NewFileTransport(FS(fsys)) 52 } 53 54 func (t fileTransport) RoundTrip(req *Request) (resp *Response, err error) { 55 // We start ServeHTTP in a goroutine, which may take a long 56 // time if the file is large. The newPopulateResponseWriter 57 // call returns a channel which either ServeHTTP or finish() 58 // sends our *Response on, once the *Response itself has been 59 // populated (even if the body itself is still being 60 // written to the res.Body, a pipe) 61 rw, resc := newPopulateResponseWriter() 62 go func() { 63 t.fh.ServeHTTP(rw, req.toHttp()) 64 rw.finish() 65 }() 66 return <-resc, nil 67 } 68 69 func newPopulateResponseWriter() (*populateResponse, <-chan *Response) { 70 pr, pw := io.Pipe() 71 rw := &populateResponse{ 72 ch: make(chan *Response), 73 pw: pw, 74 res: &Response{ 75 Proto: "HTTP/1.0", 76 ProtoMajor: 1, 77 Header: make(Header), 78 Close: true, 79 Body: pr, 80 }, 81 } 82 return rw, rw.ch 83 } 84 85 // populateResponse is a ResponseWriter that populates the *Response 86 // in res, and writes its body to a pipe connected to the response 87 // body. Once writes begin or finish() is called, the response is sent 88 // on ch. 89 type populateResponse struct { 90 res *Response 91 ch chan *Response 92 wroteHeader bool 93 hasContent bool 94 sentResponse bool 95 pw *io.PipeWriter 96 } 97 98 func (pr *populateResponse) finish() { 99 if !pr.wroteHeader { 100 pr.WriteHeader(500) 101 } 102 if !pr.sentResponse { 103 pr.sendResponse() 104 } 105 pr.pw.Close() 106 } 107 108 func (pr *populateResponse) sendResponse() { 109 if pr.sentResponse { 110 return 111 } 112 pr.sentResponse = true 113 114 if pr.hasContent { 115 pr.res.ContentLength = -1 116 } 117 pr.ch <- pr.res 118 } 119 120 func (pr *populateResponse) Header() http.Header { 121 return pr.res.Header.toHttp() 122 } 123 124 func (pr *populateResponse) WriteHeader(code int) { 125 if pr.wroteHeader { 126 return 127 } 128 pr.wroteHeader = true 129 130 pr.res.StatusCode = code 131 pr.res.Status = fmt.Sprintf("%d %s", code, StatusText(code)) 132 } 133 134 func (pr *populateResponse) Write(p []byte) (n int, err error) { 135 if !pr.wroteHeader { 136 pr.WriteHeader(StatusOK) 137 } 138 pr.hasContent = true 139 if !pr.sentResponse { 140 pr.sendResponse() 141 } 142 return pr.pw.Write(p) 143 }