[server] Try to optimize memory allocations for FastHTTP to Go HTTP bridging (it involves heavy "magic").

This commit is contained in:
Ciprian Dorin Craciun 2021-12-17 21:58:36 +02:00
parent e817765cfd
commit 01586f506d
3 changed files with 125 additions and 11 deletions

View file

@ -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 {

View 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

View file

@ -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 }
}
}