ghost

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2021 License: MIT Imports: 16 Imported by: 0

README

GHOST 👻

Version Reference License

A simple HTTP server framework in Go, without any third-party dependencies.

Use it ONLY when you seek for simpleness, and do not extremely eager for performance and robustness.

This framework is W.I.P., so the APIs are volatile, and there may be some unknown bugs in it.

Usage

All you need to do, is to make an interesting ghost, then run it.

package main

import (
    "github.com/deadblue/ghost"
    "github.com/deadblue/ghost/view"
)

type YourGhost struct{}

// Get will handle request "GET /"
func (g *YourGhost) Get(_ ghost.Context) (ghost.View, error) {
    return view.Text("You are here!"), nil
}

// GetIndexAsHtml will handle request "GET /index.html"
func (g *YourGhost) GetIndexAsHtml(_ ghost.Context) (ghost.View, error) {
    return view.Text("index.html"), nil
}

// GetDataById will handle request "GET /data/{id}", where the "id" is a path variable.
func (g *YourGhost) GetDataById(ctx ghost.Context) (ghost.View, error) {
    dataId := ctx.PathVar("id")
    return view.Text("Getting data whose id is " + dataId), nil
}

// PostUpdate will handle request "POST /update" 
func (g *YourGhost) PostUpdate(ctx ghost.Context) (ghost.View, error) {
    // Get post data from ctx.Request()
    return view.Text("Update done!"), nil
}

// BuyMeACoffee will handle request "BUY /me/a/coffee"
func (g *YourGhost) BuyMeACoffee(_ ghost.Context) (ghost.View, error) {
    return view.Text("Thank you!"), nil
}

// Implement ghost.Binder interface, to speed up the controller invoking.
func (g *YourGhost) Bind(v interface{}) ghost.Controller {
    if fn, ok := v.(func(*YourGhost, ghost.Context)(ghost.View, error)); ok {
        return func(ctx ghost.Context)(ghost.View, error) {
            return fn(g, ctx)
        }
    } else {
        return nil
    }
}

func main() {
    err := ghost.Born(&YourGhost{}).Run()
    if err != nil {
        panic(err)
    }
}

Mechanism

Each method on the ghost object, which is in form of ghost.Controller, will be mounted as a request handler.

The method name, will be translated as the mount path, following these rules:

  • Suppose the method name is in camel-case, split it into words.
  • The first word will be treated as request method.
  • If there is no remain words, the method function will handle the root request.
  • If there are remain words, each word will be treated as a path segment.
  • In remain words, there are some special words that won't be treated as path segment:
    • By: The next word will be treated as a path variable.
    • As: Link the next word with "." instead of path separator ("/").

For examples:

Method Name Handle Request
Get GET /
GetIndex GET /index
GetUserProfile GET /user/profile
PostUserProfile POST /user/profile
GetDataById GET /data/{id}
GetDataByTypeById GET /data/{type}/{id}
GetDataByIdAsJson GET /data/{id}.json
BuyMeACoffee BUY /me/a/coffee

Accessories

There are some optional interfaces, that developer can implement on his ghost, to make it more powerful:

  • Binder
  • StartupObserver
  • ShutdownObserver
  • StatusHandler

Please check the reference for detail.

Restrictions

According to the design and mechanism, GHOST has following restrictions:

  • GHOST can only handle the request in which each path segment is in lower case.
  • There can be only one path variable in each path segment.
  • The path variable can only consist of alphabet, numeric and underscore.

License

MIT

Documentation

Index

Constants

View Source
const (
	Version = "0.0.3"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Binder

type Binder interface {
	Bind(f interface{}) Controller
}

Binder is an optional but strongly recommended interface that need developer to implement on his ghost, that will speed up controller invoking.

See the benchmark for detail: https://gist.github.com/deadblue/b232340144acd20f48d38602fd628a1b#file-benchmark_test-go

A standard implementation looks like this:

func (g *YourGhost) Bind(f interface{}) ghost.Controller {
	if ctrl, ok := f.(func(*YourGhost, ghost.Context)(ghost.View, error)); ok {
		return func(ctx ghost.Context) (ghost.View, error) {
			return ctrl(g, ctx)
		}
	} else {
		return nil
	}
}

type Context

type Context interface {

	// Request returns the original HTTP request object.
	Request() *http.Request

	// Scheme returns request scheme(http/https).
	Scheme() string

	// Host returns request server host.
	Host() string

	// Method returns request method in upper-case.
	Method() string

	// Path returns request path.
	Path() string

	// RemoteIp returns the client IP.
	RemoteIp() string

	// PathVar return the variable value in request path.
	PathVar(name string) string

	// Header returns a value in request header with given name.
	Header(name string) string

	// HeaderArray returns all values in request header who has the given name.
	HeaderArray(name string) []string

	// Query returns the parameter value in query string who has the given name.
	Query(name string) string

	// QueryArray returns all parameter values in query string who has the given name.
	QueryArray(name string) []string

	// Cookie returns the cookie value who has the given name.
	Cookie(name string) string

	// CookieArray returns all cookie values who has the given name.
	CookieArray(name string) []string

	// Body return the request body, do not use it in individual goroutine,
	// because it will be closed after controller return.
	Body() io.Reader
}

Context describes the request context.

type Controller

type Controller func(ctx Context) (v View, err error)

Controller is a function to handle request.

type Shell

type Shell interface {

	// Startup starts up the shell manually, use this when you want to
	// control the shell lifecycle by yourself.
	// Otherwise, use Run instead.
	Startup() error

	// Shutdown shuts down the shell manually, use this when you want to
	// control the shell lifecycle by yourself.
	// Otherwise, use Run instead.
	Shutdown()

	// Done returns a read-only error channel, you will get notification
	// from it when the shell completely shutdown, use this when you
	// control the shell lifecycle by yourself.
	// Otherwise, use Run instead.
	Done() <-chan error

	// Run automatically runs the shell, and shutdown it when receive specific
	// OS signals, Run will exit after the shell completely shutdown.
	// If no signal specified, handles SIGINT and SIGTERM as default.
	Run(sig ...os.Signal) error
}

Shell is a lifeless object, until developer gives an interesting ghost to it.

func Born

func Born(ghost interface{}) Shell

Born creates a Shell with your ghost, and will listen at default network and address: "http://127.0.0.1:8066".

func BornAt

func BornAt(ghost interface{}, network, address string) Shell

BornAt creates a Shell with your ghost, and will listen at the network and address where you give.

type ShutdownObserver added in v0.0.2

type ShutdownObserver interface {

	// AfterShutdown will always be called after Shell completely shut down, even
	// Shell shut down with an error, developer can do finalizing works in it.
	// Currently, the returned error will be ignored.
	AfterShutdown() error
}

ShutdownObserver is an optional interface for developer's ghost.

type StartupObserver added in v0.0.2

type StartupObserver interface {

	// BeforeStartup will be called before Shell starts up, developer can do
	// initializing works in it. When BeforeStartup returns an error, the shell
	// won't start up, and return the error.
	BeforeStartup() error
}

StartupObserver is an optional interface for developer's ghost.

type StatusHandler added in v0.0.2

type StatusHandler interface {

	// OnStatus will be called when HTTP 40x and 50x error occurred.
	OnStatus(status int, ctx Context, err error) View
}

StatusHandler is an optional interface for developer's ghost, it allows developer to customize the view when HTTP 40x and 50x error occurred.

type View

type View interface {

	// HTTP status code
	Status() int

	// Response body
	Body() io.Reader
}

View describes the response.

type ViewHeaderInterceptor added in v0.0.2

type ViewHeaderInterceptor interface {

	// BeforeSendHeader will be called before kernel send the response headers to
	// client.
	// View can add/update/remove any headers in it.
	BeforeSendHeader(h http.Header)
}

ViewHeaderInterceptor is an optional interface for View. When a view implements it, kernel will pass response header to the view before send to client, view can manipulate the response header here.

type ViewSizeAdviser added in v0.0.2

type ViewSizeAdviser interface {

	// ContentLength returns the body size of the view, it will be set in response
	// header as "Content-Length", DO NOT return a incorrect value which is less or
	// more than the body size, that may cause some strange issues.
	ContentLength() int64
}

ViewSizeAdviser is an optional interface for View. Developer need not implement it when the view does not have a body, or the body is one of "bytes.Buffer", "bytes.Reader", "strings.Reader".

type ViewTypeAdviser added in v0.0.2

type ViewTypeAdviser interface {

	// ContentType returns the content type of the view, it will be set in response
	// header as "Content-Type".
	ContentType() string
}

ViewTypeAdviser is an optional interface for View. Developer need not implement it when the view does not have a body.

Directories

Path Synopsis
internal
package view provides most common-use ghost.View implementations for developers.
package view provides most common-use ghost.View implementations for developers.

Jump to

Keyboard shortcuts

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