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