Ai
1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
response.go 10.09 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-08 22:56 +08:00 . fix: daemon commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2015-2020 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package daemon
import (
"bufio"
"encoding/json"
"fmt"
"io"
"mime"
"net/http"
"path/filepath"
"strconv"
"time"
"gitee.com/mysnapcore/mysnapd/asserts"
"gitee.com/mysnapcore/mysnapd/client"
"gitee.com/mysnapcore/mysnapd/logger"
"gitee.com/mysnapcore/mysnapd/overlord/restart"
"gitee.com/mysnapcore/mysnapd/overlord/snapshotstate"
"gitee.com/mysnapcore/mysnapd/overlord/state"
"gitee.com/mysnapcore/mysnapd/snap"
"gitee.com/mysnapcore/mysnapd/systemd"
)
// ResponseType is the response type
type ResponseType string
// "there are three standard return types: Standard return value,
// Background operation, Error", each returning a JSON object with the
// following "type" field:
const (
ResponseTypeSync ResponseType = "sync"
ResponseTypeAsync ResponseType = "async"
ResponseTypeError ResponseType = "error"
)
// Response knows how to serve itself.
type Response interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
// A StructuredResponse serializes itself to our standard JSON response format.
type StructuredResponse interface {
Response
JSON() *respJSON
}
// respJSON represents our standard JSON response format.
type respJSON struct {
Type ResponseType `json:"type"`
// Status is the HTTP status code.
Status int `json:"status-code"`
// StatusText is filled by the serving pipeline.
StatusText string `json:"status"`
// Result is a free-form optional result object.
Result interface{} `json:"result"`
// Change is the change ID for an async response.
Change string `json:"change,omitempty"`
// Sources is used in find responses.
Sources []string `json:"sources,omitempty"`
// XXX SuggestedCurrency is part of unsupported paid snap code.
SuggestedCurrency string `json:"suggested-currency,omitempty"`
// Maintenance... are filled as needed by the serving pipeline.
WarningTimestamp *time.Time `json:"warning-timestamp,omitempty"`
WarningCount int `json:"warning-count,omitempty"`
Maintenance *errorResult `json:"maintenance,omitempty"`
}
func (r *respJSON) JSON() *respJSON {
return r
}
func maintenanceForRestartType(rst restart.RestartType) *errorResult {
e := &errorResult{}
switch rst {
case restart.RestartSystem, restart.RestartSystemNow:
e.Kind = client.ErrorKindSystemRestart
e.Message = systemRestartMsg
e.Value = map[string]interface{}{
"op": "reboot",
}
case restart.RestartSystemHaltNow:
e.Kind = client.ErrorKindSystemRestart
e.Message = systemHaltMsg
e.Value = map[string]interface{}{
"op": "halt",
}
case restart.RestartSystemPoweroffNow:
e.Kind = client.ErrorKindSystemRestart
e.Message = systemPoweroffMsg
e.Value = map[string]interface{}{
"op": "poweroff",
}
case restart.RestartDaemon:
e.Kind = client.ErrorKindDaemonRestart
e.Message = daemonRestartMsg
case restart.RestartSocket:
e.Kind = client.ErrorKindDaemonRestart
e.Message = socketRestartMsg
case restart.RestartUnset:
// shouldn't happen, maintenance for unset type should just be nil
panic("internal error: cannot marshal maintenance for RestartUnset")
}
return e
}
func (r *respJSON) addMaintenanceFromRestartType(rst restart.RestartType) {
if rst == restart.RestartUnset {
// nothing to do
return
}
r.Maintenance = maintenanceForRestartType(rst)
}
func (r *respJSON) addWarningCount(count int, stamp time.Time) {
if count == 0 {
return
}
r.WarningCount = count
r.WarningTimestamp = &stamp
}
func (r *respJSON) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
status := r.Status
r.StatusText = http.StatusText(r.Status)
bs, err := json.Marshal(r)
if err != nil {
logger.Noticef("cannot marshal %#v to JSON: %v", *r, err)
bs = nil
status = 500
}
hdr := w.Header()
if r.Status == 202 || r.Status == 201 {
if m, ok := r.Result.(map[string]interface{}); ok {
if location, ok := m["resource"]; ok {
if location, ok := location.(string); ok && location != "" {
hdr.Set("Location", location)
}
}
}
}
hdr.Set("Content-Type", "application/json")
w.WriteHeader(status)
w.Write(bs)
}
// SyncResponse builds a "sync" response from the given result.
func SyncResponse(result interface{}) Response {
if rsp, ok := result.(Response); ok {
return rsp
}
if err, ok := result.(error); ok {
return InternalError("internal error: %v", err)
}
return &respJSON{
Type: ResponseTypeSync,
Status: 200,
Result: result,
}
}
// AsyncResponse builds an "async" response for a created change
func AsyncResponse(result map[string]interface{}, change string) Response {
return &respJSON{
Type: ResponseTypeAsync,
Status: 202,
Result: result,
Change: change,
}
}
// A snapStream ServeHTTP method streams a snap
type snapStream struct {
SnapName string
Filename string
Info *snap.DownloadInfo
Token string
stream io.ReadCloser
resume int64
}
// ServeHTTP from the Response interface
func (s *snapStream) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
hdr := w.Header()
hdr.Set("Content-Type", "application/octet-stream")
snapname := fmt.Sprintf("attachment; filename=%s", s.Filename)
hdr.Set("Content-Disposition", snapname)
hdr.Set("Snap-Sha3-384", s.Info.Sha3_384)
// can't set Content-Length when stream is nil as it breaks http clients
// setting it also when there is a stream, for consistency
hdr.Set("Snap-Length", strconv.FormatInt(s.Info.Size, 10))
if s.Token != "" {
hdr.Set("Snap-Download-Token", s.Token)
}
if s.stream == nil {
// nothing to actually stream
return
}
hdr.Set("Content-Length", strconv.FormatInt(s.Info.Size-s.resume, 10))
if s.resume > 0 {
hdr.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", s.resume, s.Info.Size-1, s.Info.Size))
w.WriteHeader(206)
}
defer s.stream.Close()
bytesCopied, err := io.Copy(w, s.stream)
if err != nil {
logger.Noticef("cannot copy snap %s (%#v) to the stream: %v", s.SnapName, s.Info, err)
http.Error(w, err.Error(), 500)
}
if bytesCopied != s.Info.Size-s.resume {
logger.Noticef("cannot copy snap %s (%#v) to the stream: bytes copied=%d, expected=%d", s.SnapName, s.Info, bytesCopied, s.Info.Size)
http.Error(w, io.EOF.Error(), 502)
}
}
// A snapshotExportResponse 's ServeHTTP method serves a specific snapshot ID
type snapshotExportResponse struct {
*snapshotstate.SnapshotExport
setID uint64
st *state.State
}
// ServeHTTP from the Response interface
func (s snapshotExportResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Length", strconv.FormatInt(s.Size(), 10))
w.Header().Add("Content-Type", client.SnapshotExportMediaType)
if err := s.StreamTo(w); err != nil {
logger.Debugf("cannot export snapshot: %v", err)
}
s.Close()
s.st.Lock()
defer s.st.Unlock()
snapshotstate.UnsetSnapshotOpInProgress(s.st, s.setID)
}
// A fileResponse 's ServeHTTP method serves the file
type fileResponse string
// ServeHTTP from the Response interface
func (f fileResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
filename := fmt.Sprintf("attachment; filename=%s", filepath.Base(string(f)))
w.Header().Add("Content-Disposition", filename)
http.ServeFile(w, r, string(f))
}
// A journalLineReaderSeqResponse's ServeHTTP method reads lines (presumed to
// be, each one on its own, a JSON dump of a systemd.Log, as output by
// journalctl -o json) from an io.ReadCloser, loads that into a client.Log, and
// outputs the json dump of that, padded with RS and LF to make it a valid
// json-seq response.
//
// The reader is always closed when done (this is important for
// osutil.WatingStdoutPipe).
//
// Tip: “jq” knows how to read this; “jq --seq” both reads and writes this.
type journalLineReaderSeqResponse struct {
io.ReadCloser
follow bool
}
func (rr *journalLineReaderSeqResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json-seq")
flusher, hasFlusher := w.(http.Flusher)
var err error
dec := json.NewDecoder(rr)
writer := bufio.NewWriter(w)
enc := json.NewEncoder(writer)
for {
var log systemd.Log
if err = dec.Decode(&log); err != nil {
break
}
writer.WriteByte(0x1E) // RS -- see ascii(7), and RFC7464
// ignore the error...
t, _ := log.Time()
if err = enc.Encode(client.Log{
Timestamp: t,
Message: log.Message(),
SID: log.SID(),
PID: log.PID(),
}); err != nil {
break
}
if rr.follow {
if e := writer.Flush(); e != nil {
break
}
if hasFlusher {
flusher.Flush()
}
}
}
if err != nil && err != io.EOF {
fmt.Fprintf(writer, `\x1E{"error": %q}\n`, err)
logger.Noticef("cannot stream response; problem reading: %v", err)
}
if err := writer.Flush(); err != nil {
logger.Noticef("cannot stream response; problem writing: %v", err)
}
rr.Close()
}
type assertResponse struct {
assertions []asserts.Assertion
bundle bool
}
// AssertResponse builds a response whose ServerHTTP method serves one or a bundle of assertions.
func AssertResponse(asserts []asserts.Assertion, bundle bool) Response {
if len(asserts) > 1 {
bundle = true
}
return &assertResponse{assertions: asserts, bundle: bundle}
}
func (ar assertResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t := asserts.MediaType
if ar.bundle {
t = mime.FormatMediaType(t, map[string]string{"bundle": "y"})
}
w.Header().Set("Content-Type", t)
w.Header().Set("X-Ubuntu-Assertions-Count", strconv.Itoa(len(ar.assertions)))
w.WriteHeader(200)
enc := asserts.NewEncoder(w)
for _, a := range ar.assertions {
err := enc.Encode(a)
if err != nil {
logger.Noticef("cannot write encoded assertion into response: %v", err)
break
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助