pine

package module
v1.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2024 License: MIT Imports: 17 Imported by: 0

README

Pine

Simple Go server framework built on the same concepts of ease of use such as Express JS

You can simply jump to the documentation

Getting Started

To get started you will need to import the go module by typing

go

go get github.com/BryanMwangi/pine

Quick start

Getting started with pine is easy. Here's a basic example to create a simple web server that responds with "Hello, World!" on the hello path.

package main

import (
    "log"

    "github.com/BryanMwangi/pine"
)

func main() {
    // Initialize a new Pine app
    app := pine.New()

    // Define a route for the GET method on the path '/hello'
    app.Get("/hello", func(c *pine.Ctx) error {
        return c.SendString("Hello World!")
    })

    // Start the server on port 3000
    log.Fatal(app.Start(":3000"))
}

Benchmarks

Pine is optimized for speed and performance while maintaining simplicity and ease of use. Being built on top of the standard http library, pine is able to handle a large number of requests with minimal overhead.

For the benchmarks we used oha. Since we are building alternatives for Express and Fiber, the benchmarks will only be against these 2 frameworks with more coming soon.

In the benchmark we tested a simple web server that responds with "pong" on the path "/ping". The benchmark was run on a MacBook Pro with a 2.9 GHz Intel Core i7 processor and 16 GB of RAM. Each server was sent 1,000,000 requests with a connection pool of 100.

Fun fact, by the time the Express benchmarks were finished, we had run the Pine and Fiber frameworks 5 times. The results were as follows:

Framework Requests/sec Avg Latency Slowest
Express 1966 1.328 ms 50.08 ms
Pine 77229 1.328 ms 19.07125 ms
Fiber 73959 1.302 ms 50.50235 ms

https://github.com/user-attachments/assets/a3ef09b1-4f2f-48e9-ae74-04a4bd47a95b

The results show that Pine is the fastest of the three frameworks tested. It is also the most performant of the three, with an average latency of 1.328 ms and a slowest latency of 19.07125 ms.

Limitations

  • No support for file uploads out of the box
  • No support for sessions out of the box

Advantages

  • Built on top of the standard net/http library. You can easily integrate pine with other features of the standard library without having to rewrite your code.
  • Built on top of the standard context.Context. This allows for easy integration with other libraries such as database connections.
  • Supports middleware
  • Out of the box support for helmet, cors and websockets
  • Supports cron jobs that are self managed by each Cron instance
Cron job example
package main

import (
    "log"
    "time"

    "github.com/BryanMwangi/pine"
	"github.com/BryanMwangi/pine/cron"
)

func main() {
	// Initialize a new Pine app
	app := pine.New()

	task := cron.Job{
		Fn:   logHello,
		Time: 6 * time.Second,
	}
	task2 := cron.Job{
		Fn:   logError,
		Time: 1 * time.Second,
	}
	task3 := cron.Job{
		Fn:   logHello2,
		Time: 3 * time.Second,
	}

	newCron := cron.New(cron.Config{
		RestartOnError: true,
		RetryAttempts:  3,
	})

	// There is no limit to the number of jobs you can add to the queue
	// However the queue size will impact the performance so be mindful and demure
	//
	// Also note that each task is executed in its own goroutine and performance
	// is relatively determined by the number of physical cores on your machine
	newCron.AddJobs(task, task2, task3)
	newCron.Start()

	// Define a route for the GET method on the root path '/hello'
	app.Get("/hello", func(c *pine.Ctx) error {
		return c.SendString("Hello World!")
	})

	// Start the server on port 3000
	log.Fatal(app.Start(":3000", "", ""))
}

func logHello() error {
	fmt.Println("Hello World!")
	return nil
}

// errors are handled internally depending on the restart policy of the cron
// If no restart policy is set, the error will cause the job to be removed
// immediately from the queue
func logError() error {
	return fmt.Errorf("Error")
}

func logHello2() error {
	fmt.Println("Another Hello World!")
	return nil
}

Roadmap

We aim to bring Pine to the same level as other popular frameworks. Some of the features we plan to add in the future include:

  • File upload support out of the box
  • Session support and pooling
  • Caching support
  • More middlewares out of the box such as CSRF, Rate Limiting, etc.

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are highly appreciated. This version of pine is still very beta and improvements are definetly on their way. If you find a feature missing on pine and would like to add to it, please feel free to open a PR and we can definetly work together on it.

License

Distributed under the MIT License.

Documentation

Index

Constants

View Source
const (
	MethodGet     = "GET"
	MethodPost    = "POST"
	MethodPut     = "PUT"
	MethodDelete  = "DELETE"
	MethodPatch   = "PATCH"
	MethodHead    = "HEAD"
	MethodOptions = "OPTIONS"
)

Acceptable methods these are the default at the moment, more coming soon

View Source
const (
	DefaultBodyLimit = 5 * 1024 * 1024 //5MB

)

Variables

View Source
var (
	ErrParse      = errors.New("bind: cannot parse")
	ErrConvert    = errors.New("bind: cannot convert")
	ErrType       = errors.New("bind: unexpected type")
	ErrPtr        = errors.New("bind: destination must be a pointer")
	ErrValidation = errors.New("bind: validation failed")
)
View Source
var (
	ErrURIRequired    = errors.New("uri is required")
	ErrMethodRequired = errors.New("method is required")
	ErrResponseIsNil  = errors.New("response is nil")
)

Common errors if you want to use the client and its methods

Default methods, more coming soon

View Source
var (
	ErrFileName = errors.New("could not determine file name")
)

Functions

func StatusMessage

func StatusMessage(status int) string

You can send just the status message here

Types

type Client added in v1.0.5

type Client struct {
	*http.Client
	// contains filtered or unexported fields
}

Client is a wrapper around the http.Client Has methods for setting the request URI, method, headers, and body

Ideally you want to use a client only once, however if you need to use multiple instances of the same client, start by creating a new client and then call the SetRequestURI, SetMethod, SetHeaders, and SetBody methods

Note that if you call SendRequest, the old response will be overwritten If you would like to store the response, call ReadResponse and store the response in a variable of your own choosing

func NewClient added in v1.0.5

func NewClient() *Client

Call this to create a new client You can then call SetRequestURI, SetMethod, SetHeaders, and SetBody after creating the client

func NewClientWithTimeout added in v1.0.5

func NewClientWithTimeout(timeout time.Duration) *Client

NewClientWithTimeout returns a new client with a timeout

func (*Client) ReadResponse added in v1.0.5

func (c *Client) ReadResponse() (code int, body []byte, err error)

Call this method to get the response from the request Note that after calling this method the old reponse will be discarded

Attempts to read the response after calling this method more than once will return an error

func (*Client) Request added in v1.0.5

func (c *Client) Request() *Request

func (*Client) SendRequest added in v1.0.5

func (c *Client) SendRequest() error

Call this method only if you have already set the request URI and method

body and headers are optional

It returns an error if the request URI or method is not set Please note that if you call SendRequest, the old response will be overwritten

func (*Client) SetTLSVerification added in v1.0.5

func (c *Client) SetTLSVerification(skip bool)

Use this method to skip TLS verification This can be useful if the api you are calling has outdated TLS certificates

type Config

type Config struct {
	//defines the body limit for a request.
	// -1 will decline any body size
	//
	// Default: 5 * 1024 * 1024
	// Increase this to accept larger files
	BodyLimit int64

	// Defines the amount of time allowed to read an incoming request.
	// This also includes the body.
	//
	// Default: 5 Seconds
	ReadTimeout time.Duration

	// Defines the maximum duration before timing out writes of the response.
	// Increase this to allow longer response times.
	//
	// Default: 5 Seconds
	WriteTimeout time.Duration

	// Closes incomming connections after sending the first response to client.
	// This is useful when you want to close connections after a specific route
	//
	// Default: false
	DisableKeepAlive bool

	// This defines the JSON encoder used by Pine for outgoing requests. The default is
	// JSONMarshal
	//
	// Allowing for flexibility in using another json library for encoding
	// Default: json.Marshal
	JSONEncoder JSONMarshal

	JSONDecoder JSONUnmarshal

	// RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish.
	//
	// Optional. Default: DefaultMethods
	RequestMethods []string

	// UploadPath is the path where uploaded files will be saved
	//
	// Default: ./uploads
	UploadPath string

	// TLSConfig is the configuration for TLS.
	TLSConfig TLSConfig
}

Config is a struct holding the server settings. TODO: More encoders and decoders coming soon

type Cookie struct {
	//Name of the cookie
	//
	//Required field
	Name string

	//what data is stored in the cookie
	//
	//Required field
	Value string

	//determines the path in which the cookie is supposed to be used on
	//you can set this to "/" so that every request will contain the cookie
	Path string

	//This allows the browser to associate your cookie with a specific domain
	//when set to example.com cookies from example.com will always be sent
	//with every request to example.com
	Domain string

	//Determines the specific time the cookie expires
	//Max age is more prefered than expires
	Expires time.Time

	//Also sets the expiry date and you can use a string here instead
	RawExpires string

	//Max-Age field of the cookie determines how long the cookie
	// stays in the browser before expiring
	//if you want the cookies to expire immediately such as when a user logs out
	//you can set this to -1
	//
	//accepts int value which should be the time in milliseconds you want
	//the cookie to be stored in the browser
	MaxAge int

	//A boolean value that determines whether cookies will be sent over https
	//or http only.
	//
	//Default is false and http can also send the cookies
	Secure bool

	//determines whether requests over http only can send the cookie
	HttpOnly bool

	//Cookies from the same domain can only be used on the specified domain
	//Eg: cookies from app.example.com can only be used by app.example.com
	//if you want all domains associated with example.com you can set this to
	//*.example.com
	//Now both app.example.com or dev.example.com can use the same cookie
	//
	//Options include the following:
	// 0 - SameSite=Lax
	// 1 - SameSite=Strict
	// 2 - SameSite=None
	//It will alwas default to Lax
	SameSite SameSite

	//All cookie data in string format. You do not need to set this
	//Pine can handle it for you
	Raw bool

	//Pine will also take care of this
	Unparsed []string
}

cookie struct that defines the structure of a cookie

type Ctx

type Ctx struct {
	Server   *Server                // Reference to *Server
	Method   string                 // HTTP method
	BaseURI  string                 // HTTP base uri
	Request  *http.Request          // HTTP request
	Response *responseWriterWrapper // HTTP response writer
	// contains filtered or unexported fields
}

func (*Ctx) BindJSON added in v1.0.5

func (c *Ctx) BindJSON(v interface{}) error

BindJSON binds the request body to the given interface. You can use this to validate the request body without adding further logic to your handlers.

Tested with nested JSON objects and arrays If you notice any errors, please open an issue on Github and I will fix it right away

func (*Ctx) BindParam added in v1.0.5

func (c *Ctx) BindParam(key string, v interface{}) error

BindParam binds the specified parameter value of a request.

func (*Ctx) BindQuery added in v1.0.5

func (c *Ctx) BindQuery(key string, v interface{}) error

BindQuery binds the specified query value of a request.

func (*Ctx) Context

func (c *Ctx) Context() context.Context

Context returns the context of the request (This is the same as c.Request.Context()) as it returns a http.Request.Context()

func (*Ctx) DeleteCookie

func (c *Ctx) DeleteCookie(names ...string) *Ctx

This function is used to delete cookies You can pass multiple names of cookies to be deleted at once

func (*Ctx) FormFile added in v1.0.7

func (c *Ctx) FormFile(key string) (multipart.File, *multipart.FileHeader, error)

func (*Ctx) Header

func (c *Ctx) Header(key string) string

This is used to retrieve the header value specified by the key This is particularly useful when you want to retrieve specific headers from a request such as the Authorization header

func (*Ctx) IP

func (c *Ctx) IP() string

Retrieves the IP address of the client

If you notice the IP address is sometimes different from the actual IP address please open an issue and we will look into it

func (*Ctx) JSON

func (c *Ctx) JSON(data interface{}, status ...int) error

JSON writes a JSON response If you would like to set the status code of the response, you can pass it as the second argument

If you notice using c.Status(http.StatusOk).JSON(...json_payload) is not working properly, you can simply use c.JSON(...json_payload) without specifying the status Default status code is 200

func (*Ctx) Locals

func (c *Ctx) Locals(key string, value ...interface{}) interface{}
app.Get("/helloYou", authmiddleware(func(c *pine.Ctx) error {
			user:=c.Locals("key")
			return c.SendString("hello"+  user.name)
	 }))

func (*Ctx) MultipartForm added in v1.0.7

func (c *Ctx) MultipartForm() *multipart.Form

func (*Ctx) MultipartFormValue added in v1.0.7

func (c *Ctx) MultipartFormValue(key string) string

func (*Ctx) MultipartReader added in v1.0.7

func (c *Ctx) MultipartReader(key string) (*multipart.Reader, error)

func (*Ctx) Next

func (c *Ctx) Next() error

Next is used to execute the next handler in the stack This is useful when you want to execute the next handler in the stack but you want to do some additional work before executing the next handler for example, you can use this to authenticate the user

func (*Ctx) Params

func (c *Ctx) Params(key string) string

used to extract params from a specified request Eg: app.Get("/hello/:user",func(c *Pine.Ctx)error{ user:=c.Params("user")

	return c.SendString("hello"+user)
})

func (*Ctx) ParamsInt

func (c *Ctx) ParamsInt(key string) (int, error)

Same as Params above but saves you the time of converting a string params to an int type and you can extract the int value directly returns the int and error if any you can use the error to send back http.StatusBadRequest or 400 to the user if the user send a params that is not an int type

func (*Ctx) Query

func (c *Ctx) Query(key string) string

used to obtain queries from requests EG: a user could send the request http://localhost:3000/hello?user=pine you can extract the user value by calling c.Query("user")

func (*Ctx) ReadCookie

func (c *Ctx) ReadCookie(name string) (*Cookie, error)

Used to read cookies with every request This is particularly useful for middlewares

func (*Ctx) SaveFile added in v1.0.7

func (c *Ctx) SaveFile(file multipart.File, fh *multipart.FileHeader) error

func (*Ctx) SendFile added in v1.0.7

func (c *Ctx) SendFile(filePath string) error

func (*Ctx) SendStatus

func (c *Ctx) SendStatus(status int) error

SendStatus sends a status code as the response Does not send any body Does not accept additional methods like c.Status(200).JSON(...)

func (*Ctx) SendString

func (c *Ctx) SendString(body string) error

SendString sends a string as the response Default status code is 200

func (*Ctx) Set

func (c *Ctx) Set(key string, val interface{}) *Ctx

func (*Ctx) SetCookie

func (c *Ctx) SetCookie(cookies ...Cookie) *Ctx

This is used to set cookies with the response you can set more than one cookie for example, a session token and a refresh token by calling this once

Make sure the structure of your cookie meets the Cookie structure to avoid errors

func (*Ctx) Status

func (c *Ctx) Status(status int) *Ctx

/You can use this to set the staus of a response Eg: c.Status(http.StatusOk) or c.Status(200)

Does not work well with c.JSON(...) as the response will be sent as plain text

func (*Ctx) StreamFile added in v1.0.7

func (c *Ctx) StreamFile(filePath string) error

type Handler

type Handler = func(*Ctx) error

type JSONMarshal

type JSONMarshal func(v interface{}) ([]byte, error)

type JSONUnmarshal

type JSONUnmarshal func(data []byte, v interface{}) error

type Middleware

type Middleware func(Handler) Handler

type Request added in v1.0.5

type Request struct {
	http.Request
	// contains filtered or unexported fields
}

func (*Request) JSON added in v1.0.5

func (r *Request) JSON(body interface{}) error

Sets the body of the request as JSON

func (*Request) SetHeaders added in v1.0.5

func (r *Request) SetHeaders(headers map[string]string)

Use this to set the headers of the request You can add as many headers as you want in a map

For example:

headers := map[string]string{
	"X-API-KEY": "1234567890",
}

request.SetHeaders(headers)

func (*Request) SetMethod added in v1.0.5

func (r *Request) SetMethod(method string) *Request

Use this to set the method of the request

For example: request.SetMethod("GET")

func (*Request) SetRequestURI added in v1.0.5

func (r *Request) SetRequestURI(uri string) *Request

Use this to set the url of the request

For example: request.SetRequestURI("https://example.com/api/v1/users")

type Route

type Route struct {
	// Public fields
	// HTTP method
	Method string `json:"method"`
	// Original registered route path
	Path string `json:"path"`
	// Ctx handlers
	Handlers []Handler `json:"-"`
}

Route is a struct that holds all metadata for each registered handler. TODO: More features coming soon

type SameSite

type SameSite int

type Server

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

func New

func New(config ...Config) *Server

This is called to start a new Pine server You can set the configuration as per your requirements or you can use the default and let Pine take care of it for you

func (*Server) AddRoute

func (server *Server) AddRoute(method, path string, handlers ...Handler)

This method is called to register routes and their respective methods it also accepts handlers in case you want to use specific middlewares for specific routes

func (*Server) Delete

func (server *Server) Delete(path string, handlers ...Handler)

func (*Server) Get

func (server *Server) Get(path string, handlers ...Handler)

func (*Server) Options added in v1.0.4

func (server *Server) Options(path string, handlers ...Handler)

func (*Server) Patch

func (server *Server) Patch(path string, handlers ...Handler)

func (*Server) Post

func (server *Server) Post(path string, handlers ...Handler)

func (*Server) Put

func (server *Server) Put(path string, handlers ...Handler)

func (*Server) ServeHTTP

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Server) ServeShutDown

func (server *Server) ServeShutDown(ctx context.Context, hooks ...func()) error

func (*Server) Start

func (server *Server) Start(address string) error

Called to start the server after creating a new server

You can put this in a go routine to handle graceful shut downs You can check out an example on https://github/BryanMwangi/pine/Examples/RunningInGoRoutine/main.go

func (*Server) Use

func (server *Server) Use(middleware Middleware)

Use method is for specifying middleware to be used on specific routes for example you could have an authentication middleware that checks for cookies with every request to authenticate the user request

type TLSConfig added in v1.0.5

type TLSConfig struct {
	ServeTLS bool
	CertFile string
	KeyFile  string
}

Directories

Path Synopsis
Examples
Pine's websocket package is a websocket server that supports multiple channels This feature is experimental and may change in the future.
Pine's websocket package is a websocket server that supports multiple channels This feature is experimental and may change in the future.

Jump to

Keyboard shortcuts

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