apache/dubbo-go

triple request missed http header like scheme, host etc.

2456868764 opened this issue · 3 comments

Environment

  • Server:
  • Client:
  • Protocol:
  • Registry:

Issue description

The user story is to get if the current request is https or http.

there is dubbo filter demo code which just output attachments :

func (f *mtlsFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
	// get request schema
	attachments := ctx.Value(constant.AttachmentKey).(map[string]interface{})
	for key, attachment := range attachments {
		logger.Infof("get triple attachment key %s = %s", key, attachment.([]string)[0])
	}
}

and output as follow:

2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [retries ] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [user-agent grpc-go-triple/0.1.0 (go1.21.6)] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [te trailers] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [content-type application/grpc+proto] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [interface greet.GreetService] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [grpc-accept-encoding gzip] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [timeout ] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [grpc-timeout 2999023u] = %!s(MISSING)
2024-03-22 02:15:34     INFO    logger/logging.go:42    get triple attachment key [accept-encoding identity] = %!s(MISSING)


and miss some common http headers like scheme ,host etc.

Logs

Click me to check logs
Copy logs to here.

Solution by @2456868764 in #2643

setHTTPSHeaders := func(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set http scheme header
		r.Header.Set(":x-scheme", "https")
		r.Header.Set(":x-host", r.Host)
		r.Header.Set(":x-path", r.RequestURI)
		r.Header.Set(":x-method", r.Method)
		certs := r.TLS.PeerCertificates
		if len(certs) > 0 {
			peerCert := certs[0]
			if len(peerCert.URIs) > 0 {
				spiffeURI := peerCert.URIs[0].String()
				// Set spiffe scheme header
				r.Header.Set(":x-spiffe", spiffeURI)
			}
		}
		h.ServeHTTP(w, r)
	})
}

这个设置头部做法在 http2下有问题,一开始有个 method = PRI 协商协议,这个时候是不能设置头部,否则这个协商协议就挂, 要看是否有更合适解决方案 @chickenlj

我现在做法:

dubbo-go/protocol/triple/triple_protocol/server.go 启动两个端口,一个http, 一个 HTTPS

代码如下 :

func (s *Server) Run() error {
	// todo(DMwangnima): deal with TLS
	// Check if both listeners are nil
	// todo http and https port can be different based on mutual tls mode and tls config provider existed or not
	httpAddr := s.addr
	httpsAddr := s.getHTTPSAddress(s.addr)
	httpOn := true
	httpsOn := false
	if s.tlsConfigProvider != nil {
		httpsOn = true
	}

	handler := h2c.NewHandler(s.mux, &http2.Server{})

		setHTTPHeaders := func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			headers := make(map[string]interface{}, 0)
			headers[constant.HttpHeaderXSchemeName] = "http"
			headers[constant.HttpHeaderXHostName] = r.Host
			headers[constant.HttpHeaderXPathName] = r.RequestURI
			headers[constant.HttpHeaderXMethodName] = "POST"
			ctx := context.WithValue(r.Context(), constant.AttachmentKey, headers)
			request := r.WithContext(ctx)
			h.ServeHTTP(w, request)
		})
	}

	setHTTPSHeaders := func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			headers := make(map[string]interface{}, 0)
			headers[constant.HttpHeaderXSchemeName] = "https"
			headers[constant.HttpHeaderXHostName] = r.Host
			headers[constant.HttpHeaderXPathName] = r.RequestURI
			headers[constant.HttpHeaderXMethodName] = r.Method
			certs := r.TLS.PeerCertificates
			if len(certs) > 0 {
				peerCert := certs[0]
				if len(peerCert.URIs) > 0 {
					spiffeURI := peerCert.URIs[0].String()
					// Set spiffe scheme header
					headers[constant.HttpHeaderXSpiffeName] = spiffeURI
				}
			}

			ctx := context.WithValue(r.Context(), constant.AttachmentKey, headers)
			request := r.WithContext(ctx)
			h.ServeHTTP(w, request)
		})
	}

	if s.httpLn == nil && httpOn {
		httpLn, err := net.Listen("tcp", httpAddr)
		if err != nil {
			httpLn.Close()
			return err
		}
		s.httpLn = httpLn
		s.httpSrv = &http.Server{Handler: setHTTPHeaders(handler)}
	}
	if s.httpsLn == nil && httpsOn {
		tlsCfg, err := s.tlsConfigProvider()
		if err != nil {
			logger.Error("can not get tls config")
		}
		httpsLn, err := tls.Listen("tcp", httpsAddr, tlsCfg)
		if err != nil {
			httpsLn.Close()
			return err
		}
		s.httpsLn = httpsLn
		s.httpsSrv = &http.Server{Handler: setHTTPSHeaders(handler)}
	}
	if httpsOn {
		go s.httpsSrv.Serve(s.httpsLn)
	}
	// http should be on now
	if err := s.httpSrv.Serve(s.httpLn); err != nil {
		return err
	}
	return nil
}

HTTPS 可以设置自定义头, HTTP不可以,因为http 一进来就是走了 r.Method == "PRI",这个时候不能设置自定义头。

func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
	if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
		if http2VerboseLogs {
			log.Print("h2c: attempting h2c with prior knowledge.")
		}
          ....
      }

也做了其他trick做法,我现在trick做法 HTTP不设置定义头,HTTPS设置,后续在 dubbo filter 判断再补全,