// standardize the error reporting back to the client. func (self *Worker) handleError(sock *PushWS, message util.JsMap, err error) (ret error) { if self.logger != nil { self.logger.Info("worker", "Sending error", util.Fields{"error": ErrStr(err)}) } message["status"], message["error"] = sperrors.ErrToStatus(err) return websocket.JSON.Send(sock.Socket, message) }
// -- REST func (self *Handler) UpdateHandler(resp http.ResponseWriter, req *http.Request) { // Handle the version updates. var err error var port string var vers int64 if ClientCount() > self.config["max_connections"].(int) { if self.logger != nil { if toomany == 0 { atomic.StoreInt32(&toomany, 1) self.logger.Error("handler", "Socket Count Exceeded", nil) } } MetricIncrement("too many connections") http.Error(resp, "{\"error\": \"Server unavailable\"}", http.StatusServiceUnavailable) return } if toomany != 0 { atomic.StoreInt32(&toomany, 0) } timer := time.Now() filter := regexp.MustCompile("[^\\w-\\.\\=]") if self.logger != nil { self.logger.Debug("update", "Handling Update", util.Fields{"path": req.URL.Path}) } if self.logger != nil { self.logger.Debug("update", "=========== UPDATE ====", nil) } defer func() { if self.logger != nil { self.logger.Debug("update", "+++++++++++++ DONE +++", nil) } }() if req.Method != "PUT" { http.Error(resp, "", http.StatusMethodNotAllowed) return } svers := req.FormValue("version") if svers != "" { vers, err = strconv.ParseInt(svers, 10, 64) if err != nil || vers < 0 { http.Error(resp, "\"Invalid Version\"", http.StatusBadRequest) return } } else { vers = time.Now().UTC().Unix() } elements := strings.Split(req.URL.Path, "/") pk := elements[len(elements)-1] if len(pk) == 0 { if self.logger != nil { self.logger.Error("update", "No token, rejecting request", util.Fields{"remoteAddr": req.RemoteAddr, "path": req.URL.Path}) } http.Error(resp, "Token not found", http.StatusNotFound) return } if token, ok := self.config["token_key"]; ok && len(token.([]uint8)) > 0 { if self.logger != nil { // Note: dumping the []uint8 keys can produce terminal glitches self.logger.Debug("main", "Decoding...", nil) } var err error bpk, err := Decode(token.([]byte), pk) if err != nil { if self.logger != nil { self.logger.Error("update", "Could not decode token", util.Fields{"primarykey": pk, "remoteAddr": req.RemoteAddr, "path": req.URL.Path, "error": ErrStr(err)}) } http.Error(resp, "", http.StatusNotFound) return } pk = strings.TrimSpace(string(bpk)) } if filter.Find([]byte(pk)) != nil { if self.logger != nil { self.logger.Error("update", "Invalid token for update", util.Fields{"token": pk, "path": req.URL.Path}) } http.Error(resp, "Invalid Token", http.StatusNotFound) return } uaid, chid, err := storage.ResolvePK(pk) if err != nil { if self.logger != nil { self.logger.Error("update", "Could not resolve PK", util.Fields{"primaryKey": pk, "path": req.URL.Path, "error": ErrStr(err)}) } return } if chid == "" { if self.logger != nil { self.logger.Error("update", "Incomplete primary key", util.Fields{"uaid": uaid, "channelID": chid, "remoteAddr": req.RemoteAddr}) } return } //log.Printf("<< %s.%s = %d", uaid, chid, vers) if iport, ok := self.config["port"]; ok { port = iport.(string) } if port == "80" { port = "" } if port != "" { port = ":" + port } currentHost := util.MzGet(self.config, "shard.current_host", "localhost") host, err := self.store.GetUAIDHost(uaid) if err != nil { if self.logger != nil { self.logger.Error("update", "Could not discover host for UAID", util.Fields{"uaid": uaid, "error": err.Error()}) } host = util.MzGet(self.config, "shard.defaultHost", "localhost") } if util.MzGetFlag(self.config, "shard.do_proxy") { if host != currentHost && host != "localhost" { if self.logger != nil { self.logger.Info("update", "Proxying request for UAID", util.Fields{"uaid": uaid, "destination": host + port}) } // Use tcp routing. if util.MzGetFlag(self.config, "shard.router") { // If there was an error routing the update, don't // tell the AppServer. Chances are it's temporary, and // the client will get the update on next refresh/reconnect self.router.SendUpdate(host, uaid, chid, vers, timer) } else { proto := "http" if len(util.MzGet(self.config, "ssl.certfile", "")) > 0 { proto = "https" } err = proxyNotification(proto, host+port, req.URL.Path, vers) if err != nil && self.logger != nil { self.logger.Error("update", "Proxy failed", util.Fields{ "uaid": uaid, "destination": host + port, "error": err.Error()}) } } MetricIncrement("routing update: out") if err != nil { http.Error(resp, err.Error(), 500) } else { resp.Write([]byte("Ok")) } return } } if self.logger != nil { defer func(uaid, chid, path string, timer time.Time) { self.logger.Info("timer", "Client Update complete", util.Fields{ "uaid": uaid, "path": req.URL.Path, "channelID": chid, "duration": strconv.FormatInt(time.Now().Sub(timer).Nanoseconds(), 10)}) }(uaid, chid, req.URL.Path, timer) self.logger.Info("update", "setting version for ChannelID", util.Fields{"uaid": uaid, "channelID": chid, "version": strconv.FormatInt(vers, 10)}) } err = self.store.UpdateChannel(pk, vers) if err != nil { if self.logger != nil { self.logger.Error("update", "Cound not update channel", util.Fields{"UAID": uaid, "channelID": chid, "version": strconv.FormatInt(vers, 10), "error": err.Error()}) } status, _ := sperrors.ErrToStatus(err) http.Error(resp, "Could not update channel version", status) return } resp.Header().Set("Content-Type", "application/json") resp.Write([]byte("{}")) // Ping the appropriate server if client, ok := Clients[uaid]; ok { Flush(client, chid, int64(vers)) } MetricIncrement("update channel") return }
// -- REST func UpdateHandler(resp http.ResponseWriter, req *http.Request, config util.JsMap, logger *util.HekaLogger) { // Handle the version updates. var err error timer := time.Now() logger.Debug("main", fmt.Sprintf("Handling Update %s", req.URL.Path), nil) if req.Method != "PUT" { http.Error(resp, "", http.StatusMethodNotAllowed) return } vers := fmt.Sprintf("%d", time.Now().UTC().Unix()) elements := strings.Split(req.URL.Path, "/") pk := elements[len(elements)-1] if len(pk) == 0 { logger.Error("main", "No token, rejecting request", nil) http.Error(resp, "Token not found", http.StatusNotFound) return } store := storage.New(config, logger) if token, ok := config["token_key"]; ok { var err error bpk, err := Decode(token.([]byte), pk) if err != nil { logger.Error("main", fmt.Sprintf("Could not decode token %s, %s", pk, err), nil) http.Error(resp, "", http.StatusNotFound) return } pk = strings.TrimSpace(string(bpk)) } uaid, appid, err := storage.ResolvePK(pk) if err != nil { logger.Error("main", fmt.Sprintf("Could not resolve PK %s, %s", pk, err), nil) return } currentHost := "localhost" if val, ok := config["shard.currentHost"]; ok { currentHost = val.(string) } host, err := store.GetUAIDHost(uaid) if err != nil { logger.Error("main", fmt.Sprintf("Could not discover host for %s, %s (using default)", uaid, err), nil) if val, ok := config["shard.defaultHost"]; ok { host = val.(string) } else { val = "localhost" } } if host != currentHost || host != "localhost" { logger.Info("main", fmt.Sprintf("Proxying request to %s", host), nil) err = proxyNotification(host, req.URL.Path) if err != nil { logger.Error("main", fmt.Sprintf("Proxy to %s failed: %s", host, err), nil) } return } defer func(uaid, appid, path string, timer time.Time) { logger.Info("timer", "Client Update complete", util.JsMap{ "uaid": uaid, "path": req.URL.Path, "channelID": appid, "duration": time.Now().Sub(timer).Nanoseconds()}) }(uaid, appid, req.URL.Path, timer) logger.Info("main", fmt.Sprintf("setting version for %s.%s to %s", uaid, appid, vers), nil) err = store.UpdateChannel(pk, vers) if err != nil { errstr := fmt.Sprintf("Could not update channel %s.%s :: %s", uaid, appid, err) logger.Warn("main", errstr, nil) status := sperrors.ErrToStatus(err) http.Error(resp, errstr, status) return } resp.Header().Set("Content-Type", "application/json") resp.Write([]byte("{}")) logger.Info("timer", "Client Update complete", util.JsMap{"uaid": uaid, "channelID": appid, "duration": time.Now().Sub(timer).Nanoseconds()}) // Ping the appropriate server if client, ok := Clients[uaid]; ok { Flush(client) } return }
// standardize the error reporting back to the client. func (self *Worker) handleError(sock PushWS, message util.JsMap, err error) (ret error) { self.log.Info("worker", fmt.Sprintf("Sending error %s", err), nil) message["status"] = sperrors.ErrToStatus(err) message["error"] = err.Error() return websocket.JSON.Send(sock.Socket, message) }