package client import ( "bytes" "context" "encoding/json" "errors" "io" "io/ioutil" "mime" "net/http" "net/url" "os" "reflect" "strings" "time" "vamicro/api-gateway/auth" "vamicro/api-gateway/models" "vamicro/api-gateway/traces" "vamicro/config" "vamicro/extend/code" "vamicro/extend/util" "basic.com/valib/bhomeclient.git" "basic.com/valib/c_bhomebus.git/proto/source/bhome_msg" "basic.com/valib/logger.git" "github.com/gin-gonic/gin" "github.com/goinggo/mapstructure" ) type Client struct { ms *bhomeclient.MicroNode } var ( procName = "api-gateway" proc = bhomeclient.ProcInfo{ Name: procName, //进程名称 ID: procName, //进程id Info: "", //进程的描述信息,用于区分同一进程名称下多个进程 } firstCall = true ) func NewClient(ctx context.Context, q chan os.Signal) (*Client, error) { var reg = &bhomeclient.RegisterInfo{ Proc: proc, Channel: nil, PubTopic: []string{}, SubTopic: []string{}, } ms, err := bhomeclient.NewMicroNode(ctx, q, config.Server.AnalyServerId, reg, logger.Debug) if err != nil { logger.Debug("NewMicroNode err:", err) return nil, err } ms.StartClient() return &Client{ ms: ms, }, nil } func (c *Client) PublishNetTimeout(nodes []bhome_msg.BHAddress, topic string, msg []byte, timeout int) int { return c.ms.PublishNetTimeout(nodes, topic, msg, timeout) } func (c *Client) DeRegister() { if c != nil && c.ms != nil { c.ms.DeRegister() } } func (c *Client) Free() { if c != nil && c.ms != nil { c.ms.Free() } } //过期后允许通过的url var expired_NoAuthUrls = []string{ "/data/api-v/authorize", "/data/api-v/token", "/data/api-v/newClient", "/data/api-u/users/current", "/data/api-u/sysmenus/me", "/data/api-v/clients", "/data/api-u/sys/login", "/data/api-u/sys/logout", "/data/api-v/info/getServerName", "/data/api-v/license/show", "/data/api-u/users/profile", "/data/api-v/gb28181/findAreaByParentId", "/data/api-v/sysinit/getInitInfo", "/data/api-v/sysinit/savePassword", "/data/api-v/sysinit/networkList", "/data/api-v/sysinit/initNetwork", "/data/api-v/sysinit/saveRegInfo", "/data/api-v/sysinit/getRegInfo", } func (c *Client) AnyRequest(ctx *gin.Context) { contentType := ctx.Request.Header.Get("Content-Type") // RFC 7231, section 3.1.1.5 - empty type // MAY be treated as application/octet-stream if contentType == "" { contentType = "application/octet-stream" } if util.SysExpired { //试用过期之后,只允许登录入系统做更新授权处理 up := ctx.Request.URL.Path if util.ArrayContains(expired_NoAuthUrls, up) { //授权过期之后,只允许指定的url通过 logger.Info("expired!!!! current url:", up) } else { ctx.JSON(http.StatusBadRequest, "使用已过期,请购买授权") return } } if ctx.Request.URL.Path == "/data/api-u/users/current" { authDriver := auth.GenerateAuthDriver() user := (*authDriver).User(ctx) if user != nil { ctx.JSON(http.StatusOK, user) } else { ctx.JSON(http.StatusUnauthorized, "") } return } else if ctx.Request.URL.Path == "/data/api-u/sysmenus/me" { authDriver := auth.GenerateAuthDriver() userM := (*authDriver).User(ctx) if userM != nil { menus := userM["permissions"] util.ResponseFormat(ctx, code.Success, menus) } else { util.ResponseFormat(ctx, code.NotLogin, "") } return } else if ctx.Request.URL.Path == "/data/api-v/authorize" { err := auth.Oauth2Serv.HandleAuthorizeRequest(ctx.Writer, ctx.Request) if err != nil { ctx.JSON(http.StatusBadRequest, err) } return } else if ctx.Request.URL.Path == "/data/api-v/token" { err := auth.Oauth2Serv.HandleTokenRequest(ctx.Writer, ctx.Request) if err != nil { ctx.JSON(http.StatusInternalServerError, err) } return } else if ctx.Request.URL.Path == "/data/api-v/newClient" { err := auth.NewClient(ctx.Request.FormValue("domain"), ctx.Request.FormValue("intro")) if err != nil { util.ResponseFormat(ctx, code.SaveFail, err) } else { util.ResponseFormat(ctx, code.Success, "") } return } else if ctx.Request.URL.Path == "/data/api-v/clients" { var oauth2Model models.Oauth2Client oauth2Clients, err := oauth2Model.FindAll() if err != nil { util.ResponseFormat(ctx, code.DdSelectNotFindError, "") } else { util.ResponseFormat(ctx, code.Success, oauth2Clients) } return } else if ctx.Request.URL.Path == "/data/api-v/delClient" { err := auth.DelClient(ctx.Request.FormValue("id")) if err != nil { util.ResponseFormat(ctx, code.DeleteFail, "") } else { util.ResponseFormat(ctx, code.Success, "") } return } t := time.Now() var buffer = new(bytes.Buffer) buffer.ReadFrom(ctx.Request.Body) //ctx.Request.ParseForm() pfValues, parseE := parsePostForm(ctx.Request, buffer.Bytes()) formValues, _ := url.ParseQuery(ctx.Request.URL.RawQuery) /** 日志modules和operations **/ /** if ctx.Request.URL.Path == "/data/api-v/log/modules" { var moduleMod models.Modules modules,err := moduleMod.FindAll() if nil == err { util.ResponseFormat(ctx,code.Success,modules) } else { util.ResponseFormat(ctx,code.DdSelectNotFindError,err.Error()) } return } else if ("/data/api-v/log/operations" == ctx.Request.URL.Path) { var opModel models.Operations mod,ok := ctx.Request.URL.Query()["module"] if ok { opers,err := opModel.FindAll(mod[0]) if nil == err { util.ResponseFormat(ctx,code.Success,opers) } else { util.ResponseFormat(ctx,code.DdSelectNotFindError,err.Error()) } } else { util.ResponseFormat(ctx,code.DdSelectNotFindError,"") } return }**/ /** modules和operations结束 **/ //if parseMultiErr := ctx.Request.ParseMultipartForm(32 << 20);parseMultiErr != nil && parseMultiErr.Error() != "request Content-Type isn't multipart/form-data" { // logger.Debug("ParseMultipartForm err:", parseMultiErr) //} req := bhomeclient.Request{ Path: ctx.Request.URL.Path, Method: ctx.Request.Method, ContentType: ctx.ContentType(), HeaderMap: ctx.Request.Header, QueryMap: ctx.Request.URL.Query(), FormMap: formValues, PostFormMap: pfValues, //Body: buffer.Bytes(), } if req.FormMap == nil { req.FormMap = make(map[string][]string) } if req.PostFormMap == nil { req.PostFormMap = make(map[string][]string) } //gin框架的body参数只允许读取一次 if strings.HasPrefix(contentType, "multipart/form-data") { logger.Debug("equal, contentType:", contentType) ctx.Request.Body = ioutil.NopCloser(buffer) pe := ctx.Request.ParseMultipartForm(defaultMultipartMemory) if pe != nil { logger.Error("r.ParseMultipartForm err:", pe) } else { appendSignleFileArgs(ctx, &req) appendMultiArgs(ctx, &req) } } else { req.Body = buffer.Bytes() } if len(req.PostFormMap) > 0 { copyValues(req.FormMap, req.PostFormMap) } if _, exist := req.HeaderMap["Authorization"]; exist { delete(req.HeaderMap, "Authorization") } logger.Debug("请求解析完成耗时:", time.Since(t)) logger.Debug("ParseForm err:", parseE, " postForm:", req.PostFormMap, " queryMap:", req.QueryMap) t = time.Now() timeOut := 15 * 1000 if req.Path == "/data/api-v/sdk/sdkDownload" || req.Path == "/data/api-v/sysset/upgrade" || req.Path == "/data/api-v/sysset/getDeviceInfo" || req.Path == "/data/api-v/sysset/getDeviceAuthInfo" || req.Path == "/data/api-v/sdk/install" || req.Path == "/data/api-v/sdk/upload" || req.Path == "/data/api-v/dbperson/searchByPhoto" || req.Path == "/data/api-v/cluster/joinCluster" { timeOut = 60 * 1000 } if req.Path == "/data/api-v/sdk/findAllSdk" || req.Path == "/data/api-v/app/findAllApp" { timeOut = 20 * 1000 } rewriteUploadPath(&req) r, err := c.ms.Request(config.Server.AnalyServerId, req, timeOut) if r != nil { logger.Debug("请求服务耗时:", time.Since(t), " reqSize:", SizeOf(req), " repSize:", SizeOf(*r)) } else { logger.Debug("请求服务耗时:", time.Since(t), " reqPath:", req.Path, " err:", err, " r:", r) } if (req.Path == "/data/api-v/sdk/upload" || req.Path == "/data/api-v/sysset/patchUpdate") && strings.EqualFold(req.Method, "GET") { //这两个方法前端返回的内容是特殊的,只有状态码和内容 if err != nil { ctx.JSON(500, gin.H{ "code": 500, "success": false, "msg": err.Error(), "data": "", }) } else { if r.Success { if r.Data.(string) == "found" { ctx.String(http.StatusOK, "found") } else { ctx.String(http.StatusNoContent, "") } // readme: 目的是为日志统计的时候格式统一 r.Success = true } else { ctx.JSON(500, gin.H{ "code": 500, "success": false, "msg": r.Msg, "data": r.Data, }) } } } else { if err != nil { ctx.JSON(500, gin.H{ "code": 500, "success": false, "msg": err.Error(), "data": "", }) } else { if ctx.Request.URL.Path == "/data/api-u/sys/login" && r.Success { loginedM := util.Struct2Map(r.Data) tokenM := make(map[string]interface{}, 5) tokenM["id"] = loginedM["id"] tokenM["username"] = loginedM["username"] tokenM["permissions"] = loginedM["permissions"] tokenM["roleName"] = loginedM["roleName"] tokenM["roles"] = loginedM["sysRoles"] //tokenM["headpic"] = loginedM["headpic"] tokenM["headpic"] = "" authDriver := auth.GenerateAuthDriver() tokenStr := (*authDriver).Login(ctx.Request, ctx.Writer, tokenM) userId := loginedM["id"].(string) auth.RemoveOutUser(userId) ctx.JSON(200, map[string]interface{}{ "userInfo": loginedM, "access_token": tokenStr, "refresh_token": tokenStr, "scope": "app", "token_type": "Bearer", "expires_in": time.Now().Add(time.Hour * 8).Unix(), }) } else { cod := 200 if !r.Success { cod = 500 } ctx.JSON(cod, gin.H{ "code": cod, "success": r.Success, "msg": r.Msg, "data": r.Data, }) } } op := &traces.OperationPara{ Context: ctx, Error: err, Reply: r, } traces.TraceOperation(op) } } func SizeOf(d interface{}) int { b, _ := json.Marshal(d) return len(b) } func rewriteUploadPath(req *bhomeclient.Request) { if req.Path == "/data/api-v/sdk/upload" && strings.EqualFold(req.Method, "POST") { req.Path = "/data/api-v/sdk/uploadPack" } else if req.Path == "/data/api-v/sysset/patchUpdate" && strings.EqualFold(req.Method, "POST") { req.Path = "/data/api-v/sysset/patchUpdatePack" } } func copyValues(dst, src url.Values) { for k, vs := range src { dst[k] = append(dst[k], vs...) } } type maxBytesReader struct { w http.ResponseWriter r io.ReadCloser // underlying reader n int64 // max bytes remaining err error // sticky error } func (l *maxBytesReader) Read(p []byte) (n int, err error) { if l.err != nil { return 0, l.err } if len(p) == 0 { return 0, nil } // If they asked for a 32KB byte read but only 5 bytes are // remaining, no need to read 32KB. 6 bytes will answer the // question of the whether we hit the limit or go past it. if int64(len(p)) > l.n+1 { p = p[:l.n+1] } n, err = l.r.Read(p) if int64(n) <= l.n { l.n -= int64(n) l.err = err return n, err } n = int(l.n) l.n = 0 // The server code and client code both use // maxBytesReader. This "requestTooLarge" check is // only used by the server code. To prevent binaries // which only using the HTTP Client code (such as // cmd/go) from also linking in the HTTP server, don't // use a static type assertion to the server // "*response" type. Check this interface instead: type requestTooLarger interface { requestTooLarge() } if res, ok := l.w.(requestTooLarger); ok { res.requestTooLarge() } l.err = errors.New("http: request body too large") return n, l.err } func (l *maxBytesReader) Close() error { return l.r.Close() } func parsePostForm(r *http.Request, bufferBytes []byte) (vs url.Values, err error) { if r.Body == nil { err = errors.New("missing form body") return } ct := r.Header.Get("Content-Type") // RFC 7231, section 3.1.1.5 - empty type // MAY be treated as application/octet-stream if ct == "" { ct = "application/octet-stream" } ct, _, err = mime.ParseMediaType(ct) logger.Debug("ct:", ct) switch { case ct == "application/x-www-form-urlencoded": //var reader io.Reader = r.Body //maxFormSize := int64(1<<63 - 1) //if _, ok := r.Body.(*maxBytesReader); !ok { // maxFormSize = int64(10 << 20) // 10 MB is a lot of text. // reader = io.LimitReader(r.Body, maxFormSize+1) //} //b, e := ioutil.ReadAll(reader) //logger.Debug("ReadAll body:", string(b), " e:", e) // //var buffer = new(bytes.Buffer) //buffer.ReadFrom(r.Body) //logger.Debug("buffer:", string(buffer.Bytes())) // //if e != nil { // if err == nil { // err = e // } // break //} //if int64(len(b)) > maxFormSize { // err = errors.New("http: POST too large") // return //} var e error vs, e = url.ParseQuery(string(bufferBytes)) if err == nil { err = e } case ct == "multipart/form-data": // handled by ParseMultipartForm (which is calling us, or should be) // TODO(bradfitz): there are too many possible // orders to call too many functions here. // Clean this up and write more tests. // request_test.go contains the start of this, // in TestParseMultipartFormOrder and others. } return } const defaultMultipartMemory = 32 << 20 // 32 MB func parseMultipartForm(r *http.Request) { } //处理请求中的上传文件参数 func appendSignleFileArgs(ctx *gin.Context, req *bhomeclient.Request) { //single file arg file, header, err := ctx.Request.FormFile("file") if file != nil && header != nil && err == nil { logger.Debug("single file header.Filename:", header.Filename) defer file.Close() fileBytes, err := ioutil.ReadAll(file) if err != nil { logger.Debug("ioutil.ReadAll err:", err) return } req.File = bhomeclient.FileArg{ Name: header.Filename, Size: header.Size, Bytes: fileBytes, } } else { logger.Debug("single FormFile err:", err) } } func appendMultiArgs(ctx *gin.Context, req *bhomeclient.Request) { //multi file arg form, err := ctx.MultipartForm() if err != nil { logger.Debug("appendFileArgs err:", err) return } for _, fhs := range form.File { if len(fhs) > 0 { for _, fh := range fhs { f, err := fh.Open() if err != nil { logger.Debug("fh.Open err:", err) continue } fileBytes, err := ioutil.ReadAll(f) if err != nil { logger.Debug("ioutil.ReadAll err:", err) f.Close() continue } req.MultiFiles = append(req.MultiFiles, bhomeclient.FileArg{ Name: fh.Filename, Size: fh.Size, Bytes: fileBytes, }) f.Close() } } } if len(form.Value) > 0 { for k, v := range form.Value { req.PostFormMap[k] = v } } } func (c *Client) ModuleMapInit() { var operations models.Operations req := bhomeclient.Request{ Path: "/data/api-v/log/operations", Method: "POST", ContentType: "application/x-www-form-urlencoded", HeaderMap: make(map[string][]string), QueryMap: make(map[string][]string), FormMap: make(map[string][]string), PostFormMap: make(map[string][]string), //Body: buffer.Bytes(), } timeOut := 5 * 1000 r, err := c.ms.Request(config.Server.AnalyServerId, req, timeOut) if nil == err && r.Success { logger.Debug(r) logger.Debug("operations map kind", reflect.TypeOf(r.Data).Kind()) //list := r.Data.([]models.Operations) //traces.SetBlockMethod(list) setBlockMethod(r.Data.([]interface{})) } else { for { time.Sleep(3 * time.Second) r, err := c.ms.Request(config.Server.AnalyServerId, req, timeOut) if nil == err && r.Success { logger.Debug(r) logger.Debug("operations map kind", reflect.TypeOf(r.Data).Kind()) //list := r.Data.([]models.Operations) //traces.SetBlockMethod(list) setBlockMethod(r.Data.([]interface{})) break } } } operations.Name = "test" req = bhomeclient.Request{ Path: "/data/api-v/log/find_all_modules_map", Method: "POST", ContentType: "application/x-www-form-urlencoded", HeaderMap: make(map[string][]string), QueryMap: make(map[string][]string), FormMap: make(map[string][]string), PostFormMap: make(map[string][]string), //Body: buffer.Bytes(), } timeOut = 5 * 1000 r, err = c.ms.Request(config.Server.AnalyServerId, req, timeOut) if nil == err && r.Success { //logger.Debug(r) //logger.Debug("module map kind",reflect.TypeOf(r.Data).Kind()) setModuleMap(r.Data.(map[string]interface{})) //traces.SetModuleMap((r.Data).(map[string]string)) } else { for { time.Sleep(3 * time.Second) r, err := c.ms.Request(config.Server.AnalyServerId, req, timeOut) if nil == err && r.Success { //logger.Debug(r) //logger.Debug("module map kind",reflect.TypeOf(r.Data).Kind()) setModuleMap(r.Data.(map[string]interface{})) break } logger.Error(err.Error()) } } } func setModuleMap(mapItem map[string]interface{}) { moduleMap := make(map[string]string) for key, item := range mapItem { moduleMap[key] = item.(string) } logger.Debug(moduleMap) traces.SetModuleMap(moduleMap) } func setBlockMethod(res []interface{}) { var operations []models.Operations for _, item := range res { operation, err := transOperationsStruct(item.(map[string]interface{})) if nil == err { operations = append(operations, operation) } } logger.Debug("operation extract") logger.Debug(operations) traces.SetBlockMethod(operations) auth.AclInit(operations) } func transOperationsStruct(mapitem map[string]interface{}) (models.Operations, error) { var operation models.Operations if err := mapstructure.Decode(mapitem, &operation); err != nil { return operation, err } return operation, nil } /* func (c *Client) Forward(ctx *gin.Context) { targetHost := &TargetHost{ Host: "localhost", IsHttps: false, } HostReverseProxy(ctx.Writer, ctx.Request, targetHost) } func GetVerTLSConfig(CAPath string) (*tls.Config, error) { caData, err := ioutil.ReadFile(CAPath) if err != nil { return nil, err } pool := x509.NewCertPool() pool.AppendCertsFromPEM(caData) tlsConfig := &tls.Config{ RootCAs: pool, } return tlsConfig, nil } func HostReverseProxy(w http.ResponseWriter, req *http.Request, targetHost *TargetHost) { host := "" if targetHost.IsHttps { host = host + "https://" } else { host = host + "http://" } remote, err := url.Parse(host + targetHost.Host) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } proxy := httputil.NewSingleHostReverseProxy(remote) if targetHost.IsHttps { tls, err := GetVerTLSConfig(targetHost.CAPath) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } var pTransport http.RoundTripper = &http.Transport{ Dial: func(netw, addr string) (net.Conn, error) { c, err := net.DialTimeout(netw, addr, time.Second * 5) if err != nil { return nil, err } return c, nil }, ResponseHeaderTimeout: time.Second * 3, TLSClientConfig: tls, } proxy.Transport = pTransport } proxy.ServeHTTP(w, req) } type TargetHost struct { Host string IsHttps bool CAPath string }*/