wc

package module
v0.0.0-...-3299a1c Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2022 License: BSD-3-Clause Imports: 16 Imported by: 0

README

wc

This library was used to build dropinchat.com for YC Hacks.

A Go web server compatible with closure-library's goog.net.WebChannel. A WebChannel represents a logical bi-directional communication channel between client and server. By exposing a generic communication interface which can be implemented over a variety of transports (eg: BrowserChannel, WebSockets, WebRTC, etc) WebChannel provides additional flexibility over programming directly on top of WebSockets.

The client-side portion of WebChannel is open sourced (APLv2) as part of closure-library. Unfortunately, Google has not released the server-side portion of the code required to use WebChannel meaningfully. The wc package provides an open source (BSD) licensed golang server-side library to fill this missing gap.

See the wcchat package for an example application.

WebChannel

From goog.net.WebChannel:

Similar to HTML5 WebSocket and Closure BrowserChannel, WebChannel offers an abstraction for point-to-point socket-like communication between a browser client and a remote origin.

WebChannels are created via WebChannel. Multiple WebChannels may be multiplexed over the same WebChannelTransport, which represents the underlying physical connectivity over standard wire protocols such as HTTP and SPDY.

A WebChannels in turn represents a logical communication channel between the client and server end point. A WebChannel remains open for as long as the client or server end-point allows.

Messages may be delivered in-order or out-of-order, reliably or unreliably over the same WebChannel. Message delivery guarantees of a WebChannel is to be specified by the application code; and the choice of the underlying wire protocols is completely transparent to the API users.

Client-to-client messaging via WebRTC based transport may also be support via the same WebChannel API in future.

At the time of this writing (5/2014) the only WebChannel transport included in closure-library is BrowserChannel. As additional transports are added wc intends to add support for them as well.

BrowserChannel

From goog.net.BrowserChannel:

A BrowserChannel simulates a bidirectional socket over HTTP. It is the basis of the Gmail Chat IM connections to the server.

BrowserChannel works on all major browsers (including IE5.5+) using a variety of technologies including forever iframes (IE < 10) and XHR Streaming (IE10+ and non-IE).

Client Usage (JavaScript)

To connect from the client:

  • goog.net.WebChannel.Options
  • Implement a BrowserChannel.Handler subclass
  • Instantiate a BrowserChannel and connect()
  • call channel.sendMap() to send data
goog.require('goog.net.createWebChannelTransport');
goog.require('goog.net.WebChannel');

/**
 * @type {!goog.net.WebChannelTransport}
 */
var channelTransport = goog.net.createWebChannelTransport();

/**
 * @type {!goog.net.WebChannel.Options}
 */
var options = { supportsCrossDomainXhr: true };

/**
 * @type {!goog.net.WebChannel}
 */
var channel = channelTransport.createWebChannel('/channel', options);


/**
 * Browser channel handler.
 * @constructor
 * @extends {goog.net.BrowserChannel.Handler}
 */
demo.ChannelHandler = function() {};
goog.inherits(demo.ChannelHandler, goog.net.BrowserChannel.Handler);

/** @inheritDoc */
demo.ChannelHandler.prototype.channelHandleArray = function(browserChannel, array) {
  ...
};

var handler = new demo.ChannelHandler();
var channelDebug = new goog.net.ChannelDebug();
var channel = new goog.net.BrowserChannel('8', ['<host prefix>', '<blocked prefix>']);
channel.setSupportsCrossDomainXhrs(true);
channel.setHandler(handler);
channel.setChannelDebug(channelDebug);
channel.connect('channel/test', 'channel/bind', {});

channel.sendMap(...);

channel.disconnect();

Server Usage (Go)

TODO(ahochhaus): Document

go get github.com/samegoal/wc

Docs

Demo Chat Application

Alternate Implementations

Thanks

  • mdavids, upstream author of BrowserChannel, for all of the help he provided.

Documentation

Overview

Package wc implements a pure-Go web server capable of bi-directional communication between a Go (golang) net.http server and a in-browser client. Both goog.net.WebChannel and goog.net.BrowserChannel from closure-library are supported client-side.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnknownSID is the error to be returned when the requested SID is not
	// known to the server.
	ErrUnknownSID       = errors.New("wc: Unknown SID")
	BackChannelDuration = 4 * time.Minute
)

Functions

func BindHandler

func BindHandler(w http.ResponseWriter, r *http.Request)

BindHandler handles forward and backward channel HTTP requests. When using the defaults this handler should be installed at "/channel" (WebChannel) or "/channel/bind" (BrowserChannel).

func SetSessionManager

func SetSessionManager(sessionMgr SessionManager)

SetSessionManager allows the calling code to inject a custom application specific SessionManager implementation.

func SetSessionManagerWithPrefix

func SetSessionManagerWithPrefix(prefix string, sessionMgr SessionManager)

SetSessionManager allows the calling code to inject a custom application specific SessionManager implementation.

func TestHandler

func TestHandler(w http.ResponseWriter, r *http.Request)

TestHandler handles WebChannel and BrowserChannel test requests. When using the defaults this hanlder should be installed at "/channel/test".

Types

type ByLength

type ByLength []string

func (ByLength) Len

func (s ByLength) Len() int

func (ByLength) Less

func (s ByLength) Less(i, j int) bool

func (ByLength) Swap

func (s ByLength) Swap(i, j int)

type DefaultSession

type DefaultSession struct {
	SessionID string
	// contains filtered or unexported fields
}

DefaultSession provides a partial implementation of the Session interface. Callers must implement at least Authenticated(), BackChannel(), AckBackChannelThrough(), BackChannelAdd() and ForwardChannel().

func NewDefaultSession

func NewDefaultSession(r *http.Request, sid string) *DefaultSession

NewDefaultSession initializes a DefaultSession object with the specified ID.

func (*DefaultSession) BackChannelClose

func (s *DefaultSession) BackChannelClose()

BackChannelClose provides a noop implementation.

func (*DefaultSession) BackChannelNewSessionMessages

func (s *DefaultSession) BackChannelNewSessionMessages() error

BackChannelNewSessionMessages provides a noop implementation.

func (*DefaultSession) BackChannelOpen

func (s *DefaultSession) BackChannelOpen()

BackChannelOpen provides a noop implementation.

func (*DefaultSession) DataNotifier

func (s *DefaultSession) DataNotifier() chan int

DataNotifier returns the DefaultSession notifier chan.

func (*DefaultSession) Notifier

func (s *DefaultSession) Notifier() chan SessionActivity

Notifier returns the DefaultSession notifier chan.

func (*DefaultSession) SID

func (s *DefaultSession) SID() string

SID return the SessionID field.

func (*DefaultSession) SessionManager

func (s *DefaultSession) SessionManager() SessionManager

BackChannelOpen provides a noop implementation.

type DefaultSessionManager

type DefaultSessionManager struct {
}

DefaultSessionManager provides a partial implementation of the SessionManager interface. Callers must implement at least NewSession() and TerminatedSession().

func (*DefaultSessionManager) Debug

func (sm *DefaultSessionManager) Debug(debug string)

Debug discards debugging messages.

func (*DefaultSessionManager) Error

func (sm *DefaultSessionManager) Error(r *http.Request, err error)

Error logs to stderr.

func (*DefaultSessionManager) HostPrefix

func (sm *DefaultSessionManager) HostPrefix() string

HostPrefix provides an empty host prefix.

func (*DefaultSessionManager) LookupSession

func (sm *DefaultSessionManager) LookupSession(r *http.Request, sid string) (
	Session,
	*SessionInfo,
	error,
)

LookupSession provides a noop implementation. All sessions requested are returned as ErrUnknownSID which is suitable for applications which do not persist session information across server server restarts.

func (*DefaultSessionManager) Warning

func (sm *DefaultSessionManager) Warning(warn string)

Warning discards warning messages.

type GZIPResponseWriter

type GZIPResponseWriter struct {
	*gzip.Writer
	http.ResponseWriter
	// contains filtered or unexported fields
}

GZIPResponseWriter wraps a http.ResponseWriter and provides optional gzip compression. Streaming HTTP chunks is supported using the http.Flusher interface. Close notification is supported via the http.CloseNotifier interface.

func NewGZIPResponseWriter

func NewGZIPResponseWriter(
	w http.ResponseWriter,
	r *http.Request,
) *GZIPResponseWriter

NewGZIPResponseWriter creates a new GZIPResponseWriter. The http.Request is necessary to check if the browser supports GZIP compression and will not be used after the call to NewGZIPResponseWriter returns.

func (*GZIPResponseWriter) Close

func (w *GZIPResponseWriter) Close()

Close cleans up the underlying gzip.Writer (if necessary).

func (*GZIPResponseWriter) CloseNotify

func (w *GZIPResponseWriter) CloseNotify() <-chan bool

CloseNotify return the underlying CloseNotify() channel.

func (*GZIPResponseWriter) Flush

func (w *GZIPResponseWriter) Flush()

Flush writes any buffered data to the underlying ResponseWriter.

func (*GZIPResponseWriter) Header

func (w *GZIPResponseWriter) Header() http.Header

Header return the http.Header from the underlying http.ResponseWriter.

func (*GZIPResponseWriter) Write

func (w *GZIPResponseWriter) Write(b []byte) (int, error)

Writes the response to the underlying http.ResponseWriter while transparently supporting compression for uncompressed data.

func (*GZIPResponseWriter) WriteHeader

func (w *GZIPResponseWriter) WriteHeader(code int)

WriteHeader detects gzip compession and then proxies the supplied status code to the underlying http.ResponseWriter.

type Message

type Message struct {
	ID   int
	Body []byte
}

Message describes a single forward or backchannel message.

func NewMessage

func NewMessage(ID int, Body []byte) *Message

NewMessage creates a new Message struct.

type Session

type Session interface {
	SID() string

	// Authenticated verifies that the sid and request pair are correctly
	// associated and have access to the user application. If Authenticated()
	// returns true back/forward channel messages will be sent/received.
	//
	// This check only determines if the given HTTP request should be able to
	// send and receive messages for the specified sid. Additional application
	// level security is required for almost all applications.
	Authenticated(r *http.Request) bool

	// Notifier provides the channel for application code to pass SessionActivity
	// events for processing by WebChannel.
	Notifier() chan SessionActivity

	// DataNotifier() provides a channel for application code to pass byte counts
	// each time new BackChannel data arrives. Writes to this session will not
	// block so it is safe to write to this channel even from inside the
	// session's go routine (for example from the ForwardChannel() callback).
	DataNotifier() chan int

	// BackChannelNewSessionMessages allows insertion of messages into the back
	// channel queue of a new session. Do not add messages from NewSession().
	BackChannelNewSessionMessages() error

	// BackChannelPeek returns a slice of all pending backchannel Messages.
	BackChannelPeek() ([]*Message, error)

	// BackChannelClose notifies that the current backchannel has closed. Most
	// applications do not need this and can rely on the default implementation.
	BackChannelClose()

	// BackChannelOpen notifies that a new backchannel has opened. Most
	// applications do not need this and can rely on the default implementation.
	BackChannelOpen()

	// BackChannelAckThrough notifies that all messages up to an including ID
	// have been successfully delivered to the client. These messages should now
	// be garbage collected.
	BackChannelACKThrough(ID int) error

	// BackChannelAdd adds the message to into the pending backchannel queue.
	// This functionality is necessary to support WebChannel internal messages
	// such as "create", "noop" and "stop". These internal messages are passed to
	// the application layer to allow for a shared message numbering scheme on
	// the wire and application level.
	BackChannelAdd(messageBody []byte) error

	// ForwardChannel passes the set of messages delivered from the client to the
	// application for persistent storage (eg: being added to a "MessageQueue"
	// for later processing) or processed synchronously prior to returning from
	// ForwardChannel. At the point that ForwardChannel() returns the messages
	// will be ACKed to the client and not attempted to be redelivered.
	// Therefore, it is vital that a non-nil error be returned (such that
	// no ACK is sent to the client) if the messages could not be added to
	// storage for later processing (or processed synchronously).
	ForwardChannel(msgs []*Message) error

	// SessionManager returns the SessionManager for the current session.
	//
	// Most applications only require a single SessionManager.
	SessionManager() SessionManager
}

Session specifies the interface for the calling application to interact with an individual WebChannel session. This is used to both modify the Session and receive events from it. Only a single method will be invoked per session at a time.

type SessionActivity

type SessionActivity int

SessionActivity sends notifications from application level code to the wc library.

const (
	// ServerTerminate notifies wc the the given session should be terminated.
	// Common use cases include Session timeouts, automatically logging a user
	// out of the web application, or terminating a session out from under an
	// active user when their account is deleted.
	//
	// It is the responsibility of the application level code to send
	// ServerTerminate notifications when active sessions should be timed out.
	// Failure to do so will cause leaking Session objects and the goroutines
	// processing those sessions. Additionally, it is the responsibility of the
	// application level code to cleanup old sessions which are no longer used
	// but might be retained in persistent storage (such as a message queue).
	// Such session "leakage" can happen, for example, when the server crashes
	// and a session which was previously being processed (and would have been
	// timed-out) it not used again after the application restarts.
	ServerTerminate SessionActivity = 0
)

type SessionInfo

type SessionInfo struct {
	// BackChannelAID should be set to the lowest ID in the back channel message
	// queue when computing the return value for LookupSession().
	BackChannelAID int

	// ForwardChannelAID should be set to the largest previously received forward
	// channel ID when computing the return value for LookupSession().
	ForwardChannelAID int
}

SessionInfo tracks the state related to which messages have been processed on the forward and backward channels.

type SessionManager

type SessionManager interface {
	// Lookup a previous session which is unknown to the server at this time.
	// This is useful to clients which store persistent session information
	// which survives across server restarts. When a requested SID cannot be
	// found, return ErrUnknownSID.
	LookupSession(r *http.Request, sid string) (Session, *SessionInfo, error)

	// NewSession creates a new WebChannel session. The returned Session object
	// must have SID() populated. Additionally, session persistent state should
	// be created as necessary. Do not add messages to the back channel from
	// this function, instead see BackChannelNewSessionMessages().
	NewSession(r *http.Request) (Session, error)

	// TerminatedSession notifies that the Session has been terminated (either
	// by the client or server).
	TerminatedSession(s Session, reason TerminationReason) error

	// Error logs internal failure conditions to application level code.
	Error(r *http.Request, err error)

	// Warning logs warning conditions to application level code.
	Warning(string)

	// Debug logs internal wc debugging messages. This is most useful to
	// developers of wc.
	Debug(string)

	// HostPrefix is used to circumvent same host connection limits.
	//
	// On the client, hostPrefix_ values will be passed to correctHostPrefix()
	// prior to use.
	//
	// WebChannel: https://github.com/google/closure-library/blob/master/closure/goog/labs/net/webchannel/webchannelbase.js#L151
	// BrowserChannel: https://github.com/google/closure-library/blob/master/closure/goog/net/browserchannel.js#L235
	//
	// The default, disabling the host prefix, is acceptable for most users. This
	// library does not support BlockedPrefix (used by BrowserChannel only).
	HostPrefix() string
}

SessionManager specifies the interface for the calling application to interact with newly created and resumed sessions. Multiple methods can be invoked concurrently from different goroutines (make sure to protect your state accordingly).

Some actions (eg: adding common HTTP headers, HTTP request logging, etc) should be performed by the application code outside of the wc Session{,Manager} interfaces. For an example of doing so, see the wcchat demo application.

type TerminationReason

type TerminationReason int

TerminationReason denotes the reason a session is being terminated.

const (
	// ClientTerminateRequest denotes the client explicitly requesting the
	// SID be terminated.
	ClientTerminateRequest TerminationReason = iota

	// ServerTerminateRequest denotes application code on the server requesting
	// the termination of the SID by sending a ServerTerminate event to the
	// Session Notifier().
	ServerTerminateRequest
)

Jump to

Keyboard shortcuts

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