Ejemplo n.º 1
0
// 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)
}
Ejemplo n.º 2
0
// -- 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
}
Ejemplo n.º 3
0
// -- 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
}
Ejemplo n.º 4
0
// 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)
}