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 5package http 6 7import ( 8 "fmt" 9 "io" 10) 11 12// fileTransport implements RoundTripper for the 'file' protocol. 13type 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// ... 30func NewFileTransport(fs FileSystem) RoundTripper { 31 return fileTransport{fileHandler{fs}} 32} 33 34func (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 49func 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. 69type populateResponse struct { 70 res *Response 71 ch chan *Response 72 wroteHeader bool 73 hasContent bool 74 sentResponse bool 75 pw *io.PipeWriter 76} 77 78func (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 88func (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 100func (pr *populateResponse) Header() Header { 101 return pr.res.Header 102} 103 104func (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 114func (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