netio

package module
v1.3.5 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 21 Imported by: 0

README

NetIO – Lightweight HTTP Library for Go

NetIO is a fast, minimalistic HTTP library built on top of native TCP connections. It provides flexible routing, middleware support, automatic JSON parsing, and efficient context management – all without external dependencies.

✨ Features

  • 🚀 High‑performance, zero‑dependency core
  • 🛣️ Path parameter routing (e.g., /users/:id)
  • 🔗 Global and per‑route middleware
  • 📦 Automatic JSON body parsing & responses
  • 🔍 Query string, header, and path parameter binding to structs
  • 📁 Multipart file upload support
  • 🌐 Built‑in CORS middleware
  • ⚙️ Configurable maximum request body size
  • 🧠 Optimised context pooling for low latency

📦 Installation

go get github.com/atendi9/netio

🚀 Quick Start

package main

import (
    "encoding/json"
    "log"

    "github.com/atendi9/netio"
    "github.com/atendi9/netio/cors"
)

func main() {
    app, err := netio.New(netio.AppConfig{
        Port:        "8080",
        AppName:     "myapp",
        MaxBodySize: "10 MB",
    })
    if err != nil {
        log.Fatal(err)
    }

    // Global middleware
    app.Use(cors.Middleware(cors.Config{
        AllowOrigins: []string{"*"},
    }))

    app.Use(func(c *netio.Context) {
        log.Printf("Method=%s Path=%s IP=%s", c.Method(), c.Path(), c.IP())
        c.Next()
    })

    // POST route
    app.POST("/", func(c *netio.Context) {
        body := c.Body()
        log.Println(string(body))
        c.JSON(map[string]any{"message": "Hello World"})
    })

    // GET route
    app.GET("/", func(c *netio.Context) {
        c.Send([]byte(`{"message":"Hello World"}`))
    })

    app.Listen()
}

⚙️ Configuration

AppConfig controls the server’s behaviour:

type AppConfig struct {
    Port        string      // listening port (required)
    AppName     string      // application name for logs (optional)
    MaxBodySize MaxBodySize // max body size, e.g., "15 MB", "500 KB"
}
  • MaxBodySize supports units: B, KB, MB, GB, TB. Default is "15 MB".

🛣️ Routing

NetIO supports the common HTTP methods:

  • GET
  • POST
  • PUT
  • DELETE
  • PATCH
Path Parameters

Parameters are defined with a colon prefix:

app.GET("/users/:id", func(c *netio.Context) {
    userID := c.Param("id")
    c.JSON(map[string]string{"user_id": userID})
})

app.GET("/users/:id/posts/:postId", func(c *netio.Context) {
    userID := c.Param("id")
    postID := c.Param("postId")
    c.JSON(map[string]string{
        "user_id": userID,
        "post_id": postID,
    })
})

🔧 Middleware

Global Middleware

Add middleware that runs for every request:

app.Use(func(c *netio.Context) {
    // before request
    log.Println("before")
    c.Next()
    // after request (if any)
    log.Println("after")
})
CORS Middleware

Import the cors subpackage:

import "github.com/atendi9/netio/cors"

app.Use(cors.Middleware(cors.Config{
    AllowOrigins:     []string{"http://localhost:3000"},
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
    AllowHeaders:     []string{"Content-Type", "Authorization"},
    ExposeHeaders:    []string{"Content-Length"},
    AllowCredentials: true,
}))

📝 The Context Object

The Context holds all request/response data and provides helper methods.

Request Data
// HTTP method
method := c.Method()

// Path (with optional default)
path := c.Path() // or c.Path("/fallback")

// Headers
allHeaders := c.Headers()
userAgent := c.Header("User-Agent")

// Raw body
body := c.Body() // []byte

// Client IP
ip := c.IP()
ips := c.IPs() // X-Forwarded-For + direct IP

// Query parameters
name := c.Query("name", "default")
c.QueryParser(&struct {
    Name string `query:"name"`
    Age  int    `query:"age"`
}{})
Path Parameters
// Direct access
userID := c.Param("id")

// Parse into struct
type UserParams struct {
    UserID string `param:"id"`
    PostID string `param:"postId"`
}
var params UserParams
c.ParamsParser(&params)
Request Body Parsing
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

var user User
if err := c.BodyParser(&user); err != nil {
    c.SendStatus(400)
    return
}
Header Parsing
type AuthHeaders struct {
    Authorization string `header:"Authorization"`
    XRequestID    string `header:"X-Request-ID"`
}

var headers AuthHeaders
c.ReqHeaderParser(&headers)
Responses
// Send JSON
c.JSON(map[string]string{"status": "ok"})

// Send raw bytes
c.Send([]byte("Hello"))

// Send only status code
c.SendStatus(204)

// Set response header
c.HeaderSet("X-Custom", "value")
File Upload
app.POST("/upload", func(c *netio.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.SendStatus(400)
        return
    }

    // *multipart.FileHeader
    log.Printf("Received: %s (%d bytes)", file.Filename, file.Size)

    c.JSON(map[string]string{"status": "uploaded"})
})

⚖️ Body Size Limiting

Set the maximum allowed request body size in AppConfig. Requests exceeding the limit receive a 413 Payload Too Large response.

app, _ := netio.New(netio.AppConfig{
    Port:        "8080",
    MaxBodySize: "10 MB", // default is "15 MB"
})

📊 Logging

On startup, NetIO logs the server address:

myapp ▷ http.server is running
myapp ▷ http://localhost:8080

🎯 Advanced Examples

Complete REST API
type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

// GET /users/:id
app.GET("/users/:id", func(c *netio.Context) {
    id := c.Param("id")
    user := User{ID: id, Name: "John", Age: 30}
    c.JSON(user)
})

// POST /users
app.POST("/users", func(c *netio.Context) {
    var user User
    if err := c.BodyParser(&user); err != nil {
        c.SendStatus(400)
        return
    }
    // store user...
    c.SendStatus(201)
})

// PUT /users/:id
app.PUT("/users/:id", func(c *netio.Context) {
    id := c.Param("id")
    var user User
    if err := c.BodyParser(&user); err != nil {
        c.SendStatus(400)
        return
    }
    user.ID = id
    c.JSON(user)
})

// DELETE /users/:id
app.DELETE("/users/:id", func(c *netio.Context) {
    id := c.Param("id")
    // delete user...
    c.JSON(map[string]string{"deleted": id})
})

📄 License

MIT License – see LICENSE for details.


Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDstMustBeAPointer    = errors.New("dst must be pointer")
	ErrUnsupportedFieldType = errors.New("unsupported field type")
)
View Source
var (
	ErrInvalidSize              = errors.New("invalid maxBodySize")
	ErrUnknownUnit              = errors.New("unknown unit")
	ErrInvalidMaxBodySizeFormat = errors.New("invalid format")
)
View Source
var ErrAborted = errors.New("aborted")
View Source
var ErrEmptyBody = errors.New("empty body")
View Source
var ErrFormFileNotFound = errors.New("form file not found")
View Source
var ErrInvalidCertKeyPaths = errors.New("certPath and keyPath must be provided")

ErrInvalidCertKeyPaths is returned when either the certificate or key path is empty.

Functions

This section is empty.

Types

type App

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

func New

func New(config AppConfig) (*App, error)

func (*App) DELETE

func (a *App) DELETE(path string, h ...Handler)

func (*App) GET

func (a *App) GET(path string, h ...Handler)

func (*App) Group added in v1.1.1

func (a *App) Group(basePath string, m ...Handler) Router

Group creates a new route group with a common base path and middleware.

All routes registered within this group will be prefixed with basePath, and the provided middlewares will be executed before the route handlers.

Groups can be nested, and child groups inherit both the path prefix and middleware stack from their parent.

func (*App) Listen

func (a *App) Listen() error

func (*App) ListenHTTPS added in v1.2.4

func (a *App) ListenHTTPS(certPath, keyPath string) error

ListenHTTPS starts an HTTPS server using the provided certificate and key files.

func (*App) PATCH

func (a *App) PATCH(path string, h ...Handler)

func (*App) POST

func (a *App) POST(path string, h ...Handler)

func (*App) PUT

func (a *App) PUT(path string, h ...Handler)

func (*App) ServeFiles added in v1.3.2

func (a *App) ServeFiles(endpoint, dirPath string) error

ServeFiles serves static files from the specified directory at the given endpoint. For example:

ServeFiles("/static/", "./public")

will serve files from the "./public" directory at the "/static/" endpoint.

func (*App) ServeHTTP added in v1.2.8

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP makes the app implement Go's http.Handler interface. This allows the app to be used in http.ListenAndServe.

func (*App) Shutdown added in v1.1.1

func (a *App) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server: closes the listener and waits for active connections to finish, respecting the context deadline.

func (*App) Use

func (a *App) Use(h Handler)

type AppConfig

type AppConfig struct {
	Port        string
	AppName     string
	MaxBodySize MaxBodySize
	Logger      Logger
	Startup     startFn
	// MaxConns caps the number of connections served concurrently.
	// A non-positive value falls back to defaultMaxConns.
	MaxConns int
}

type Context

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

func (*Context) Abort

func (c *Context) Abort()

func (*Context) Body

func (c *Context) Body() []byte

Body returns a copy of the request body.

A copy is returned (rather than the internal buffer) so a handler may safely retain the result — or hand it to a spawned goroutine — without risking mutation by request parsing. Header/Params/Query lookups already return string copies and are likewise safe to retain.

func (*Context) BodyParser

func (c *Context) BodyParser(v any) error

func (*Context) FormFile

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

func (*Context) Header

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

func (*Context) HeaderAppend added in v1.3.3

func (c *Context) HeaderAppend(key, value string)

func (*Context) HeaderSet

func (c *Context) HeaderSet(key, value string)

func (*Context) Headers

func (c *Context) Headers() map[string][]string

func (*Context) IP

func (c *Context) IP() string

func (*Context) IPs

func (c *Context) IPs() []string

func (*Context) JSON

func (c *Context) JSON(data any) error

func (*Context) Logger added in v1.3.5

func (c *Context) Logger() Logger

Logger returns the app-configured logger, falling back to a default one when the Context was built without an App. Never nil, so middlewares outside this package can report diagnostics through the same sink as the framework.

func (*Context) Method

func (c *Context) Method() string

func (*Context) Next

func (c *Context) Next() error

func (*Context) Now

func (c *Context) Now() time.Time

func (*Context) Param

func (c *Context) Param(key string) string

Param is an alias for Params without default value support.

func (*Context) Params

func (c *Context) Params(name string, defaultValue ...string) string

func (*Context) ParamsParser

func (c *Context) ParamsParser(v any) error

func (*Context) Path

func (c *Context) Path(defaultValue ...string) string

func (*Context) Query

func (c *Context) Query(name string, defaultValue ...string) string

func (*Context) QueryParser

func (c *Context) QueryParser(v any) error

func (*Context) ReqHeaderParser

func (c *Context) ReqHeaderParser(v any) error

func (*Context) Send

func (c *Context) Send(data []byte) error

func (*Context) SendFile added in v1.1.3

func (c *Context) SendFile(filePath string)

func (*Context) SendFileFromReader added in v1.1.3

func (c *Context) SendFileFromReader(r io.ReadCloser)

SendFileFromReader streams a file from a reader, writing HTTP headers first and using Transfer-Encoding: chunked to avoid buffering the entire payload.

func (*Context) SendStatus

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

func (*Context) Status added in v1.1.3

func (c *Context) Status(statusCode int) *Context

type Handler

type Handler func(c *Context)

Handler defines the signature for request handler functions that process a Context.

type KV

type KV struct {
	K []byte
	V []byte
}

type Logger added in v1.2.1

type Logger func(msg ...string)

func NewDefaultLogger added in v1.2.1

func NewDefaultLogger(appName string) Logger

type MaxBodySize

type MaxBodySize string

func (MaxBodySize) String

func (s MaxBodySize) String() string

type Router added in v1.1.1

type Router interface {
	Get(path string, h ...Handler)
	Post(path string, h ...Handler)
	Put(path string, h ...Handler)
	Delete(path string, h ...Handler)
	Patch(path string, h ...Handler)

	// Group creates a new Router with the given path prefix and optional middleware.
	//
	// The returned Router inherits the current base path and middleware stack,
	// allowing nested groups for better route organization.
	Group(path string, m ...Handler) Router
	Use(h ...Handler)
}

Router defines a contract for registering HTTP routes and creating route groups.

A Router allows attaching handlers to specific HTTP methods and paths. It also supports grouping routes under a common path prefix with shared middleware.

Implementations should ensure that middlewares defined in groups are executed before route-specific handlers.

Directories

Path Synopsis
e2e
cmd command

Jump to

Keyboard shortcuts

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