TOC

Fasthttp 源码阅读

代码结构

https://github.com/valyala/fasthttp/tree/v1.51.0
https://github.com/valyala/fasthttp/archive/refs/tags/v1.51.0.zip

module github.com/valyala/fasthttp

go 1.20

require (
    github.com/andybalholm/brotli v1.0.5
    github.com/klauspost/compress v1.17.0
    github.com/valyala/bytebufferpool v1.0.0
    github.com/valyala/tcplisten v1.0.0
    golang.org/x/crypto v0.14.0
    golang.org/x/net v0.17.0
    golang.org/x/sys v0.13.0
)

require golang.org/x/text v0.13.0 // indirect
$ tree .
.
├── allocation_test.go
├── args.go
├── args_test.go
├── args_timing_test.go
├── b2s_new.go
├── b2s_old.go
├── brotli.go
├── brotli_test.go
├── bytesconv.go
├── bytesconv_32.go
├── bytesconv_32_test.go
├── bytesconv_64.go
├── bytesconv_64_test.go
├── bytesconv_table.go
├── bytesconv_table_gen.go
├── bytesconv_test.go
├── bytesconv_timing_test.go
├── client.go
├── client_example_test.go
├── client_test.go
├── client_timing_test.go
├── client_timing_wait_test.go
├── client_unix_test.go
├── coarsetime.go
├── coarsetime_test.go
├── compress.go
├── compress_test.go
├── cookie.go
├── cookie_test.go
├── cookie_timing_test.go
├── doc.go
├── examples
│   ├── client
│   │   ├── client.go
│   │   ├── Makefile
│   │   └── README.md
│   ├── fileserver
│   │   ├── fileserver.go
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── ssl-cert-snakeoil.key
│   │   └── ssl-cert-snakeoil.pem
│   ├── helloworldserver
│   │   ├── helloworldserver.go
│   │   ├── Makefile
│   │   └── README.md
│   ├── host_client
│   │   ├── hostclient.go
│   │   ├── Makefile
│   │   └── README.md
│   ├── letsencrypt
│   │   └── letsencryptserver.go
│   ├── multidomain
│   │   ├── Makefile
│   │   ├── multidomain.go
│   │   └── README.md
│   └── README.md
├── expvarhandler
│   ├── expvar.go
│   └── expvar_test.go
├── fasthttpadaptor
│   ├── adaptor.go
│   ├── adaptor_test.go
│   ├── b2s_new.go
│   ├── b2s_old.go
│   ├── request.go
│   └── request_test.go
├── fasthttpproxy
│   ├── http.go
│   ├── proxy_env.go
│   └── socks5.go
├── fasthttputil
│   ├── doc.go
│   ├── inmemory_listener.go
│   ├── inmemory_listener_test.go
│   ├── inmemory_listener_timing_test.go
│   ├── pipeconns.go
│   └── pipeconns_test.go
├── fs.go
├── fs_example_test.go
├── fs_fs_test.go
├── fs_handler_example_test.go
├── fs_test.go
├── fuzzit
│   ├── cookie
│   │   └── cookie_fuzz.go
│   ├── request
│   │   └── request_fuzz.go
│   ├── response
│   │   └── response_fuzz.go
│   └── url
│       └── url_fuzz.go
├── go.mod
├── go.sum
├── header.go
├── header_regression_test.go
├── header_test.go
├── header_timing_test.go
├── headers.go
├── http.go
├── http_test.go
├── lbclient.go
├── lbclient_example_test.go
├── LICENSE
├── methods.go
├── nocopy.go
├── peripconn.go
├── peripconn_test.go
├── pprofhandler
│   └── pprof.go
├── prefork
│   ├── prefork.go
│   ├── prefork_test.go
│   └── README.md
├── README.md
├── requestctx_setbodystreamwriter_example_test.go
├── reuseport
│   ├── LICENSE
│   ├── reuseport.go
│   ├── reuseport_aix.go
│   ├── reuseport_error.go
│   ├── reuseport_example_test.go
│   ├── reuseport_test.go
│   └── reuseport_windows.go
├── round2_32.go
├── round2_32_test.go
├── round2_64.go
├── round2_64_test.go
├── s2b_new.go
├── s2b_old.go
├── SECURITY.md
├── server.go
├── server_example_test.go
├── server_test.go
├── server_timing_test.go
├── stackless
│   ├── doc.go
│   ├── func.go
│   ├── func_test.go
│   ├── func_timing_test.go
│   ├── writer.go
│   └── writer_test.go
├── status.go
├── status_test.go
├── status_timing_test.go
├── stream.go
├── stream_test.go
├── stream_timing_test.go
├── streaming.go
├── streaming_test.go
├── strings.go
├── tcp.go
├── tcp_windows.go
├── tcpdialer.go
├── testdata
│   └── test.png
├── timer.go
├── tls.go
├── TODO
├── uri.go
├── uri_test.go
├── uri_timing_test.go
├── uri_unix.go
├── uri_windows.go
├── uri_windows_test.go
├── userdata.go
├── userdata_test.go
├── userdata_timing_test.go
├── workerpool.go
└── workerpool_test.go

21 directories, 149 files

Request / Response

type RequestCtx struct {
    noCopy           noCopy
    Request          Request
    Response         Response
    userValues       userData
    connID           uint64
    connRequestNum   uint64
    connTime         time.Time
    remoteAddr       net.Addr
    time             time.Time
    logger           ctxLogger
    s                *Server
    c                net.Conn
    fbr              firstByteReader
    timeoutResponse  *Response
    timeoutCh        chan struct{}
    timeoutTimer     *time.Timer
    hijackHandler    HijackHandler
    hijackNoResponse bool
    formValueFunc    FormValueFunc
}

type Request struct {
    noCopy                         noCopy
    Header                         RequestHeader
    uri                            URI
    postArgs                       Args
    bodyStream                     io.Reader
    w                              requestBodyWriter
    body                           *bytebufferpool.ByteBuffer
    bodyRaw                        []byte
    multipartForm                  *multipart.Form
    multipartFormBoundary          string
    secureErrorLogMessage          bool
    parsedURI                      bool
    parsedPostArgs                 bool
    keepBodyBuffer                 bool
    isTLS                          bool
    timeout                        time.Duration
    UseHostHeader                  bool
    DisableRedirectPathNormalizing bool
}

type RequestHeader struct {
    noCopy                noCopy
    disableNormalizing    bool
    noHTTP11              bool
    connectionClose       bool
    noDefaultContentType  bool
    disableSpecialHeader  bool
    cookiesCollected      bool
    contentLength         int
    contentLengthBytes    []byte
    secureErrorLogMessage bool
    method                []byte
    requestURI            []byte
    proto                 []byte
    host                  []byte
    contentType           []byte
    userAgent             []byte
    mulHeader             [][]byte
    h                     []argsKV
    trailer               []argsKV
    bufKV                 argsKV
    cookies               []argsKV
    rawHeaders            []byte
}

type Response struct {
    noCopy                noCopy
    Header                ResponseHeader
    ImmediateHeaderFlush  bool
    StreamBody            bool
    bodyStream            io.Reader
    w                     responseBodyWriter
    body                  *bytebufferpool.ByteBuffer
    bodyRaw               []byte
    SkipBody              bool
    keepBodyBuffer        bool
    secureErrorLogMessage bool
    raddr                 net.Addr
    laddr                 net.Addr
}
func requestHandler(ctx *fasthttp.RequestCtx) {
    // 获取 URL 参数
    queryArgs := ctx.QueryArgs()
    queryArgs.VisitAll(func(key, value []byte) {
        fmt.Printf("URL(querystring) %s: %s\n", key, value)
    })

    name := queryArgs.GetString("name", "default_value")
    fmt.Printf("name: %s\n", name)

    // 获取所有请求头
    ctx.Request.Header.VisitAll(func(key, value []byte) {
        fmt.Printf("Header %s: %s\n", key, value)
    })

    // 获取 Body 参数(表单参数)
    if ctx.IsPost() || ctx.IsPut() {
        fmt.Println("Form Parameters:")
        formArgs := ctx.FormArgs()
        formArgs.VisitAll(func(key, value []byte) {
            fmt.Printf("  %s: %s\n", key, value)
        })
    }

    // 获取 JSON 请求体中的参数(如果存在 JSON)
    if strings.Contains(string(ctx.Request.Header.ContentType()), "application/json") {
        var jsonBody map[string]interface{}
        if err := json.Unmarshal(ctx.Request.Body(), &jsonBody); err != nil {
            fmt.Println("Failed to parse JSON body:", err)
        } else {
            fmt.Println("JSON Body Parameters:")
            for key, value := range jsonBody {
                fmt.Printf("  %s: %v\n", key, value)
            }
        }
    }
}

// 封装获取客户端 IP 地址的逻辑
func getRemoteAddr(ctx *fasthttp.RequestCtx) string {
    // 首先检查 X-Forwarded-For 头部是否存在
    xForwardedFor := string(ctx.Request.Header.Peek("X-Forwarded-For"))
    if xForwardedFor != "" {
        // X-Forwarded-For 头部包含一个以逗号分隔的 IP 地址列表,取第一个(客户端真实 IP)
        clientIP := strings.Split(xForwardedFor, ",")[0]
        return clientIP
    }

    // 如果没有 X-Forwarded-For 头,则返回 RequestCtx 的 RemoteAddr(包含 IP 和端口)
    return ctx.RemoteAddr().String()
}

RequestHeader

通过复用 slice 来提升效率。

type Args struct {
    noCopy noCopy

    args []argsKV
    buf  []byte
}

type argsKV struct {
    key     []byte
    value   []byte
    noValue bool
}

func (a *Args) Add(key, value string) {
    a.args = appendArg(a.args, key, value, argsHasValue)
}

func appendArg(args []argsKV, key, value string, noValue bool) []argsKV {
    var kv *argsKV
    args, kv = allocArg(args)
    kv.key = append(kv.key[:0], key...)
    if noValue {
        kv.value = kv.value[:0]
    } else {
        kv.value = append(kv.value[:0], value...)
    }
    kv.noValue = noValue
    return args
}

func allocArg(h []argsKV) ([]argsKV, *argsKV) {
    n := len(h)
    if cap(h) > n {
        h = h[:n+1]
    } else {
        h = append(h, argsKV{
            value: []byte{},
        })
    }
    return h, &h[n]
}

func (a *Args) Reset() {
    a.args = a.args[:0]
}

参考资料与拓展阅读

如果你有魔法,你可以看到一个评论框~