go-omaha/omaha/handler.go
Michael Marineau f33cb66abb omaha: add complete http handler implementation
The handler is driven by something implementing the 'Updater' interface.
2017-04-21 14:12:44 -07:00

134 lines
3.7 KiB
Go

// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package omaha
import (
"encoding/xml"
"log"
"net/http"
)
type OmahaHandler struct {
Updater
}
func (o *OmahaHandler) ServeHTTP(w http.ResponseWriter, httpReq *http.Request) {
if httpReq.Method != "POST" {
log.Printf("omaha: Unexpected HTTP method: %s", httpReq.Method)
http.Error(w, "Expected a POST", http.StatusBadRequest)
return
}
// A request over 1M in size is certainly bogus.
reader := http.MaxBytesReader(w, httpReq.Body, 1024*1024)
decoder := xml.NewDecoder(reader)
var omahaReq Request
if err := decoder.Decode(&omahaReq); err != nil {
log.Printf("omaha: Failed decoding XML: %v", err)
http.Error(w, "Invalid XML", http.StatusBadRequest)
return
}
if omahaReq.Protocol != "3.0" {
log.Printf("omaha: Unexpected protocol: %q", omahaReq.Protocol)
http.Error(w, "Omaha 3.0 Required", http.StatusBadRequest)
return
}
httpStatus := 0
omahaResp := NewResponse()
for _, appReq := range omahaReq.Apps {
appResp := o.serveApp(omahaResp, httpReq, &omahaReq, appReq)
if appResp.Status == AppOK {
// HTTP is ok if any app is ok.
httpStatus = http.StatusOK
} else if httpStatus == 0 {
// If no app is ok HTTP will use the first error.
if appResp.Status == AppInternalError {
httpStatus = http.StatusInternalServerError
} else {
httpStatus = http.StatusBadRequest
}
}
}
if httpStatus == 0 {
httpStatus = http.StatusBadRequest
}
w.Header().Set("Content-Type", "text/xml; charset=utf-8")
w.WriteHeader(httpStatus)
if _, err := w.Write([]byte(xml.Header)); err != nil {
log.Printf("omaha: Failed writing response: %v", err)
return
}
encoder := xml.NewEncoder(w)
encoder.Indent("", "\t")
if err := encoder.Encode(omahaResp); err != nil {
log.Printf("omaha: Failed encoding response: %v", err)
}
}
func (o *OmahaHandler) serveApp(omahaResp *Response, httpReq *http.Request, omahaReq *Request, appReq *AppRequest) *AppResponse {
if err := o.CheckApp(omahaReq, appReq); err != nil {
if appStatus, ok := err.(AppStatus); ok {
return omahaResp.AddApp(appReq.Id, appStatus)
}
log.Printf("omaha: CheckApp failed: %v", err)
return omahaResp.AddApp(appReq.Id, AppInternalError)
}
appResp := omahaResp.AddApp(appReq.Id, AppOK)
if appReq.UpdateCheck != nil {
o.checkUpdate(appResp, httpReq, omahaReq, appReq)
}
if appReq.Ping != nil {
o.Ping(omahaReq, appReq)
appResp.AddPing()
}
for _, event := range appReq.Events {
o.Event(omahaReq, appReq, event)
appResp.AddEvent()
}
return appResp
}
func (o *OmahaHandler) checkUpdate(appResp *AppResponse, httpReq *http.Request, omahaReq *Request, appReq *AppRequest) {
update, err := o.CheckUpdate(omahaReq, appReq)
if err != nil {
if updateStatus, ok := err.(UpdateStatus); ok {
appResp.AddUpdateCheck(updateStatus)
} else {
log.Printf("omaha: CheckUpdate failed: %v", err)
appResp.AddUpdateCheck(UpdateInternalError)
}
} else if update != nil {
u := appResp.AddUpdateCheck(UpdateOK)
fillUpdate(u, update, httpReq)
} else {
appResp.AddUpdateCheck(NoUpdate)
}
}
func fillUpdate(u *UpdateResponse, update *Update, httpReq *http.Request) {
u.URLs = update.URLs([]string{"http://" + httpReq.Host})
u.Manifest = &update.Manifest
}