src/pkg/net/http/filetransport.go - The Go Programming Language

Golang

Source file src/pkg/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	}