rpc.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package handler
  15. import (
  16. "context"
  17. "net/http"
  18. "reflect"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/appctx"
  22. "yunion.io/x/pkg/util/printutils"
  23. "yunion.io/x/pkg/utils"
  24. "yunion.io/x/onecloud/pkg/appsrv"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient/auth"
  27. "yunion.io/x/onecloud/pkg/mcclient/modulebase"
  28. )
  29. type RPCHandlers struct {
  30. *SHandlers
  31. }
  32. func NewRPCHandlers(prefix string) *RPCHandlers {
  33. return &RPCHandlers{NewHandlers(prefix)}
  34. }
  35. func (h *RPCHandlers) AddGet(mf appsrv.MiddlewareFunc) *RPCHandlers {
  36. h.AddByMethod(GET, mf, NewHP(RpcHandler, APIVer, "rpc"))
  37. return h
  38. }
  39. func (h *RPCHandlers) AddPost(mf appsrv.MiddlewareFunc) *RPCHandlers {
  40. h.AddByMethod(POST, mf, NewHP(RpcHandler, APIVer, "rpc"))
  41. return h
  42. }
  43. func RpcHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  44. curpath := appctx.AppContextCurrentPath(ctx)
  45. var resType string
  46. var resId string
  47. var callName string
  48. resType = curpath[0]
  49. if len(curpath) == 2 {
  50. callName = curpath[1]
  51. } else {
  52. resId = curpath[1]
  53. callName = curpath[2]
  54. }
  55. var e error
  56. var verb string
  57. var params jsonutils.JSONObject = nil
  58. switch req.Method {
  59. case "GET":
  60. verb = "Get"
  61. params, e = jsonutils.ParseQueryString(req.URL.RawQuery)
  62. if e != nil {
  63. log.Errorf("Error parse query string: %s", e)
  64. }
  65. case "POST":
  66. verb = "Do"
  67. params, e = appsrv.FetchJSON(req)
  68. if e != nil {
  69. log.Errorf("Error get JSON body: %s", e)
  70. }
  71. default:
  72. httperrors.InvalidInputError(ctx, w, "Unsupported RPC method %s", req.Method)
  73. return
  74. }
  75. token := AppContextToken(ctx)
  76. s := auth.GetSession(ctx, token, FetchRegion(req))
  77. funcname := verb + utils.Kebab2Camel(callName, "-")
  78. mod, e := modulebase.GetModule(s, resType)
  79. if e != nil || mod == nil {
  80. if e != nil {
  81. log.Debugf("module %s not found %s", resType, e)
  82. }
  83. httperrors.NotFoundError(ctx, w, "resource %s not exists", resType)
  84. return
  85. }
  86. modvalue := reflect.ValueOf(mod)
  87. funcvalue := modvalue.MethodByName(funcname)
  88. if !funcvalue.IsValid() || funcvalue.IsNil() {
  89. httperrors.NotFoundError(ctx, w, "RPC method %s not found", funcname)
  90. return
  91. }
  92. callParams := make([]reflect.Value, 0)
  93. callParams = append(callParams, reflect.ValueOf(s))
  94. if len(resId) > 0 {
  95. callParams = append(callParams, reflect.ValueOf(resId))
  96. }
  97. if params == nil {
  98. params = jsonutils.NewDict()
  99. }
  100. callParams = append(callParams, reflect.ValueOf(params))
  101. log.Debugf("%s", callParams)
  102. retValue := funcvalue.Call(callParams)
  103. retobj := retValue[0]
  104. reterr := retValue[1]
  105. if reterr.IsNil() {
  106. addr := retobj.Interface()
  107. v, ok := addr.(jsonutils.JSONObject)
  108. if ok {
  109. appsrv.SendJSON(w, v)
  110. return
  111. }
  112. v2, ok := addr.([]printutils.SubmitResult)
  113. if ok {
  114. w.WriteHeader(207)
  115. appsrv.SendJSON(w, modulebase.SubmitResults2JSON(v2))
  116. return
  117. }
  118. httperrors.BadGatewayError(ctx, w, "recv invalid data")
  119. return
  120. }
  121. errAddr := reterr.Interface()
  122. ge, ok := errAddr.(error)
  123. if ok {
  124. je := httperrors.NewGeneralError(ge)
  125. httperrors.GeneralServerError(ctx, w, je)
  126. return
  127. }
  128. httperrors.BadGatewayError(ctx, w, "%s", reterr.Interface())
  129. }