valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.95k stars 1.76k forks source link

How to hijack writer? #1474

Open ivanjaros opened 1 year ago

ivanjaros commented 1 year ago

In standard http i hijack the writer by wrapping it into my struct and log response data(method, path, code..) based on response codes. It does not seem that fasthttp has a way to set the response writer. Is that so or am I overlooking a method somewhere?

li-jin-gou commented 1 year ago

Maybe you can use this way wrapper and fasthttp and net/http are not the same here.

import (
    "flag"
    "fmt"
    "log"

    "github.com/valyala/fasthttp"
)

var addr = flag.String("addr", ":8080", "TCP address to listen to")

func main() {
    flag.Parse()

    h := requestHandler
    if err := fasthttp.ListenAndServe(*addr, CORS(h)); err != nil {
        log.Fatalf("Error in ListenAndServe: %v", err)
    }
}

func CORS(next fasthttp.RequestHandler) fasthttp.RequestHandler {
    return func(ctx *fasthttp.RequestCtx) {

        ctx.Response.Header.Set("Access-Control-Allow-Credentials", corsAllowCredentials)
        ctx.Response.Header.Set("Access-Control-Allow-Headers", corsAllowHeaders)
        ctx.Response.Header.Set("Access-Control-Allow-Methods", corsAllowMethods)
        ctx.Response.Header.Set("Access-Control-Allow-Origin", corsAllowOrigin)

        next(ctx)
    }
}

func requestHandler(ctx *fasthttp.RequestCtx) {
    fmt.Fprintf(ctx, "Hello, world!\n\n")

    fmt.Fprintf(ctx, "Request method is %q\n", ctx.Method())
    fmt.Fprintf(ctx, "RequestURI is %q\n", ctx.RequestURI())
    fmt.Fprintf(ctx, "Requested path is %q\n", ctx.Path())
    fmt.Fprintf(ctx, "Host is %q\n", ctx.Host())
    fmt.Fprintf(ctx, "Query string is %q\n", ctx.QueryArgs())
    fmt.Fprintf(ctx, "User-Agent is %q\n", ctx.UserAgent())
    fmt.Fprintf(ctx, "Connection has been established at %s\n", ctx.ConnTime())
    fmt.Fprintf(ctx, "Request has been started at %s\n", ctx.Time())
    fmt.Fprintf(ctx, "Serial request number for the current connection is %d\n", ctx.ConnRequestNum())
    fmt.Fprintf(ctx, "Your ip is %q\n\n", ctx.RemoteIP())

    fmt.Fprintf(ctx, "Raw request is:\n---CUT---\n%s\n---CUT---", &ctx.Request)

    ctx.SetContentType("text/plain; charset=utf8")

    // Set arbitrary headers
    ctx.Response.Header.Set("X-My-Header", "my-header-value")

    // Set cookies
    var c fasthttp.Cookie
    c.SetKey("cookie-name")
    c.SetValue("cookie-value")
    ctx.Response.Header.SetCookie(&c)
}
ivanjaros commented 1 year ago

That is not what I asked. I want to hijack Write() method, not add headers with wrappers. It's not just for logging but also for wrapping responses with etag handler that allows me to bypass handlers altogether and return http 304.

li-jin-gou commented 1 year ago

Tweaking the logic of the wrap function should work, the above is just a sample code. It can modify the body, as well as the status code, header.

alexdyukov commented 1 year ago

Not true in general

You can rewrite response writer after next call (see my example), but streams or early flush() breaks this logic and atm i dont know how to solve this issue

Could someone post an example with hijack writer to replace passwords by asterisks (*)?