github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/internal/http/http.go (about) 1 package simplehttp 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "io" 8 "io/fs" 9 "net" 10 "net/http" 11 "os" 12 "reflect" 13 14 "github.com/Asutorufa/yuhaiin/internal/appapi" 15 yf "github.com/yuhaiin/yuhaiin.github.io" 16 "google.golang.org/protobuf/proto" 17 "google.golang.org/protobuf/types/known/emptypb" 18 ) 19 20 func Server(o *appapi.Components) { 21 if debug != nil { 22 debug(o.Mux) 23 } 24 25 HandleFront(o.Mux) 26 ServeHTTP(o) 27 } 28 29 var debug func(*http.ServeMux) 30 31 func ServeHTTP(o *appapi.Components) { 32 for k, b := range map[string]func(http.ResponseWriter, *http.Request) error{ 33 "GET /sublist": GrpcToHttp(o.Subscribe.Get), 34 "GET /nodes": GrpcToHttp(o.Node.Manager), 35 "GET /config": GrpcToHttp(o.Setting.Load), 36 "GET /info": GrpcToHttp(o.Setting.Info), 37 "GET /interfaces": GrpcToHttp(o.Tools.GetInterface), 38 "GET /node/now": GrpcToHttp(o.Node.Now), 39 40 "POST /config": GrpcToHttp(o.Setting.Save), 41 "POST /sub": GrpcToHttp(o.Subscribe.Save), 42 "POST /tag": GrpcToHttp(o.Tag.Save), 43 "POST /node": GrpcToHttp(o.Node.Get), 44 "POST /bypass": GrpcToHttp(o.Tools.SaveRemoteBypassFile), 45 "POST /latency": GrpcToHttp(o.Node.Latency), 46 47 "DELETE /conn": GrpcToHttp(o.Connections.CloseConn), 48 "DELETE /node": GrpcToHttp(o.Node.Remove), 49 "DELETE /sub": GrpcToHttp(o.Subscribe.Remove), 50 "DELETE /tag": GrpcToHttp(o.Tag.Remove), 51 52 "PUT /node": GrpcToHttp(o.Node.Use), 53 54 "PATCH /sub": GrpcToHttp(o.Subscribe.Update), 55 "PATCH /node": GrpcToHttp(o.Node.Save), 56 57 // WEBSOCKET 58 "GET /conn": ConnWebsocket(o), 59 60 "OPTIONS /": func(w http.ResponseWriter, r *http.Request) error { return nil }, 61 } { 62 o.Mux.Handle(k, http.HandlerFunc(func(ow http.ResponseWriter, r *http.Request) { 63 cross(ow) 64 w := &wrapResponseWriter{ow, false} 65 err := b(w, r) 66 if err != nil { 67 http.Error(w, err.Error(), http.StatusInternalServerError) 68 } else if !w.writed { 69 w.WriteHeader(http.StatusOK) 70 } 71 })) 72 73 } 74 } 75 76 func cross(w http.ResponseWriter) { 77 w.Header().Set("Access-Control-Allow-Origin", "*") 78 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, PATCH, OPTIONS, HEAD") 79 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Token") 80 w.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token") 81 w.Header().Set("Access-Control-Allow-Credentials", "true") 82 } 83 84 func HandleFront(mux *http.ServeMux) { 85 var ffs fs.FS 86 edir := os.Getenv("EXTERNAL_WEB") 87 if edir != "" { 88 ffs = os.DirFS(edir) 89 } else { 90 ffs = yf.Content 91 } 92 93 dirs, err := fs.Glob(ffs, "*") 94 if err != nil { 95 return 96 } 97 98 handler := http.FileServer(http.FS(ffs)) 99 100 mux.Handle("GET /", handler) 101 for _, v := range dirs { 102 mux.Handle(fmt.Sprintf("GET %s/", v), handler) 103 } 104 } 105 106 type wrapResponseWriter struct { 107 http.ResponseWriter 108 writed bool 109 } 110 111 func (w *wrapResponseWriter) Write(b []byte) (int, error) { 112 w.writed = true 113 return w.ResponseWriter.Write(b) 114 } 115 116 func (w *wrapResponseWriter) WriteHeader(s int) { 117 w.writed = true 118 w.ResponseWriter.WriteHeader(s) 119 } 120 121 func (w *wrapResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 122 w.writed = true 123 return http.NewResponseController(w.ResponseWriter).Hijack() 124 } 125 126 func GrpcToHttp[req, resp proto.Message](function func(context.Context, req) (resp, error)) func(http.ResponseWriter, *http.Request) error { 127 typeEmpty := reflect.TypeOf(&emptypb.Empty{}) 128 reqType := reflect.TypeOf(*new(req)) 129 respType := reflect.TypeOf(*new(resp)) 130 newPr := reflect.New(reqType.Elem()).Interface().(req).ProtoReflect() 131 132 var unmarshalProto func(*http.Request, req) error 133 if reqType == typeEmpty { 134 unmarshalProto = func(r1 *http.Request, r2 req) error { return nil } 135 } else { 136 unmarshalProto = func(r1 *http.Request, r2 req) error { 137 bytes, err := io.ReadAll(r1.Body) 138 if err != nil { 139 return err 140 } 141 142 return proto.Unmarshal(bytes, r2) 143 } 144 } 145 146 var marshalProto func(http.ResponseWriter, resp) error 147 148 if respType == typeEmpty { 149 marshalProto = func(w http.ResponseWriter, r resp) error { return nil } 150 } else { 151 marshalProto = func(w http.ResponseWriter, r resp) error { 152 bytes, err := proto.Marshal(r) 153 if err != nil { 154 return fmt.Errorf("marshal proto failed: %w", err) 155 } 156 157 _, err = w.Write(bytes) 158 return err 159 } 160 } 161 162 return func(w http.ResponseWriter, r *http.Request) error { 163 pr := newPr.New().Interface().(req) 164 165 if err := unmarshalProto(r, pr); err != nil { 166 return err 167 } 168 169 resp, err := function(r.Context(), pr) 170 if err != nil { 171 return err 172 } 173 174 return marshalProto(w, resp) 175 } 176 }