fiber_long_poll

package module
v2.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2020 License: MIT Imports: 10 Imported by: 0

README

GoDoc Go Report Card

Long polling library for Fiber web-framework

Golang long polling library for fasthttp-based web framework called Fiber.

Makes web pub-sub easy via an HTTP long-poll server.

Table of Contents

About

This library is just a port of existing library for long polling https://github.com/jcuga/golongpoll, but for Fiber ecosystem. You can read about it here https://github.com/jcuga/golongpoll#table-of-contents.

Usage

Here is example code: click!

Golang server side:

package main

import (
	"errors"
	"fmt"
	"log"
	"time"

	lp "github.com/LdDl/fiber-long-poll/v2"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/valyala/fasthttp"
)

var (
	userID = "my_pretty_uuid"
)

func main() {

	config := fiber.Config{
		ErrorHandler: func(ctx *fiber.Ctx, err error) error {
			log.Println(err)
			return ctx.Status(fasthttp.StatusInternalServerError).JSON(errors.New("panic error"))
		},
		IdleTimeout: 10 * time.Second,
	}
	allCors := cors.New(cors.Config{
		AllowOrigins:     "*",
		AllowHeaders:     "Origin, Authorization, Content-Type, Content-Length, Accept, Accept-Encoding, X-HttpRequest",
		AllowMethods:     "GET, POST, PUT, DELETE",
		ExposeHeaders:    "Content-Length",
		AllowCredentials: true,
		MaxAge:           5600,
	})

	server := fiber.New(config)
	server.Use(allCors)

	manager, err := lp.StartLongpoll(lp.Options{
		LoggingEnabled:                 false,
		MaxLongpollTimeoutSeconds:      120,
		MaxEventBufferSize:             100,
		EventTimeToLiveSeconds:         60 * 2,
		DeleteEventAfterFirstRetrieval: false,
	})
	if err != nil {
		log.Printf("Failed to create manager: %q", err)
		return
	}
	defer manager.Shutdown()

	go generatingMessages(manager)
	server.Get("/unread_messages", GetMessages(manager))

	err = server.Listen(":8080")
	if err != nil {
		fmt.Printf("Can't start server due the error: %s\n", err.Error())
	}
}

// GetMessages Long polling request
func GetMessages(manager *lp.LongpollManager) func(ctx *fiber.Ctx) error {
	return func(ctx *fiber.Ctx) error {
		ctx.Context().PostArgs().Set("timeout", "10")
		ctx.Context().PostArgs().Set("category", fmt.Sprintf("unread_messages_for_%s", userID))
		return manager.SubscriptionHandler(ctx)
	}
}

// generatingMessages Generate some messages
func generatingMessages(manager *lp.LongpollManager) {
	i := 0
	for {
		manager.Publish(fmt.Sprintf("unread_messages_for_%s", userID), fmt.Sprintf("Number: %d", i))
		i++
		time.Sleep(3 * time.Second)
	}
}

JavaScript client side:


<html>
    <head>
        <title>Fiber long polling example</title>
    </head>
    <body>
        <ul id="unred-messages"></ul>
        <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
        <script>
            if(typeof window.console == 'undefined') { window.console = {log: function (msg) {} }; }
            var sinceTime = (new Date(Date.now())).getTime();
            (function poll() {
                var timeout = 45;  // in seconds
                var optionalSince = "";
                if (sinceTime) {
                    optionalSince = "&since_time=" + sinceTime;
                }
                var pollUrl = `http://localhost:8080/unread_messages`;
                // how long to wait before starting next longpoll request in each case:
                var successDelay = 10;  // 10 ms
                var errorDelay = 3000;  // 3 sec
                $.ajax({ url: pollUrl,
                    success: function(data) {
                        if (data && data.events && data.events.length > 0) {
                            // got events, process them
                            // NOTE: these events are in chronological order (oldest first)
                            for (var i = 0; i < data.events.length; i++) {
                                // Display event
                                var event = data.events[i];
                                $("#unred-messages").append("<li>" + JSON.stringify(event.data) + " at " + (new Date(event.timestamp).toLocaleTimeString()) +  "</li>")
                                // Update sinceTime to only request events that occurred after this one.
                                sinceTime = event.timestamp;
                            }
                            console.log(data.events);
                            // success!  start next longpoll
                            setTimeout(poll, successDelay);
                            return;
                        }
                        if (data && data.timeout) {
                            console.log("No events, checking again.");
                            // no events within timeout window, start another longpoll:
                            setTimeout(poll, successDelay);
                            return;
                        }
                        if (data && data.error) {
                            console.log("Error response: " + data.error);
                            console.log("Trying again shortly...")
                            setTimeout(poll, errorDelay);
                            return;
                        }
                        // We should have gotten one of the above 3 cases:
                        // either nonempty event data, a timeout, or an error.
                        console.log("Didn't get expected event data, try again shortly...");
                        setTimeout(poll, errorDelay);
                    }, dataType: "json",
                error: function (data) {
                    console.log("Error in ajax request--trying again shortly...");
                    setTimeout(poll, errorDelay);  // 3s
                }
                });
            })();
        </script>
    </body>
</html>

Support

If you have troubles or questions please open an issue.

License

You can check it here

Documentation

Index

Constants

View Source
const (
	// FOREVER - Magic number to represent 'Forever' in LongpollOptions.EventTimeToLiveSeconds
	FOREVER = -1001
)

Variables

This section is empty.

Functions

This section is empty.

Types

type LongpollManager

type LongpollManager struct {
	SubscriptionHandler func(ctx *fiber.Ctx) error
	// contains filtered or unexported fields
}

LongpollManager provides an interface to interact with the internal longpolling pup-sub goroutine.

This allows you to publish events via Publish() If for some reason you want to stop the pub-sub goroutine at any time you can call Shutdown() and all longpolling will be disabled. Note that the pub-sub goroutine will exit on program exit, so for most simple programs, calling Shutdown() is not necessary.

A LongpollManager is created with each subscriptionManager that gets created by calls to manager := StartLongpoll(options) This interface also exposes the HTTP handler that client code can attach to a URL like so:

mux := http.NewServeMux()
mux.HandleFunc("/custom/path/to/events", manager.SubscriptionHandler)

Note, this http handler can be wrapped by another function (try capturing the manager in a closure) to add additional validation, access control, or other functionality on top of the subscription handler.

You can have another http handler publish events by capturing the manager in a closure and calling manager.Publish() from inside a http handler. See the advanced example (examples/advanced/advanced.go)

If for some reason you want multiple goroutines handling different pub-sub channels, you can simply create multiple LongpollManagers.

func StartLongpoll

func StartLongpoll(opts Options) (*LongpollManager, error)

StartLongpoll - Creates a LongpollManager, starts the internal pub-sub goroutine and returns the manager reference which you can use anywhere to Publish() events or attach a URL to the manager's SubscriptionHandler member. This function takes an Options struct that configures the longpoll behavior. If Options.EventTimeToLiveSeconds is omitted, the default is forever.

func (*LongpollManager) Publish

func (m *LongpollManager) Publish(category string, data interface{}) error

Publish an event for a given subscription category. This event can have any arbitrary data that is convert-able to JSON via the standard's json.Marshal() the category param must be a non-empty string no longer than 1024, otherwise you get an error.

func (*LongpollManager) Shutdown

func (m *LongpollManager) Shutdown()

Shutdown allows the internal goroutine that handles the longpull pup-sub to be stopped. This may be useful if you want to turn off longpolling without terminating your program. After a shutdown, you can't call Publish() or get any new results from the SubscriptionHandler. Multiple calls to this function on the same manager will result in a panic.

type Options

type Options struct {
	// Whether or not to print logs about longpolling
	LoggingEnabled bool

	// Max client timeout seconds  to be accepted by the SubscriptionHandler
	// (The 'timeout' HTTP query param).  Defaults to 120.
	MaxLongpollTimeoutSeconds int

	// How many events to buffer per subscriptoin category before discarding
	// oldest events due to buffer being exhausted.  Larger buffer sizes are
	// useful for high volumes of events in the same categories.  But for
	// low-volumes, smaller buffer sizes are more efficient.  Defaults to 250.
	MaxEventBufferSize int

	// How long (seconds) events remain in their respective category's
	// eventBuffer before being deleted. Deletes old events even if buffer has
	// the room.  Useful to save space if you don't need old events.
	// You can use a large MaxEventBufferSize to handle spikes in event volumes
	// in a single category but have a relatively short EventTimeToLiveSeconds
	// value to save space in the more common low-volume case.
	// If you want events to remain in the buffer as long as there is room per
	// MaxEventBufferSize, then use the magic value longpoll.FOREVER here.
	// Defaults to FOREVER.
	EventTimeToLiveSeconds int

	// Whether or not to delete an event as soon as it is retrieved via an
	// HTTP longpoll.  Saves on space if clients only interested in seing an
	// event once and never again.  Meant mostly for scenarios where events
	// act as a sort of notification and each subscription category is assigned
	// to a single client.  As soon as any client(s) pull down this event, it's
	// gone forever.  Notice how multiple clients can get the event if there
	// are multiple clients actively in the middle of a longpoll when a new
	// event occurs.  This event gets sent to all listening clients and then
	// the event skips being placed in a buffer and is gone forever.
	DeleteEventAfterFirstRetrieval bool
}

Options for LongpollManager that get sent to StartLongpoll(options)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL