Code Monkey home page Code Monkey logo

air's Introduction

Air

GitHub Actions codecov Go Report Card PkgGoDev

An ideally refined web framework for Go.

High-performance? Fastest? Almost all web frameworks are using these words to tell people that they are the best. Maybe they are, maybe not. Air does not intend to follow the crowd. Our goal is always to strive to make it easy for people to use Air to build their web applications. So, we can only guarantee you one thing: Air can serve properly.

Features

  • API
    • As less as possible
    • As clean as possible
    • As simple as possible
    • As expressive as possible
  • Server
    • HTTP/2 (h2 & h2c) support
    • SSL/TLS support
    • ACME support
    • PROXY (v1 & v2) support
    • Graceful shutdown support
  • Router
    • Based on the Radix Tree
    • Zero dynamic memory allocation
    • Blazing fast
    • Has a good inspection mechanism
    • Group routes support
  • Gas (aka middleware)
    • Router level:
      • Before router
      • After router
    • Route level
    • Group level
  • WebSocket
    • Full-duplex communication
  • Reverse proxy
    • Retrieves resources on behalf of a client from another server
    • Supported protocols:
      • HTTP
      • WebSocket
      • gRPC
  • Binder
    • Binds HTTP request body into the provided struct
    • Supported MIME types:
      • application/json
      • application/xml
      • application/protobuf
      • application/msgpack
      • application/toml
      • application/yaml
      • application/x-www-form-urlencoded
      • multipart/form-data
  • Renderer
    • Rich template functions
    • Hot update support
  • Minifier
    • Minifies HTTP response on the fly
    • Supported MIME types:
      • text/html
      • text/css
      • application/javascript
      • application/json
      • application/xml
      • image/svg+xml
  • Gzip
    • Compresses HTTP response by using the gzip
    • Default MIME types:
      • text/plain
      • text/html
      • text/css
      • application/javascript
      • application/json
      • application/xml
      • application/toml
      • application/yaml
      • image/svg+xml
  • Coffer
    • Accesses binary asset files by using the runtime memory
    • Significantly improves the performance of the air.Response.WriteFile
    • Asset file minimization and gzip support
    • Default asset file extensions:
      • .html
      • .css
      • .js
      • .json
      • .xml
      • .toml
      • .yaml
      • .yml
      • .svg
      • .jpg
      • .jpeg
      • .png
      • .gif
    • Hot update support
  • I18n
    • Adapt to the request's favorite conventions
    • Implanted into the air.Response.Render
    • Hot update support
  • Error
    • Centralized handling

Installation

Open your terminal and execute

$ go get github.com/aofei/air

done.

The only requirement is the Go, at least v1.13.

Hello, 世界

Create a file named hello.go

package main

import "github.com/aofei/air"

func main() {
	air.Default.GET("/", func(req *air.Request, res *air.Response) error {
		return res.WriteString("Hello, 世界")
	})
	air.Default.Serve()
}

and run it

$ go run hello.go

then visit http://localhost:8080.

Documentation

Does all web frameworks need to have a complicated (or a lovely but lengthy) website to guide people how to use them? Well, Air has only one Doc with useful comments. In fact, Air is so succinct that you don't need to understand how to use it through a large document.

Gases

As we all know that the air of Earth is a mixture of gases. So the same is that Air adopts the gas as its composition. Everyone can create new gas and use it within Air simply.

A gas is a function chained in the HTTP request-response cycle with access to the air.Request and air.Response which it uses to perform a specific action, for example, logging every request or recovering from panics.

return func(next air.Handler) air.Handler {
	return func(req *air.Request, res *air.Response) error {
		// Do something here...
		return next(req, res) // Execute the next handler
	}
}

If you already have some good HTTP middleware, you can simply wrap them into gases by calling the air.WrapHTTPMiddleware.

If you are looking for some useful gases, simply visit here.

Examples

If you want to be familiar with Air as soon as possible, simply visit here.

FAQ

Why named Air?

"A" for "An", "I" for "Ideally" and "R" for "Refined". So, Air.

Why based on the net/http?

In fact, I've tried to implement a full-featured HTTP server (just like the awesome valyala/fasthttp). But when I finished about half of the work, I suddenly realized: What about stability? What about those awesome middleware outside? And, seriously, what am I doing?

Why not just use the net/http?

Yeah, we can of course use the net/http directly, after all, it can meet many requirements. But, ummm... it's really too stable, isn't it? I mean, to ensure Go's backward compatibility (which is extremely necessary), we can't easily add some handy features to the net/http. And, the http.Request does not only represents the request received by the server, but also represents the request made by the client. In some cases it can be confusing. So why not just use the net/http as the underlying server, and then implement a refined web framework that are only used for the server-side on top of it?

Do you know we already got the gin-gonic/gin and labstack/echo?

Of course, I knew it when I started Go. And, I love both of them! But, why not try some new flavors? Are you sure you prefer them instead of Air? Don't even give Air a try? Wow... well, maybe Air is not for you. After all, it's for people who love to try new things. Relax and continue to maintain the status quo, you will be fine.

What about the fantastic Gorilla web toolkit?

Just call the air.WrapHTTPHandler and air.WrapHTTPMiddleware.

Is Air good enough?

Far from enough. But it's already working.

Community

If you want to discuss Air, or ask questions about it, simply post questions or ideas here.

Contributing

If you want to help build Air, simply follow this to send pull requests here.

License

This project is licensed under the MIT License.

License can be found here.

air's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

air's Issues

Code organization and API naming convention.

Writing Format

  1. 100 characters per line.
  2. Use <Tab> to indentation.
  3. A <Tab> occupies 8 spaces.
  4. Use // to comment.

Code Organization

  1. Use gofmt to format all go files before git commit.

Testing

  1. Use go test to test whether the Air can server properly.

Gzip Support and cache-control headers

It would be very nice to see a golang web framework handle gzip natively and do it well. There are various libraries and middlewares out there which purport a well adjusted and performant solution to serving gzipped content.

I've made a fairly primitive project some time ago, which provides an echo instance with the ability to read, monitor, cache and recache various static assets either within a specified folder or definitively located elsewhere; with this it would determine an asset's compress-ability and compress it thusly, serving it, thereby, only when a client's Accept-Encoding header indicates gzip support.

Desireable Features In a Gzipping Sollution:

  • - Cache Gzipped Assets:

    • In memory, with coffer potentially.
      • watched assets, should, on changes, (asynchronously) re-compress/update a memory/disk backed asset.ext(.gz)
    • On Disk, like some nginx/apache configs permit.
      • eg. route localhost/asset.txt -> first looks for - > ./assets/.cache/asset.txt.gz
    • Client Side, by setting and maintaining the correct cache-control and etag headers.
  • - Compress at the highest level when caching, but automatically scale down with dynamic content.


Air does not seem to handle caching headers, (to my knowledge, I may be wrong), as all my coffer cached assets seem to be missing their cache-control headers. While it would be possible to deliberately add such caching headers, it would certainly also have been nice had the air sub systems managed cache headers when it detected that the higher level user has not set any or has not deliberately disabled cache-control.

Cookies Go Missing when they're set before a redirect

air.GET("/auth/:verifier", func(req *air.Request, res *air.Response) error {
	user, err := VerifyUser(req.Param("verifier").Value().String())
	if err != nil || user == nil {
		if DevMode {
			fmt.Println("Unable to Authenticate user: ", err)
		}
		return UnauthorizedError.Send(res)
	}

	newtoken, err := GenerateAuthToken(user, false)
	if err == nil {
// This Cookie never reaches the client
		res.SetCookie("Auth", &air.Cookie{
			Value:    newtoken,
			Path:     "/",
			MaxAge:   60 * 60 * 24 * 7,
                        Domain: AppDomain,
			HTTPOnly: !DevMode,
			Secure:   !DevMode,
		})
	} else {
		if DevMode {
			fmt.Println("error verifying the user, GenerateAuthToken db problem: ", err)
		}
	}

	if user.isAdmin() {
		return res.Redirect("/admin")
	}
	return res.Redirect("/")
})

the problem is probably somewhere in res.Write, but it looks like the Header application step of serving is ignored when content io.ReadSeeker is nil, or something else is happening, not sure.
But I need cookies to set on redirects, it worked in echo, it should work here.

Add MsgPack support

vmihailenco/msgpack has a fairly decent and easy to use golang msgpack implementation it would be great to see native support in air, I've used echo and gin in the past and it would have been nice to see msgpack being handled natively the same way JSON is.

// WriteMsgPack responds to the client with the "application/msgpack" content v.
func (r *Response) WriteMsgPack(v interface{}) error {
	var (
		b   []byte
		err error
	)

	b, err = msgpack.Marshal(v)
	if err != nil {
		return err
	}

	r.Headers["content-type"] = &Header{
		Name:   "content-type",
		Values: []string{"application/msgpack"},
	}

	return r.WriteBlob(b)
}

Also maybe there is a way to stream json and msgpack.
Streaming might be more performant and memory efficient than writing blobs.
But I'm not sure how to do it right and handle stream failures properly.

I'm using these functions in my Gin application

// SendMsgpack send a msgpack encoded response with a status code
func SendMsgpack(c ctx, code int, msg interface{}) error {
	c.Status(code)
	c.Header("Content-Type", "application/msgpack")
	return msgpack.NewEncoder(c.Writer).Encode(msg)
}

// SendJSON send a json encoded response with a status code
func SendJSON(c ctx, code int, msg interface{}) error {
	c.Status(code)
	c.Header("Content-Type", "application/json")
	return json.NewEncoder(c.Writer).Encode(msg)
}

ps. air 很牛,真的,谢谢你创造它

How to write test becnhmark

func airHandler(req *air.Request, res *air.Response) error {
	return nil
}
func airHandlerWrite(req *air.Request, res *air.Response) error {
	// io.WriteString(res, s)
	return res.WriteString(req.Param("name").Value().String())
}
func airHandlerTest(req *air.Request, res *air.Response) error {
	return res.WriteString(req.Path)
}

// func (a *air.Air) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 	// a := air.New()
// 	a.server.ServeHTTP(r, w)
// }
func loadAir(routes []route) http.Handler {
	h := airHandler
	if loadTestHandler {
		h = airHandlerTest
	}
	app := air.New()
	for _, r := range routes {
		switch r.method {
		case "GET":
			app.GET(r.path, h)
		case "POST":
			app.POST(r.path, h)
		case "PUT":
			app.PUT(r.path, h)
		case "PATCH":
			app.PATCH(r.path, h)
		case "DELETE":
			app.DELETE(r.path, h)
		default:
			panic("Unknow HTTP method: " + r.method)
		}
	}
	return app
}
func loadAirSingle(method, path string, h air.Handler) http.Handler {

	app := air.New()
	// app.Middleware.Skip(nil, h)
	switch method {
	case "GET":
		app.GET(path, h)
	case "POST":
		app.POST(path, h)
	case "PUT":
		app.PUT(path, h)
	case "PATCH":
		app.PATCH(path, h)
	case "DELETE":
		app.DELETE(path, h)
	default:
		panic("Unknow HTTP method: " + method)
	}

	return app
}

Error

./router.go:557:6: cannot define new methods on non-local type air.Air
./router.go:559:3: a.server undefined (cannot refer to unexported field or method server)
./router.go:583:2: cannot use app (type *air.Air) as type http.Handler in return argument:
        *air.Air does not implement http.Handler (missing ServeHTTP method)
./router.go:604:2: cannot use app (type *air.Air) as type http.Handler in return argument:
        *air.Air does not implement http.Handler (missing ServeHTTP method)

AutoPush needs more control

the <link> tag has several variations which don't always need pushing for example with preload/favicon pushing can disturb caching and cause needless bandwidth consumption and sometimes in the worst cases duplicate requests, so there should be a bit more specificity and potentially a feature to exclude/omit certain paths from the push list, (immutable/async cached assets assets that are very big)

see transplacer's take on this matter.

Dependency Headaches: some things aren't importing for reasons unknown

Simply cannot import /text/langauge for some reason.

go version
     go version go1.11.2 windows/amd64

go get -u golang.org/x/text/language
     go: finding golang.org/x/text/language latest
     go get golang.org/x/text/language: no matching versions for query "latest"

on my DigitalOcean Debian server, there's been some trouble with these:

go: github.com/tdewolff/minify/[email protected]: go.mod has non-.../v2 module path "github.com/tdewolff/minify" (and .../v2/go.mod does not exist) at revision v2.3.7
go: error loading module requirements

though on my windows devbox it works fine.


on a side note, massive updates today, wow!
This project is growing and getting better everyday 😲, it's hard to keep up lol.
But it's all good, many of the changes in the last day have been breaking (to me at least), so
I'm going to stick to the vanilla version as it is and simply write my extentions
in an extention.go file as I see no reason to tediously duplicate all the awesome
progress and developments you're driving.

The only reason for my fork was to add a couple of convenience features, like:

  • native msgpack support in binder.go and response.go
  • req.Query("param") is a nice to have feature
  • .SetCookie(name, http.Cookie), or, gin style .SetCookie(name, all_the_params_laid_flat...)
  • req.Cookie(name) -> string, and, req.GetCookie(name) -> *Cookie
  • Coffer Gzip support, and Gzip support generally
  • Streaming, which got solved already, thanks 👍
  • Plain http middleware support
    • specifically: throttled, which could make for a nice gas
  • +Various Header methods that make life easier.
  • cache-control related concerns, autocert things
  • HostWhiteList stuff, redirecting localhost to the main domain when in debug mode which was annoying, I wish there was a toggle.

And I see you've added many of these features already, so I'm wondering if
perhaps we could reach consensus on some of these features so that they can
potentially be integrated and made official.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.