[server] Try to optimize memory allocations for FastHTTP to Go HTTP bridging (it involves heavy "magic").
This commit is contained in:
parent
e817765cfd
commit
01586f506d
3 changed files with 125 additions and 11 deletions
|
@ -503,27 +503,29 @@ func (_server *server) ServeHTTP (_response http.ResponseWriter, _request *http.
|
|||
|
||||
_server.Serve (_context)
|
||||
|
||||
_responseHeaders := _response.Header ()
|
||||
_responseHeaders["Date"] = nil
|
||||
_responseHeaders := HttpResponseWriterHeaderDoMagic (_response)
|
||||
_context.Response.Header.VisitAll (
|
||||
func (_key_0 []byte, _value_0 []byte) () {
|
||||
switch string (_key_0) {
|
||||
case "Connection", "Content-Length", "Date" :
|
||||
// NOP
|
||||
default :
|
||||
_key := string (_key_0)
|
||||
_value := string (_value_0)
|
||||
_responseHeaders[_key] = append (_responseHeaders[_key], _value)
|
||||
_key := CanonicalHeaderNameFromBytes (_key_0)
|
||||
if _values, _ := _responseHeaders[_key]; _values == nil {
|
||||
_values = CanonicalHeaderValueArrayFromBytes (_value_0)
|
||||
_responseHeaders[_key] = _values
|
||||
} else {
|
||||
_value := CanonicalHeaderValueFromBytes (_value_0)
|
||||
_values = append (_values, _value)
|
||||
_responseHeaders[_key] = _values
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_responseBody := _context.Response.Body ()
|
||||
if len (_responseBody) > 0 {
|
||||
_responseHeaders["Content-Length"] = []string { fmt.Sprintf ("%d", len (_responseBody)) }
|
||||
}
|
||||
_responseHeaders["Date"] = nil
|
||||
|
||||
_response.WriteHeader (_context.Response.Header.StatusCode ())
|
||||
_response.Write (_responseBody)
|
||||
|
||||
_response.Write (_context.Response.Body ())
|
||||
}
|
||||
|
||||
|
||||
|
@ -724,6 +726,7 @@ func main_0 () (error) {
|
|||
} else {
|
||||
AbortError (nil, "[1a5476b1] HTTP/3 Alt-Svc is invalid!")
|
||||
}
|
||||
CanonicalHeaderValueRegister (_http3AltSvc)
|
||||
}
|
||||
|
||||
if !_dummy {
|
||||
|
|
98
sources/lib/server/headers-magic.go
Normal file
98
sources/lib/server/headers-magic.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
|
||||
|
||||
package server
|
||||
|
||||
|
||||
import "fmt"
|
||||
import "net/http"
|
||||
import "log"
|
||||
import "reflect"
|
||||
import "sync/atomic"
|
||||
import "unsafe"
|
||||
|
||||
|
||||
|
||||
|
||||
func HttpResponseWriterHeaderDoMagic (_response http.ResponseWriter) (http.Header) {
|
||||
|
||||
|
||||
if !_httpResponseWriteHeaderMagic_enabled {
|
||||
return _response.Header ()
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Because we don't modify the headers after calling `WriteHeader`,
|
||||
// the following code tricks `net/http` into believing it didn't gave us the headers.
|
||||
// This eliminates the `http.Header.Clone()` call on `WriteHeader`.
|
||||
|
||||
_responseReflect := reflect.ValueOf (_response) .Elem ()
|
||||
_responseAddress := unsafe.Pointer (_responseReflect.UnsafeAddr ())
|
||||
_responseType := _responseReflect.Type ()
|
||||
_responsePackage := _responseType.PkgPath ()
|
||||
_responseTypeName := _responseType.Name ()
|
||||
|
||||
var _header http.Header
|
||||
|
||||
switch {
|
||||
|
||||
case (_responsePackage == "net/http") && (_responseTypeName == "response") : {
|
||||
|
||||
_handlerHeaderOffset := atomic.LoadInt32 (&_httpResponseWriteHeaderMagic_netHttp_handlerHeaderOffset)
|
||||
_cwHeaderOffset := atomic.LoadInt32 (&_httpResponseWriteHeaderMagic_netHttp_cwHeaderOffset)
|
||||
|
||||
if (_handlerHeaderOffset == 0) || (_cwHeaderOffset == 0) {
|
||||
|
||||
_handlerHeaderReflect := _responseReflect.FieldByName ("handlerHeader")
|
||||
_handlerHeaderAddress := unsafe.Pointer (_handlerHeaderReflect.UnsafeAddr ())
|
||||
|
||||
_cwHeaderReflect := _responseReflect.FieldByName ("cw") .FieldByName ("header")
|
||||
_cwHeaderAddress := unsafe.Pointer (_cwHeaderReflect.UnsafeAddr ())
|
||||
|
||||
_handlerHeaderOffset = int32 (int64 (uintptr (_handlerHeaderAddress)) - int64 (uintptr (_responseAddress)))
|
||||
_cwHeaderOffset = int32 (int64 (uintptr (_cwHeaderAddress)) - int64 (uintptr (_responseAddress)))
|
||||
|
||||
atomic.StoreInt32 (&_httpResponseWriteHeaderMagic_netHttp_handlerHeaderOffset, _handlerHeaderOffset)
|
||||
atomic.StoreInt32 (&_httpResponseWriteHeaderMagic_netHttp_cwHeaderOffset, _cwHeaderOffset)
|
||||
}
|
||||
|
||||
_handlerHeaderValue := (*http.Header) (unsafe.Add (_responseAddress, _handlerHeaderOffset))
|
||||
_cwHeaderValue := (*http.Header) (unsafe.Add (_responseAddress, _cwHeaderOffset))
|
||||
|
||||
_header = make (map[string][]string, 16)
|
||||
|
||||
*_handlerHeaderValue = _header
|
||||
*_cwHeaderValue = _header
|
||||
}
|
||||
|
||||
case (_responsePackage == "net/http") && (_responseTypeName == "http2responseWriter") : {
|
||||
|
||||
_header = _response.Header ()
|
||||
}
|
||||
|
||||
case (_responsePackage == "github.com/lucas-clemente/quic-go/http3") && (_responseTypeName == "responseWriter") : {
|
||||
|
||||
_header = _response.Header ()
|
||||
}
|
||||
|
||||
default : {
|
||||
|
||||
log.Printf ("[ee] [64583df9] unsupported HTTP ResponseWriter `%s.%s`!\n", _responsePackage, _responseTypeName)
|
||||
|
||||
if _httpResponseWriteHeaderMagic_panic {
|
||||
panic (fmt.Sprintf ("[09274c17] unsupported HTTP ResponseWriter `%s.%s`!", _responsePackage, _responseTypeName))
|
||||
}
|
||||
|
||||
_header = _response.Header ()
|
||||
}
|
||||
}
|
||||
|
||||
return _header
|
||||
}
|
||||
|
||||
|
||||
var _httpResponseWriteHeaderMagic_enabled = true
|
||||
var _httpResponseWriteHeaderMagic_panic = true
|
||||
|
||||
var _httpResponseWriteHeaderMagic_netHttp_handlerHeaderOffset int32
|
||||
var _httpResponseWriteHeaderMagic_netHttp_cwHeaderOffset int32
|
||||
|
|
@ -22,6 +22,8 @@ func CanonicalHeaderNameFromBytes (_header []byte) (string) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func CanonicalHeaderValueFromBytes (_value []byte) (string) {
|
||||
if _canonical, _found := CanonicalHeaderValuesMap[BytesToString (*NoEscapeBytes (&_value))]; _found {
|
||||
return _canonical
|
||||
|
@ -30,6 +32,14 @@ func CanonicalHeaderValueFromBytes (_value []byte) (string) {
|
|||
}
|
||||
}
|
||||
|
||||
func CanonicalHeaderValueArrayFromBytes (_value []byte) ([]string) {
|
||||
if _canonical, _found := CanonicalHeaderValuesArraysMap[BytesToString (*NoEscapeBytes (&_value))]; _found {
|
||||
return _canonical
|
||||
} else {
|
||||
return []string { string (_value) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func CanonicalHeaderValueRegister (_value string) () {
|
||||
CanonicalHeaderValues = append (CanonicalHeaderValues, _value)
|
||||
|
@ -41,6 +51,7 @@ func CanonicalHeaderValueRegister (_value string) () {
|
|||
|
||||
var CanonicalHeaderNamesMap map[string]string
|
||||
var CanonicalHeaderValuesMap map[string]string
|
||||
var CanonicalHeaderValuesArraysMap map[string][]string
|
||||
|
||||
|
||||
var CanonicalHeaderNames = []string {
|
||||
|
@ -218,8 +229,10 @@ func init () {
|
|||
CanonicalHeaderValues = append (CanonicalHeaderValues, MimeTypes ...)
|
||||
|
||||
CanonicalHeaderValuesMap = make (map[string]string, len (CanonicalHeaderValues))
|
||||
CanonicalHeaderValuesArraysMap = make (map[string][]string, len (CanonicalHeaderValues))
|
||||
for _, _value := range CanonicalHeaderValues {
|
||||
CanonicalHeaderValuesMap[_value] = _value
|
||||
CanonicalHeaderValuesArraysMap[_value] = []string { _value }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue