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