]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/http/filetransport.go
94684b07a1395985102a302980f0f9c426f295b3
[gostls13.git] / src / net / http / filetransport.go
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 }