github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/usersession/agent/response.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package agent 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "net/http" 26 27 "github.com/snapcore/snapd/logger" 28 ) 29 30 // TODO: clean up unused code further after we have progressed enough 31 // to have a clear sense of what is untested and uneeded here 32 33 // ResponseType is the response type 34 type ResponseType string 35 36 // "there are three standard return types: Standard return value, 37 // Background operation, Error", each returning a JSON object with the 38 // following "type" field: 39 const ( 40 ResponseTypeSync ResponseType = "sync" 41 ResponseTypeAsync ResponseType = "async" 42 ResponseTypeError ResponseType = "error" 43 ) 44 45 // Response knows how to serve itself, and how to find itself 46 type Response interface { 47 ServeHTTP(w http.ResponseWriter, r *http.Request) 48 } 49 50 type resp struct { 51 Status int // HTTP status code 52 Type ResponseType 53 Result interface{} 54 } 55 56 type respJSON struct { 57 Type ResponseType `json:"type"` 58 Result interface{} `json:"result"` 59 } 60 61 func (r *resp) MarshalJSON() ([]byte, error) { 62 return json.Marshal(respJSON{ 63 Type: r.Type, 64 Result: r.Result, 65 }) 66 } 67 68 func (r *resp) ServeHTTP(w http.ResponseWriter, _ *http.Request) { 69 status := r.Status 70 bs, err := r.MarshalJSON() 71 if err != nil { 72 logger.Noticef("cannot marshal %#v to JSON: %v", *r, err) 73 bs = nil 74 status = 500 75 } 76 77 hdr := w.Header() 78 if r.Status == 202 || r.Status == 201 { 79 if m, ok := r.Result.(map[string]interface{}); ok { 80 if location, ok := m["resource"]; ok { 81 if location, ok := location.(string); ok && location != "" { 82 hdr.Set("Location", location) 83 } 84 } 85 } 86 } 87 88 hdr.Set("Content-Type", "application/json") 89 w.WriteHeader(status) 90 w.Write(bs) 91 } 92 93 type errorKind string 94 95 const ( 96 errorKindLoginRequired = errorKind("login-required") 97 errorKindServiceControl = errorKind("service-control") 98 ) 99 100 type errorValue interface{} 101 102 type errorResult struct { 103 Message string `json:"message"` // mandatory in error responses 104 Kind errorKind `json:"kind,omitempty"` 105 Value errorValue `json:"value,omitempty"` 106 } 107 108 // SyncResponse builds a "sync" response from the given result. 109 func SyncResponse(result interface{}) Response { 110 if err, ok := result.(error); ok { 111 return InternalError("internal error: %v", err) 112 } 113 114 if rsp, ok := result.(Response); ok { 115 return rsp 116 } 117 118 return &resp{ 119 Type: ResponseTypeSync, 120 Status: 200, 121 Result: result, 122 } 123 } 124 125 // AsyncResponse builds an "async" response from the given *Task 126 func AsyncResponse(result map[string]interface{}) Response { 127 return &resp{ 128 Type: ResponseTypeAsync, 129 Status: 202, 130 Result: result, 131 } 132 } 133 134 // makeErrorResponder builds an errorResponder from the given error status. 135 func makeErrorResponder(status int) errorResponder { 136 return func(format string, v ...interface{}) Response { 137 res := &errorResult{} 138 if len(v) == 0 { 139 res.Message = format 140 } else { 141 res.Message = fmt.Sprintf(format, v...) 142 } 143 if status == 401 { 144 res.Kind = errorKindLoginRequired 145 } 146 return &resp{ 147 Type: ResponseTypeError, 148 Result: res, 149 Status: status, 150 } 151 } 152 } 153 154 // errorResponder is a callable that produces an error Response. 155 // e.g., InternalError("something broke: %v", err), etc. 156 type errorResponder func(string, ...interface{}) Response 157 158 // standard error responses 159 var ( 160 Unauthorized = makeErrorResponder(401) 161 NotFound = makeErrorResponder(404) 162 BadRequest = makeErrorResponder(400) 163 MethodNotAllowed = makeErrorResponder(405) 164 InternalError = makeErrorResponder(500) 165 NotImplemented = makeErrorResponder(501) 166 Forbidden = makeErrorResponder(403) 167 Conflict = makeErrorResponder(409) 168 )