github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/resource/api/private/server/handler.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package server
     5  
     6  // TODO(ericsnow) Eliminate the apiserver dependencies, if possible.
     7  
     8  import (
     9  	"io"
    10  	"net/http"
    11  
    12  	"github.com/juju/errors"
    13  
    14  	"github.com/juju/juju/resource"
    15  	"github.com/juju/juju/resource/api"
    16  )
    17  
    18  // TODO(ericsnow) Define the HTTPHandlerConstraints here? Perhaps
    19  // even the HTTPHandlerSpec?
    20  
    21  // LegacyHTTPHandler is the HTTP handler for the resources
    22  // endpoint. We use it rather having a separate handler for each HTTP
    23  // method since registered API handlers must handle *all* HTTP methods
    24  // currently.
    25  type LegacyHTTPHandler struct {
    26  	LegacyHTTPHandlerDeps
    27  }
    28  
    29  // NewLegacyHTTPHandler creates a new http.Handler for the resources endpoint.
    30  func NewLegacyHTTPHandler(deps LegacyHTTPHandlerDeps) *LegacyHTTPHandler {
    31  	return &LegacyHTTPHandler{
    32  		LegacyHTTPHandlerDeps: deps,
    33  	}
    34  }
    35  
    36  // ServeHTTP implements http.Handler.
    37  func (h *LegacyHTTPHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
    38  	opener, err := h.NewResourceOpener(req)
    39  	if err != nil {
    40  		h.SendHTTPError(resp, err)
    41  		return
    42  	}
    43  
    44  	// We do this *after* authorization, etc. (in h.Extract...) in order
    45  	// to prioritize errors that may originate there.
    46  	switch req.Method {
    47  	case "GET":
    48  		logger.Infof("handling resource download request")
    49  
    50  		opened, err := h.HandleDownload(opener, req)
    51  		if err != nil {
    52  			logger.Errorf("cannot fetch resource reader: %v", err)
    53  			h.SendHTTPError(resp, err)
    54  			return
    55  		}
    56  		defer opened.Close()
    57  
    58  		h.UpdateDownloadResponse(resp, opened.Resource)
    59  
    60  		resp.WriteHeader(http.StatusOK)
    61  		if err := h.Copy(resp, opened); err != nil {
    62  			// We cannot use api.SendHTTPError here, so we log the error
    63  			// and move on.
    64  			logger.Errorf("unable to complete stream for resource: %v", err)
    65  			return
    66  		}
    67  
    68  		logger.Infof("resource download request successful")
    69  	default:
    70  		h.SendHTTPError(resp, errors.MethodNotAllowedf("unsupported method: %q", req.Method))
    71  	}
    72  }
    73  
    74  // LegacyHTTPHandlerDeps exposes the external dependencies
    75  // of LegacyHTTPHandler.
    76  type LegacyHTTPHandlerDeps interface {
    77  	baseLegacyHTTPHandlerDeps
    78  	ExtraDeps
    79  }
    80  
    81  //ExtraDeps exposes the non-superficial dependencies of LegacyHTTPHandler.
    82  type ExtraDeps interface {
    83  	// NewResourceOpener returns a new opener for the request.
    84  	NewResourceOpener(*http.Request) (resource.Opener, error)
    85  }
    86  
    87  type baseLegacyHTTPHandlerDeps interface {
    88  	// UpdateDownloadResponse updates the HTTP response with the info
    89  	// from the resource.
    90  	UpdateDownloadResponse(http.ResponseWriter, resource.Resource)
    91  
    92  	// SendHTTPError wraps the error in an API error and writes it to the response.
    93  	SendHTTPError(http.ResponseWriter, error)
    94  
    95  	// HandleDownload provides the download functionality.
    96  	HandleDownload(resource.Opener, *http.Request) (resource.Opened, error)
    97  
    98  	// Copy implements the functionality of io.Copy().
    99  	Copy(io.Writer, io.Reader) error
   100  }
   101  
   102  // NewLegacyHTTPHandlerDeps returns an implementation of LegacyHTTPHandlerDeps.
   103  func NewLegacyHTTPHandlerDeps(extraDeps ExtraDeps) LegacyHTTPHandlerDeps {
   104  	return &legacyHTTPHandlerDeps{
   105  		ExtraDeps: extraDeps,
   106  	}
   107  }
   108  
   109  // legacyHTTPHandlerDeps is a partial implementation of LegacyHandlerDeps.
   110  type legacyHTTPHandlerDeps struct {
   111  	ExtraDeps
   112  }
   113  
   114  // SendHTTPError implements LegacyHTTPHandlerDeps.
   115  func (deps legacyHTTPHandlerDeps) SendHTTPError(resp http.ResponseWriter, err error) {
   116  	api.SendHTTPError(resp, err)
   117  }
   118  
   119  // UpdateDownloadResponse implements LegacyHTTPHandlerDeps.
   120  func (deps legacyHTTPHandlerDeps) UpdateDownloadResponse(resp http.ResponseWriter, info resource.Resource) {
   121  	api.UpdateDownloadResponse(resp, info)
   122  }
   123  
   124  // HandleDownload implements LegacyHTTPHandlerDeps.
   125  func (deps legacyHTTPHandlerDeps) HandleDownload(opener resource.Opener, req *http.Request) (resource.Opened, error) {
   126  	name := api.ExtractDownloadRequest(req)
   127  	return opener.OpenResource(name)
   128  }
   129  
   130  // Copy implements LegacyHTTPHandlerDeps.
   131  func (deps legacyHTTPHandlerDeps) Copy(w io.Writer, r io.Reader) error {
   132  	_, err := io.Copy(w, r)
   133  	return err
   134  }