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  }