web

package
v0.0.0-...-9b564c9 Latest Latest
Warning

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

Go to latest
Published: Dec 27, 2020 License: MIT Imports: 23 Imported by: 1

README

go-serverapp/web

This repository contains the go-serverapp/web library.

To install:

go get github.com/ugorji/go-serverapp/web

Package Documentation

Package web is a very lightweight framework for web applications.

It adds clean seamless and easy to use support for:

  • access logging
  • pipelines
  • template management
  • shared storage
  • ...

TEMPLATES

Templates support optimized management of templates for the whole application.

The model is as below:

  • Each view page (e.g. landing page) corresponds to a given Template (set)
  • A config file defines the templates, so we can re-use templates
  • A Tree is used, so that a TemplateSet can be easily configured to share templates. E.g. All the core templates share the same header and footers.

Typical usage for loading from within main app

    vcn = new(web.ViewConfigNode)
    myVfs = new(util.VFS)
    err = myVfs.AddIfExist("templates.zip", "templates")
    f, err = os.Open("resources/core/web/views.json")
    err = json.NewDecoder(f).Decode(vcn)
    views = web.NewViews()
    views.FnMap["Eq"] = reflect.DeepEqual // MUST register functions BEFORE adding templates
    views.AddTemplates(myVfs, regexp.MustCompile(`.*\.thtml`)) // ...
    views.Load(vcn)

The View Handlers can call:

    views.Views["core.landing"].Execute(writer, "main", data)

WEBSTORE

WebStore allows us store information along with a namespace. It simply maintains a non-synchronized map[interface{}] map[string]interface{} for each namespace. It expects that the namespace represents usage within a single goroutine.

An example usage is within a web environment where the namespace can be the *http.Request object.

It is generally recommended to pass structures around in function calls. However, there may be times where using this is the only option, and in those situations, use with care.

It can be used in a web environment, where you want to store data on behalf of a request. In this context, the namespace is the request.

Exported Package API

const FlashMessage = "FlashMessage"
const MinMimeSniffLen = 64
var ClosedErr = errors.New("<closed>")
func AddHandlerMessages(r *http.Request, w http.ResponseWriter, ckName string, ...) (err error)
func NewCookie(host, name, value string, ttlsec int, encode bool) *http.Cookie
func NewGzipWriterPool(level, initPoolLen, poolCap int) *pool.T
type AccessLogger struct{ ... }
    func NewAccessLogger(filename string) *AccessLogger
type BufferPipe struct{ ... }
    func NewBufferPipe(size, initPoolLen, poolCap int) (s *BufferPipe)
type FlusherPipe struct{}
type GzipPipe struct{ ... }
    func NewGzipPipe(level, initPoolLen, poolCap int) (s *GzipPipe)
type HTTPServer struct{ ... }
type HandlerMessage struct{ ... }
type HttpHandlerPipe struct{ ... }
type Listener struct{ ... }
    func NewListener(l net.Listener, maxNumConn int32, panicFlags OnPanicFlags) (s *Listener)
type OnPanicFlags uint8
    const OnPanicRecover OnPanicFlags = 1 << iota ...
type Pipe interface{ ... }
type Pipeline struct{ ... }
    func NewPipeline(pipes ...Pipe) *Pipeline
type ResponseWriter interface{ ... }
    func AsResponseWriter(w http.ResponseWriter) ResponseWriter
type ViewConfigNode struct{ ... }
type Views struct{ ... }
    func NewViews() *Views
type WebStore struct{ ... }
    func NewWebStore() *WebStore

Documentation

Overview

Package web is a very lightweight framework for web applications.

It adds clean seamless and easy to use support for:

  • access logging
  • pipelines
  • template management
  • shared storage
  • ...

TEMPLATES

Templates support optimized management of templates for the whole application.

The model is as below:

  • Each view page (e.g. landing page) corresponds to a given Template (set)
  • A config file defines the templates, so we can re-use templates
  • A Tree is used, so that a TemplateSet can be easily configured to share templates. E.g. All the core templates share the same header and footers.

Typical usage for loading from within main app

vcn = new(web.ViewConfigNode)
myVfs = new(util.VFS)
err = myVfs.AddIfExist("templates.zip", "templates")
f, err = os.Open("resources/core/web/views.json")
err = json.NewDecoder(f).Decode(vcn)
views = web.NewViews()
views.FnMap["Eq"] = reflect.DeepEqual // MUST register functions BEFORE adding templates
views.AddTemplates(myVfs, regexp.MustCompile(`.*\.thtml`)) // ...
views.Load(vcn)

The View Handlers can call:

views.Views["core.landing"].Execute(writer, "main", data)

WEBSTORE

WebStore allows us store information along with a namespace. It simply maintains a non-synchronized map[interface{}] map[string]interface{} for each namespace. It expects that the namespace represents usage within a single goroutine.

An example usage is within a web environment where the namespace can be the *http.Request object.

It is generally recommended to pass structures around in function calls. However, there may be times where using this is the only option, and in those situations, use with care.

It can be used in a web environment, where you want to store data on behalf of a request. In this context, the namespace is the request.

Index

Constants

View Source
const FlashMessage = "FlashMessage"
View Source
const MinMimeSniffLen = 64 // 512 is default

Variables

View Source
var ClosedErr = errors.New("<closed>")

Functions

func AddHandlerMessages

func AddHandlerMessages(r *http.Request, w http.ResponseWriter,
	ckName string, messages ...HandlerMessage,
) (err error)

func NewCookie

func NewCookie(host, name, value string, ttlsec int, encode bool) *http.Cookie

Returns a new cookie. Note: NewCookie sets the domain from the Host if possible (as an appropriate .XYZ.tld) The cookie domain is only set if the app Host is not localhost, and is a real Hostname.

func NewGzipWriterPool

func NewGzipWriterPool(level, initPoolLen, poolCap int) *pool.T

Types

type AccessLogger

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

AccessLogger handles access logging, including opening/closing files and buffering output. It also acts as a pipe, so it can participate in the execution of a request.

func NewAccessLogger

func NewAccessLogger(filename string) *AccessLogger

func (*AccessLogger) Close

func (s *AccessLogger) Close() (err error)

func (*AccessLogger) Reopen

func (s *AccessLogger) Reopen() (err error)

func (*AccessLogger) Reset

func (s *AccessLogger) Reset(name string) (err error)

func (*AccessLogger) ServeHttpPipe

func (s *AccessLogger) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

log access in the combined log format.

type BufferPipe

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

BufferPipe allows you use a Buffer, especially close to the lowest handler, to ensure that handlers up the chain do not get results in tiny quantities.

Templates may write out results in bytes of length 1, 2, 17, etc. Some pipes may need to determine the content type before converting the result (e.g. gzip pipe). BufferPipe helps here.

func NewBufferPipe

func NewBufferPipe(size, initPoolLen, poolCap int) (s *BufferPipe)

func (*BufferPipe) ServeHttpPipe

func (s *BufferPipe) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

type FlusherPipe

type FlusherPipe struct{}

Flusher pipe just does a flush after calling Next. May be a good candidate to put on the end of the pipeline closest. This way, it calls Flush across the whole pipeline chain.

func (FlusherPipe) ServeHttpPipe

func (s FlusherPipe) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

type GzipPipe

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

GzipPipe will convert http responseWriter to write out the compressed bits if accept-encoding contains gzip, and deciphered content-type is text/* or matches typical text types (xml, html, json, javascript, css).

func NewGzipPipe

func NewGzipPipe(level, initPoolLen, poolCap int) (s *GzipPipe)

func (*GzipPipe) ServeHttpPipe

func (s *GzipPipe) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

type HTTPServer

type HTTPServer struct {
	Pipes []Pipe
	*Listener
	// contains filtered or unexported fields
}

Server implements net.Listener and http.Handler.

Typical Usage:

l, err = net.Listen("tcp", ":8080")
lis = web.NewListener(l, 1000, OnPanicRecover)
httpWebSvr = web.NewServer(lis)
httpWebSvr.AddPipe(web.HttpHandlerPipe{myHandler})
http.Serve(httpWebSvr, httpWebSvr)

func (*HTTPServer) ServeHTTP

func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves the request as a pipeline.

func (*HTTPServer) ServeHttpPipe

func (s *HTTPServer) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

type HandlerMessage

type HandlerMessage struct {
	Text  string
	Error bool
}

type HttpHandlerPipe

type HttpHandlerPipe struct {
	http.Handler
}

Wraps a standard http.Handler into a Pipe. It calls its ServeHTTP, and then calls pipeline's next.

func (HttpHandlerPipe) ServeHttpPipe

func (h HttpHandlerPipe) ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)

ServeHttpPipe calls the wrapped Handlers ServeHTTP, calls pipeline's next.

type Listener

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

Listener implements net.Listener

It keeps track of connections doing work. It will pause accepting new connections if maxConn is above a given max, and resume accepting when below.

It will also close gracefully, giving in-flight requests time to shutdown gracefully.

Closing the listener also returns an appropriate error (ClosedErr) so that users know that it was closed gracefully.

func NewListener

func NewListener(l net.Listener, maxNumConn int32, panicFlags OnPanicFlags) (s *Listener)

Return a new listener.

func (*Listener) Accept

func (s *Listener) Accept() (c net.Conn, err error)

func (*Listener) Addr

func (s *Listener) Addr() net.Addr

func (*Listener) Close

func (s *Listener) Close() (err error)

func (*Listener) HardPause

func (s *Listener) HardPause()

func (*Listener) IsClosed

func (s *Listener) IsClosed() bool

func (*Listener) ResetMaxNumConn

func (s *Listener) ResetMaxNumConn(maxNumConn int32)

func (*Listener) ResumeFromHardPause

func (s *Listener) ResumeFromHardPause()

func (*Listener) Run

func (s *Listener) Run(onClosed, onPaused, onRun func())

func (*Listener) WaitZeroInflight

func (s *Listener) WaitZeroInflight()

type OnPanicFlags

type OnPanicFlags uint8
const (
	OnPanicRecover OnPanicFlags = 1 << iota
	OnPanicLogStack
	OnPanicLog

	OnPanicAll = 0xff // convenience all flags set
)

type Pipe

type Pipe interface {
	ServeHttpPipe(w ResponseWriter, r *http.Request, f *Pipeline)
}

Pipe is a step in handling a web request. A Pipe can do some work, call the next pipe in the chain, and finish up its work when that returns.

Note that Pipes should still be smart about when to call Flush. The general guideline is that Pipes that do internally buffering should call Flush at the end of their ServeHttpPipe (after calling Pipeline.Next).

type Pipeline

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

Pipeline contains a sequence of pipes, which work together to handle a request. You can have an accesslogger, security checker, gzipper, etc in the pipeline.

func NewPipeline

func NewPipeline(pipes ...Pipe) *Pipeline

func (*Pipeline) Next

func (p *Pipeline) Next(w ResponseWriter, r *http.Request)

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	http.CloseNotifier
	http.Flusher
	http.Hijacker
	// Write(b []byte) (i int, err error)
	// Header() http.Header
	// WriteHeader(code int)
	// Flush()
	// CloseNotify() <-chan bool
	// Hijack() (net.Conn, *bufio.ReadWriter, error)
	IsHeaderWritten() bool
	NumBytesWritten() int64
	ResponseCode() int
}

ResponseWriter is a fat interface encompassing http.ResponseWriter, and adding all net/http responsewriter interfaces (Hijacker, Flusher, CloseNotifier). This is done so that all pipe writers can be hijacked, flushed, etc. It also adds some tracking methods.

func AsResponseWriter

func AsResponseWriter(w http.ResponseWriter) ResponseWriter

type ViewConfigNode

type ViewConfigNode struct {
	Name     string
	AliasTo  string
	Parent   *ViewConfigNode
	Children []*ViewConfigNode
	Views    map[string]string
}

A node that is parseable from an external source, that defines a tree of configured views. Each view is eventually used to construct a template.Set

type Views

type Views struct {
	FnMap template.FuncMap
	Views map[string]*template.Template
	// contains filtered or unexported fields
}

Manage all the template sets for the application. Web Handlers should call TemplateSet.Execute(...) to render their content. Note that we really share templates (ie by sharing the actual parse trees).

func NewViews

func NewViews() *Views

func (*Views) AddTemplates

func (views *Views) AddTemplates(vfs *vfs.Vfs, r *regexp.Regexp) (err error)

An app can call this function multiple times passing a VFS and a regexp. It will Load all the templates matching that regexp, and parse them. (We need to add funcMap so that we can parse/find functions defined at parse time, etc).

func (*Views) Load

func (views *Views) Load(vcn *ViewConfigNode) (err error)

This is called after templates have been found. Using the map, it will create TemplateSets for each view (sharing templates performantly)

type WebStore

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

func NewWebStore

func NewWebStore() *WebStore

func (*WebStore) Clear

func (rc *WebStore) Clear(ns interface{})

Ensure this clear method is called at the appropriate time to clear out the namespace entries.

For example, in a web context, where information is stored on behalf of the request, ensure this method is called at the end of the request.

func (*WebStore) Get

func (rc *WebStore) Get(ns interface{}, key string) interface{}

func (*WebStore) Put

func (rc *WebStore) Put(ns interface{}, key string, val interface{})

Jump to

Keyboard shortcuts

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