pine

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2024 License: MIT Imports: 14 Imported by: 0

README

Pine

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

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 root 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 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","",""))
}

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 50.08 ms 1.328 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 background tasks managed by Pine's runtime scheduler
Background tasks example
package main

import (
    "log"
    "time"

    "github.com/BryanMwangi/pine"
)

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

	task := pine.BackgroundTask{
		Fn:   logHello,
		Time: 6 * time.Second,
	}
	task2 := pine.BackgroundTask{
		Fn:   logError,
		Time: 1 * time.Second,
	}
	task3 := pine.BackgroundTask{
		Fn:   logHello2,
		Time: 3 * time.Second,
	}
	//add the task to the queue
	//the queue can accept as many tasks as you want
	//however the queue size will impact the performance so be mindful and demure
	app.AddQueue(task, task2, task3)

	// 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
}

//returning an error will immediately stop the task and place it out of 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:

  • Websocket support
  • 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.
  • More background tasks with more sophisticated scheduling and handling

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 = 4 * 1024 * 1024
)

Variables

Default methods, more coming soon

Functions

func StatusMessage

func StatusMessage(status int) string

You can send just the status message here

Types

type BackgroundTask

type BackgroundTask struct {
	Fn   func() error
	Time time.Duration
	// contains filtered or unexported fields
}

This is the structure of a background task you can use this to put whatever tasks you want to perform in the background as the server runs and Pine will take care of executing them in the background

time is optional and defaults to 5 minutes according to the server configuration

Fn is the function that will be executed It should always return an error as the error is what will be used to delete the task from the queue

type Config

type Config struct {
	//defines the body limit for a request.
	// -1 will decline any body size
	//
	// Default: 4 * 1024 * 1024
	// Increase this to accept larger files
	BodyLimit int `json:"body_limit"`

	// The amount of time allowed to read the full request including body.
	// It is reset after the request handler has returned.
	// The connection's read deadline is reset when the connection opens.
	//
	// Default: unlimited
	ReadTimeout time.Duration `json:"read_timeout"`

	// The maximum duration before timing out writes of the response.
	// It is reset after the request handler has returned.
	//
	// Default: unlimited
	WriteTimeout time.Duration `json:"write_timeout"`

	//This is the periodic time in which the server can execute
	//background tasks background tasks can run infinitely
	//as long as the server is running
	//for example you can use this to make requests to other servers
	//or update your database
	//
	// Default: 5 minutes
	BackgroundTimeout time.Duration `json:"background_timeout"`

	// When set to true, disables keep-alive connections.
	// The server will close incoming connections after sending the first response to client.
	//
	// Default: false
	DisableKeepAlive bool `json:"disable_keep_alive"`

	JSONEncoder JSONMarshal `json:"-"`

	JSONDecoder JSONUnmarshal `json:"-"`
	// StreamRequestBody enables request body streaming,
	// and calls the handler sooner when given body is
	// larger then the current limit.
	StreamRequestBody bool

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

	// Client is used to make requests to other servers
	Client *http.Client
}

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) 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) 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

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 interface{}, value ...interface{}) interface{}

This can be used to set the local values of a request This is particulary usefule when unpacking data from a cookie Eg: You can parse a JWT token and decode the data inside it Then you can simply pack this data into the locals value of your request by doing c.Locals("key", data)

now whenever a request is made with that cookie if you set up your middleware to unpack the data in the locals field of your request you can access this data in your route

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

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) 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 helpers 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 with c.JSON(...) as the response will be sent as plain text

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 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 {

	//in case you want to serve https out of the box
	//you can set this to an empty string when you start a new server by
	//doing app:=pine.New(":3000","","")
	//this will default the server to http
	CertFile string

	//in case you want to serve https out of the box
	//you can set this to an empty string when you start a new server by
	//doing app:=pine.New(":3000","","")
	//this will default the server to http
	KeyFile string
	// 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) AddQueue

func (server *Server) AddQueue(tasks ...BackgroundTask)

AddQueue is used put some functions in a queue that can be executed in the background for a specified period of time This is particularly useful for making requests to other servers or for performing some other background task

You can add as many tasks as you want to the queue however the please be mindful of the queue size as it will impact the performance check out examples at https://github.com/BryanMwangi/pine/tree/main/Examples/BackgroundTask/main.go

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) Client

func (server *Server) Client() *http.Client

this is used to act make the server act as a client the server can be used to make requests to other servers

func (*Server) Delete

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

func (*Server) Get

func (server *Server) Get(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, CertFile, KeyFile 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

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