func (s *Server) subscribe(w http.ResponseWriter, r *http.Request) { if _, ok := w.(http.Flusher); !ok { http.Error(w, "streaming unsupported", http.StatusInternalServerError) return } rd, err := s.newReader(w, r) if rd != nil { defer rd.Close() } if err != nil { handleError(w, r, err) return } _, err = io.Copy(newWriteFlusher(w), rd) netErr, ok := err.(net.Error) if ok && netErr.Timeout() { util.CountWithData("server.sub.read.timeout", 1, "msg=%q request_id=%q", err, r.Header.Get("Request-Id")) return } if err != nil { rollbar.Error(rollbar.ERR, fmt.Errorf("unhandled error: %#v", err)) } util.CountWithData("server.sub.read.finish", 1, "msg=%q request_id=%q", err, r.Header.Get("Request-Id")) }
func storeOutput(channel string, requestURI string) { if buf, err := broker.Get(channel); err == nil { if err := storage.Put(requestURI, bytes.NewBuffer(buf)); err != nil { util.CountWithData("server.storeOutput.put.error", 1, "err=%s", err.Error()) } } else { util.CountWithData("server.storeOutput.get.error", 1, "err=%s", err.Error()) } }
func (s *Server) publish(w http.ResponseWriter, r *http.Request) { if !util.StringInSlice(r.TransferEncoding, "chunked") { http.Error(w, "A chunked Transfer-Encoding header is required.", http.StatusBadRequest) return } writer, err := broker.NewWriter(key(r)) if err != nil { handleError(w, r, err) return } body := bufio.NewReader(r.Body) defer r.Body.Close() wl, err := broker.Len(writer) if err != nil { handleError(w, r, err) return } if wl > 0 { _, err = body.Discard(int(wl)) if err != nil { handleError(w, r, err) return } } _, err = io.Copy(writer, body) if err == io.ErrUnexpectedEOF { util.CountWithData("server.pub.read.eoferror", 1, "msg=%q request_id=%q", err, r.Header.Get("Request-Id")) return } netErr, ok := err.(net.Error) if ok && netErr.Timeout() { util.CountWithData("server.pub.read.timeout", 1, "msg=%q request_id=%q", err, r.Header.Get("Request-Id")) handleError(w, r, netErr) return } if err != nil { log.Printf("%#v", err) http.Error(w, "Unhandled error, please try again.", http.StatusInternalServerError) rollbar.Error(rollbar.ERR, fmt.Errorf("unhandled error: %#v", err)) return } util.CountWithData("server.pub.read.end", 1, "request_id=%q", r.Header.Get("Request-Id")) writer.Close() // Asynchronously upload the output to our defined storage backend. go storeOutput(key(r), requestURI(r), s.StorageBaseURL) }
func mkstream(w http.ResponseWriter, _ *http.Request) { registrar := broker.NewRedisRegistrar() uuid, err := util.NewUUID() if err != nil { http.Error(w, "Unable to create stream. Please try again.", http.StatusServiceUnavailable) rollbar.Error(rollbar.ERR, fmt.Errorf("unable to create new uuid for stream: %#v", err)) util.CountWithData("mkstream.create.fail", 1, "error=%s", err) return } if err := registrar.Register(uuid); err != nil { http.Error(w, "Unable to create stream. Please try again.", http.StatusServiceUnavailable) rollbar.Error(rollbar.ERR, fmt.Errorf("unable to register stream: %#v", err)) util.CountWithData("mkstream.create.fail", 1, "error=%s", err) return } util.Count("mkstream.create.success") io.WriteString(w, string(uuid)) }
func (s *Server) createStream(w http.ResponseWriter, r *http.Request) { registrar := broker.NewRedisRegistrar() if err := registrar.Register(key(r)); err != nil { http.Error(w, "Unable to create stream. Please try again.", http.StatusServiceUnavailable) rollbar.Error(rollbar.ERR, fmt.Errorf("unable to register stream: %#v", err)) util.CountWithData("put.create.fail", 1, "error=%s", err) return } util.Count("put.create.success") w.WriteHeader(http.StatusCreated) }
// Register registers the new channel func (rr *RedisRegistrar) Register(channelName string) (err error) { conn := redisPool.Get() defer conn.Close() channel := channel(channelName) _, err = conn.Do("SETEX", channel.id(), redisChannelExpire, make([]byte, 0)) if err != nil { util.CountWithData("RedisRegistrar.Register.error", 1, "error=%s", err) return } return }
// IsRegistered checks whether a channel name is registered func (rr *RedisRegistrar) IsRegistered(channelName string) (registered bool) { conn := redisPool.Get() defer conn.Close() channel := channel(channelName) exists, err := redis.Bool(conn.Do("EXISTS", channel.id())) if err != nil { util.CountWithData("RedisRegistrar.IsRegistered.error", 1, "error=%s", err) return false } return exists }
func pub(w http.ResponseWriter, r *http.Request) { if !util.StringSliceUtil(r.TransferEncoding).Contains("chunked") { http.Error(w, "A chunked Transfer-Encoding header is required.", http.StatusBadRequest) return } writer, err := broker.NewWriter(key(r)) if err != nil { handleError(w, r, err) return } defer writer.Close() body := bufio.NewReader(r.Body) defer r.Body.Close() done := make(chan struct{}) go func() { for { select { case <-done: // Asynchronously upload the output to our defined storage backend. go storeOutput(key(r), requestURI(r)) return case <-time.After(*util.StorageInterval): // Asynchronously upload the output to our defined storage backend. go storeOutput(key(r), requestURI(r)) } } }() _, err = io.Copy(writer, body) if err == io.ErrUnexpectedEOF { util.CountWithData("server.pub.read.eoferror", 1, "msg=\"%v\"", err.Error()) return } if err != nil { log.Printf("%#v", err) http.Error(w, "Unhandled error, please try again.", http.StatusInternalServerError) rollbar.Error(rollbar.ERR, fmt.Errorf("unhandled error: %#v", err)) return } close(done) }
func (r *reader) Read(p []byte) (n int, err error) { if r.closed { // Don't read from a closed redigo connection return 0, io.EOF } if n, err := r.replay(p); n > 0 || err != nil { return n, err } switch msg := r.psc.Receive().(type) { case redis.Message: case redis.PMessage: return r.read(msg, p) case redis.Subscription: case error: util.CountWithData("RedisBroker.redisSubscribe.ReceiveError", 1, "err=%s", msg) err = msg return } return }
func handleError(w http.ResponseWriter, r *http.Request, err error) { switch err { case broker.ErrNotRegistered, storage.ErrNoStorage, storage.ErrNotFound: message := "Channel is not registered." if r.Header.Get("Accept") == "text/ascii; version=feral" { message = assets.HttpCatGone } http.Error(w, message, http.StatusNotFound) case storage.ErrRange: w.WriteHeader(http.StatusRequestedRangeNotSatisfiable) case errNoContent: // As indicated in the w3 spec[1] an SSE stream // that's already done should return a `204 No Content` // [1]: http://www.w3.org/TR/2012/WD-eventsource-20120426/ w.WriteHeader(http.StatusNoContent) default: util.CountWithData("server.handleError", 1, "error=%s", err.Error()) w.WriteHeader(http.StatusInternalServerError) } }