gws

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2020 License: MIT Imports: 11 Imported by: 0

README

Go Report Card build

WIP: gws

A WebSocket client and server for GraphQL.

graphql-transport-ws implements Apollos' "GraphQL over WebSocket" protocol, which can be found in PROTOCOL.md. It provides an RPC-like API for both client and server usage.

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrStreamClosed = errors.New("gws: stream is closed")

ErrStreamClosed is returned by Send if the stream is closed before Send is called.

View Source
var ErrUnsubscribed = errors.New("gws: received cancelled due to unsubscribe")

ErrUnsubscribed is return by a subscription receive when the subscription is ubsubscribed or completed before the next response is received.

Functions

func NewHandler

func NewHandler(h Handler, opts ...ServerOption) http.Handler

NewHandler configures an http.Handler, which will upgrade incoming connections to WebSocket and serve the "graphql-ws" subprotocol.

Example
h := func(s *Stream, req *Request) error {
	// Remember to always close the stream when done sending.
	defer s.Close()

	// Use your choice of a GraphQL runtime to execute the query
	// Then, return the results JSON encoded with a *Response.
	r := &Response{
		Data: []byte(`{"hello":{"world":"this is example data"}}`),
	}
	return s.Send(context.TODO(), r)
}

// Simply register the handler with a http mux of your choice
// and it will handle the rest.
//
http.Handle("graphql", NewHandler(HandlerFunc(h)))

err := http.ListenAndServe(":80", nil)
if err != nil {
	// Always handle your errors
	return
}
Output:

Types

type Client

type Client interface {
	// Query provides an RPC like API for performing GraphQL queries.
	Query(context.Context, *Request) (*Response, error)

	// Subscribe provides an RPC like API for performing GraphQL subscription queries.
	Subscribe(context.Context, *Request) (*Subscription, error)
}

Client provides high-level API for making GraphQL requests over WebSocket.

func NewClient

func NewClient(conn *Conn) Client

NewClient takes a connection and initializes a client over it.

Example (Concurrent)
conn, err := Dial(context.TODO(), "ws://example.com")
if err != nil {
	// Make sure to handle the error
	return
}
defer conn.Close()

// Performing queries is completely concurrent safe.
client := NewClient(conn)

respCh := make(chan *Response)
go func() {
	resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"})
	if err != nil {
		// Remember, always handle errors
		return
	}
	respCh <- resp
}()

go func() {
	resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"})
	if err != nil {
		// Remember, always handle errors
		return
	}
	respCh <- resp
}()

for resp := range respCh {
	// Always check resp.Errors
	fmt.Println(resp)
}
Output:

Example (Query)
conn, err := Dial(context.TODO(), "ws://example.com")
if err != nil {
	// Make sure to handle the error
	return
}
defer conn.Close()

client := NewClient(conn)

resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"})
if err != nil {
	// Remember, always handle errors
	return
}
// Always check resp.Errors

var exampleResp struct {
	Hello struct {
		World string `json:"world"`
	} `json:"hello"`
}

err = json.Unmarshal(resp.Data, &exampleResp)
if err != nil {
	return
}

// Now, exampleResp.Hello.World would be your query result.
Output:

type CompressionMode

type CompressionMode websocket.CompressionMode

CompressionMode represents the modes available to the deflate extension. See https://tools.ietf.org/html/rfc7692

A compatibility layer is implemented for the older deflate-frame extension used by safari. See https://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06 It will work the same in every way except that we cannot signal to the peer we want to use no context takeover on our side, we can only signal that they should. It is however currently disabled due to Safari bugs. See https://github.com/nhooyr/websocket/issues/218

const (
	// CompressionNoContextTakeover grabs a new flate.Reader and flate.Writer as needed
	// for every message. This applies to both server and client side.
	//
	// This means less efficient compression as the sliding window from previous messages
	// will not be used but the memory overhead will be lower if the connections
	// are long lived and seldom used.
	//
	// The message will only be compressed if greater than 512 bytes.
	//
	CompressionNoContextTakeover CompressionMode = iota

	// CompressionContextTakeover uses a flate.Reader and flate.Writer per connection.
	// This enables reusing the sliding window from previous messages.
	// As most WebSocket protocols are repetitive, this can be very efficient.
	// It carries an overhead of 8 kB for every connection compared to CompressionNoContextTakeover.
	//
	// If the peer negotiates NoContextTakeover on the client or server side, it will be
	// used instead as this is required by the RFC.
	//
	CompressionContextTakeover

	// CompressionDisabled disables the deflate extension.
	//
	// Use this if you are using a predominantly binary protocol with very
	// little duplication in between messages or CPU and memory are more
	// important than bandwidth.
	//
	CompressionDisabled
)

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is a client connection that should be closed by the client.

func Dial

func Dial(ctx context.Context, endpoint string, opts ...DialOption) (*Conn, error)

Dial creates a connection to the given endpoint. By default, it's a non-blocking dial (the function won't wait for connections to be established, and connecting happens in the background).

Example
conn, err := Dial(context.TODO(), "ws://example.com")
if err != nil {
	// Make sure to handle the error
	return
}
defer conn.Close()

// Create a single client with the connection.
// There is no need to create multiple connections or clients
// because it will all be managed for you.
Output:

func (*Conn) Close

func (c *Conn) Close() error

Close closes the underlying WebSocket connection.

type ConnOption

type ConnOption interface {
	DialOption
	ServerOption
}

ConnOption represents a configuration that applies symmetrically on both sides, client and server.

func WithCompression

func WithCompression(mode CompressionMode, threshold int) ConnOption

WithCompression configures compression over the WebSocket. By default, compression is disabled and for now is considered an experimental feature.

type DialOption

type DialOption interface {
	SetDial(*dialOpts)
}

DialOption configures how we set up the connection.

func WithHTTPClient

func WithHTTPClient(client *http.Client) DialOption

WithHTTPClient provides an http.Client to override the default one used.

func WithHeaders

func WithHeaders(headers http.Header) DialOption

WithHeaders adds custom headers to every dial HTTP request.

type ErrIO

type ErrIO struct {
	// Msg
	Msg string

	// Err
	Err error
}

ErrIO represents a wrapped I/O error.

func (ErrIO) Error

func (e ErrIO) Error() string

Error implements the error interface.

func (ErrIO) Unwrap

func (e ErrIO) Unwrap() error

Unwrap is for the errors package to use within its As, Is, and Unwrap functions.

type ErrUnexpectedMessage

type ErrUnexpectedMessage struct {
	// Expected was the expected message type.
	Expected string

	// Received was the received message type.
	Received string
}

ErrUnexpectedMessage represents a unexpected message type.

func (ErrUnexpectedMessage) Error

func (e ErrUnexpectedMessage) Error() string

Error implements the error interface.

type Handler added in v0.3.0

type Handler interface {
	ServeGraphQL(*Stream, *Request) error
}

Handler is for handling incoming GraphQL queries. All other "GraphQL over Websocket" protocol messages are automatically handled internally.

All resolvers errors should be included in *Response and any validation error should be returned as error.

type HandlerFunc added in v0.3.0

type HandlerFunc func(*Stream, *Request) error

HandlerFunc

func (HandlerFunc) ServeGraphQL added in v0.3.0

func (f HandlerFunc) ServeGraphQL(s *Stream, req *Request) error

ServeGraphQL implements the Handler interface.

type Request

type Request struct {
	Query         string                 `json:"query"`
	Variables     map[string]interface{} `json:"variables"`
	OperationName string                 `json:"operationName"`
}

Request represents a payload sent from the client.

type Response

type Response struct {
	Data   json.RawMessage   `json:"data"`
	Errors []json.RawMessage `json:"errors"`
}

Response represents a payload returned from the server. It supports lazy decoding by leaving the inner data for the user to decode.

type ServerError

type ServerError struct {
	Msg string `json:"msg"`
}

ServerError represents a payload which is sent by the server if it encounters a non-GraphQL resolver error.

func (*ServerError) Error

func (e *ServerError) Error() string

Error implements the error interface.

type ServerOption

type ServerOption interface {
	SetServer(*options)
}

ServerOption allows the user to configure the handler.

func WithOrigins

func WithOrigins(origins ...string) ServerOption

WithOrigins lists the host patterns for authorized origins. The request host is always authorized. Use this to allow cross origin WebSockets.

type Stream added in v0.3.0

type Stream struct {
	// contains filtered or unexported fields
}

Stream is used for streaming responses back to the client.

func (*Stream) Close added in v0.3.0

func (s *Stream) Close() error

Close notifies the client that no more results will be sent and closes the stream. It also frees any resources associated with the stream, thus meaning Close should always be called to prevent any leaks.

func (*Stream) Send added in v0.3.0

func (s *Stream) Send(ctx context.Context, resp *Response) error

Send sends a response to the client. It is safe for concurrent use.

type Subscription added in v0.3.0

type Subscription struct {
	// contains filtered or unexported fields
}

Subscription represents a stream of results corresponding to a GraphQL subscription query.

func (*Subscription) Recv added in v0.3.0

func (s *Subscription) Recv(ctx context.Context) (*Response, error)

Recv is a blocking call which waits for either a response from the server or the context to be cancelled. Context cancellation does not cancel the subscription as a whole just the current recv call.

Recv is safe for concurrent use but it is a first come first serve basis. In other words, the response is not duplicated across all receivers.

func (*Subscription) Unsubscribe added in v0.3.0

func (s *Subscription) Unsubscribe() error

Unsubscribe tells the server to stop sending anymore results and cleans up any resources associated with the subscription.

Jump to

Keyboard shortcuts

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