// 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 }