package http import ( "bytes" "context" "crypto/tls" "net/http" "github.com/gin-gonic/gin" "gitlink.org.cn/cloudream/common/pkgs/async" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/jcs-pub/client/internal/cluster" "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth" "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/proxy" "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types" v1 "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/v1" "gitlink.org.cn/cloudream/jcs-pub/client/internal/services" clirpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/client" "golang.org/x/net/http2" ) type ServerEventChan = async.UnboundChannel[ServerEvent] type ServerEvent interface { IsServerEvent() bool } type ExitEvent struct { ServerEvent Err error } type Server struct { cfg types.Config httpSrv *http.Server svc *services.Service clster *cluster.Cluster eventChan *ServerEventChan auth *auth.Auth proxy *proxy.ClusterProxy v1Svr *v1.Server } func NewServer(cfg types.Config, svc *services.Service, clster *cluster.Cluster) *Server { return &Server{ cfg: cfg, svc: svc, clster: clster, eventChan: async.NewUnboundChannel[ServerEvent](), v1Svr: v1.NewServer(&cfg, svc), } } func (s *Server) Start() *ServerEventChan { go func() { if !s.cfg.Enabled { return } engine := gin.New() s.httpSrv = &http.Server{ Addr: s.cfg.Listen, Handler: engine, } s.auth = auth.New(&s.cfg) s.proxy = proxy.NewClusterProxy(s.clster) s.httpSrv.TLSConfig = &tls.Config{ GetConfigForClient: s.auth.TLSConfigSelector, } http2.ConfigureServer(s.httpSrv, &http2.Server{}) s.v1Svr.InitRouters(engine.Group("/v1"), s.auth, s.proxy) logger.Infof("start serving http at: %s", s.cfg.Listen) err := s.httpSrv.ListenAndServeTLS("", "") s.eventChan.Send(ExitEvent{Err: err}) }() return s.eventChan } func (s *Server) Stop() { if s.httpSrv == nil { s.eventChan.Send(ExitEvent{}) return } s.httpSrv.Shutdown(context.Background()) } func (s *Server) ServeHTTP(ctx context.Context, req *clirpc.HTTPProxyRequest) (*clirpc.HTTPProxyResponse, error) { ctx2 := context.WithValue(ctx, auth.ClusterProxyRequest, true) r, err := http.NewRequestWithContext(ctx2, req.Method, req.URI, bytes.NewReader(req.Body)) if err != nil { return nil, err } for _, v := range req.Header { r.Header.Add(v.Key, v.Value) } resp := proxyRespWriter{ header: make(http.Header), } s.httpSrv.Handler.ServeHTTP(&resp, r) var header []*clirpc.HeaderKV for k, v := range resp.header { for _, vv := range v { header = append(header, &clirpc.HeaderKV{ Key: k, Value: vv, }) } } return &clirpc.HTTPProxyResponse{ StatusCode: int32(resp.statusCode), Header: header, Body: resp.body.Bytes(), }, nil } type proxyRespWriter struct { statusCode int header http.Header body bytes.Buffer } func (w *proxyRespWriter) Header() http.Header { return w.header } func (w *proxyRespWriter) Write(b []byte) (int, error) { return w.body.Write(b) } func (w *proxyRespWriter) WriteHeader(statusCode int) { w.statusCode = statusCode }