Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Side by Side Diff: appengine/ephelper/epfrontend/error.go

Issue 1750143003: Remove ephelper and other endpoints code. (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/ephelper/epfrontend/doc.go ('k') | appengine/ephelper/epfrontend/error_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package epfrontend
6
7 import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "net/http"
12
13 "github.com/GoogleCloudPlatform/go-endpoints/endpoints"
14 )
15
16 type outerError struct {
17 Error *innerError `json:"error"`
18 }
19
20 type innerError struct {
21 Code int `json:"code"`
22 Errors []*errorInstance `json:"errors"`
23 Message string `json:"message"`
24 }
25
26 type errorInstance struct {
27 Domain string `json:"domain"`
28 Message string `json:"message"`
29 Reason string `json:"reason"`
30 }
31
32 // endpointsErrorResponse is a copy of go-endpoints' errorResponse struct.
33 type endpointsErrorResponse struct {
34 // Currently always "APPLICATION_ERROR"
35 State string `json:"state"`
36 Name string `json:"error_name"`
37 Msg string `json:"error_message,omitempty"`
38 Code int `json:"-"`
39 }
40
41 // errorResponseWriter is an http.ResponseWriter implementation that captures
42 // endpoints errors that are written to it and emits them as error JSON.
43 //
44 // This fulfills the purpose of collecting an error response, generated either
45 // by the frontend processing code (e.g., could not find method) or the backend
46 // handling code (e.g., endpoint returned an error, JSON unmarshal, etc.) and
47 // boxing it into a frontend error response structure.
48 //
49 // If the frontend handler encounters an error, it will be set via setError.
50 //
51 // If the backend code generates an error, it will be captured as follows:
52 // 1) The backend will invoke WriteHeader with an error status. That will enable
53 // response buffering.
54 // 2) Write will be called to write out the backend error response JSON. This
55 // will be captured for deconstruction.
56 //
57 // In either case, the error state will be forwarded to the real underlying
58 // ResponseWriter via forwardError. If no error is assigned, all operations
59 // essentially pass through to the underlying ResponseWriter, making this very
60 // low overhead in the standard case.
61 type errorResponseWriter struct {
62 http.ResponseWriter
63
64 status int
65 inst *errorInstance
66 buf bytes.Buffer
67 }
68
69 func (w *errorResponseWriter) WriteHeader(status int) {
70 if w.status != 0 {
71 return
72 }
73
74 // If we have an error status (>= 400)
75 w.status = status
76 if w.status >= http.StatusBadRequest {
77 w.status, w.inst = w.translateReason(status)
78 }
79 w.ResponseWriter.WriteHeader(w.status)
80 }
81
82 func (w *errorResponseWriter) Write(d []byte) (int, error) {
83 // If we have an error status.
84 if w.inst != nil {
85 return w.buf.Write(d)
86 }
87 return w.ResponseWriter.Write(d)
88 }
89
90 // setError explicitly configures an error response.
91 //
92 // This is used by the frontend ServeHTTP code when an error is encountered
93 // prior to calling into the backend. If the backend is invoked, setError will
94 // not be called.
95 func (w *errorResponseWriter) setError(err error) {
96 e, ok := err.(*endpoints.APIError)
97 if !ok {
98 e = &endpoints.APIError{
99 Msg: err.Error(),
100 Code: http.StatusInternalServerError,
101 }
102 }
103 w.WriteHeader(e.Code)
104 w.inst.Message = err.Error()
105 }
106
107 func (w *errorResponseWriter) forwardError() bool {
108 if w.inst == nil {
109 // No buffered error; leave things be.
110 return false
111 }
112
113 ierr := innerError{
114 Code: w.status,
115 Message: "unspecified error",
116 Errors: []*errorInstance{w.inst},
117 }
118 if w.inst.Message == "" {
119 // Attempt to load the message from the backend endpoints error JSON.
120 if w.buf.Len() > 0 {
121 resp := endpointsErrorResponse{}
122 if err := json.NewDecoder(&w.buf).Decode(&resp); err != nil {
123 w.inst.Message = fmt.Sprintf("Failed to decode e rror JSON (%s): %s", err, w.buf.String())
124 } else {
125 w.inst.Message = resp.Msg
126 ierr.Message = resp.Msg
127 }
128 }
129 }
130
131 feErr := outerError{
132 Error: &ierr,
133 }
134 data, err := json.MarshalIndent(feErr, "", " ")
135 if err != nil {
136 return true
137 }
138 w.ResponseWriter.Write(data)
139 return true
140 }
141
142 // translateReason returns an errorInstance populated from an HTTP status.
143 //
144 // The mappings here are copied from Python AppEngine:
145 // /google/appengine/tools/devappserver2/endpoints/generated_error_info.py
146 func (*errorResponseWriter) translateReason(status int) (int, *errorInstance) {
147 code := status
148 r := errorInstance{
149 Domain: "global",
150 }
151 switch status {
152 case 400:
153 r.Reason = "badRequest"
154 case 401:
155 r.Reason = "required"
156 case 402:
157 r.Reason = "unsupportedProtocol"
158 case 403:
159 r.Reason = "forbidden"
160 case 404:
161 r.Reason = "notFound"
162 case 405:
163 r.Reason = "unsupportedMethod"
164 case 409:
165 r.Reason = "conflict"
166 case 410:
167 r.Reason = "deleted"
168 case 412:
169 r.Reason = "conditionNotMet"
170 case 413:
171 r.Reason = "uploadTooLarge"
172
173 case 406, 407, 411, 414, 415, 416, 417:
174 code = 404
175 r.Reason = "unsupportedProtocol"
176
177 case 408:
178 fallthrough
179 default:
180 code = 503
181 r.Reason = "backendError"
182 }
183 return code, &r
184 }
OLDNEW
« no previous file with comments | « appengine/ephelper/epfrontend/doc.go ('k') | appengine/ephelper/epfrontend/error_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698