/parse.go

https://code.google.com/p/gorest/ · Go · 366 lines · 273 code · 56 blank · 37 comment · 144 complexity · 1899fe273ff532c7ea3c8148bd5bb65c MD5 · raw file

  1. //Copyright 2011 Siyabonga Dlamini (siyabonga.dlamini@gmail.com). All rights reserved.
  2. //
  3. //Redistribution and use in source and binary forms, with or without
  4. //modification, are permitted provided that the following conditions
  5. //are met:
  6. //
  7. // 1. Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. //
  10. // 2. Redistributions in binary form must reproduce the above copyright
  11. // notice, this list of conditions and the following disclaimer
  12. // in the documentation and/or other materials provided with the
  13. // distribution.
  14. //
  15. //THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  16. //IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. //OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. //IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. //SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  20. //PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  21. //OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  22. //WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  23. //OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  24. //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. package gorest
  26. import (
  27. "log"
  28. "reflect"
  29. "strings"
  30. )
  31. type argumentData struct {
  32. parameter param
  33. data string
  34. }
  35. type param struct {
  36. positionInPath int
  37. name string
  38. typeName string
  39. }
  40. var aLLOWED_PAR_TYPES = []string{"string", "int", "int32", "int64", "bool", "float32", "float64"}
  41. func prepServiceMetaData(root string, tags reflect.StructTag, i interface{}, name string) serviceMetaData {
  42. md := new(serviceMetaData)
  43. if tag := tags.Get("root"); tag != "" {
  44. md.root = tag
  45. }
  46. if root != "" {
  47. md.root = root + md.root
  48. }
  49. log.Println("All EndPoints for service [", name, "] , registered under root path: ", md.root)
  50. if tag := tags.Get("consumes"); tag != "" {
  51. md.consumesMime = tag
  52. if GetMarshallerByMime(tag) == nil {
  53. log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
  54. }
  55. } else {
  56. md.consumesMime = Application_Json //Default
  57. }
  58. if tag := tags.Get("produces"); tag != "" {
  59. md.producesMime = tag
  60. if GetMarshallerByMime(tag) == nil {
  61. log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
  62. }
  63. } else {
  64. md.producesMime = Application_Json //Default
  65. }
  66. if tag := tags.Get("realm"); tag != "" {
  67. md.realm = tag
  68. if GetAuthorizer(tag) == nil {
  69. log.Panic("The realm:[" + tag + "], is not registered. Please register this realm before registering your service.")
  70. }
  71. }
  72. md.template = i
  73. return *md
  74. }
  75. func makeEndPointStruct(tags reflect.StructTag, serviceRoot string) endPointStruct {
  76. ms := new(endPointStruct)
  77. if tag := tags.Get("method"); tag != "" {
  78. if tag == "GET" {
  79. ms.requestMethod = GET
  80. } else if tag == "POST" {
  81. ms.requestMethod = POST
  82. } else if tag == "PUT" {
  83. ms.requestMethod = PUT
  84. } else if tag == "DELETE" {
  85. ms.requestMethod = DELETE
  86. } else if tag == "HEAD" {
  87. ms.requestMethod = HEAD
  88. } else if tag == "OPTIONS" {
  89. ms.requestMethod = OPTIONS
  90. } else {
  91. log.Panic("Unknown method type:[" + tag + "] in endpoint declaration. Allowed types {GET,POST,PUT,DELETE,HEAD,OPTIONS}")
  92. }
  93. if tag := tags.Get("path"); tag != "" {
  94. serviceRoot = strings.TrimRight(serviceRoot, "/")
  95. ms.signiture = serviceRoot + "/" + strings.Trim(tag, "/")
  96. } else {
  97. log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
  98. }
  99. if tag := tags.Get("output"); tag != "" {
  100. ms.outputType = tag
  101. if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
  102. ms.outputTypeIsArray = true
  103. ms.outputType = ms.outputType[2:]
  104. }
  105. if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!
  106. if ms.outputType[4:10] == "string" {
  107. ms.outputTypeIsMap = true
  108. ms.outputType = ms.outputType[11:]
  109. } else {
  110. log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [output] tag. Endpoint: " + ms.signiture)
  111. }
  112. }
  113. }
  114. if tag := tags.Get("input"); tag != "" {
  115. ms.inputMime = tag
  116. }
  117. if tag := tags.Get("role"); tag != "" {
  118. ms.role = tag
  119. }
  120. if tag := tags.Get("postdata"); tag != "" {
  121. ms.postdataType = tag
  122. if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
  123. ms.postdataTypeIsArray = true
  124. ms.postdataType = ms.postdataType[2:]
  125. }
  126. if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!
  127. if ms.postdataType[4:10] == "string" {
  128. ms.postdataTypeIsMap = true
  129. ms.postdataType = ms.postdataType[11:]
  130. } else {
  131. log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [postdata] tag. Endpoint: " + ms.signiture)
  132. }
  133. }
  134. }
  135. parseParams(ms)
  136. return *ms
  137. }
  138. log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
  139. return *ms //Should not get here
  140. }
  141. func parseParams(e *endPointStruct) {
  142. e.signiture = strings.Trim(e.signiture, "/")
  143. e.params = make([]param, 0)
  144. e.queryParams = make([]param, 0)
  145. e.nonParamPathPart = make(map[int]string, 0)
  146. pathPart := e.signiture
  147. queryPart := ""
  148. if i := strings.Index(e.signiture, "?"); i != -1 {
  149. pathPart = e.signiture[:i]
  150. //e.root = pathPart
  151. pathPart = strings.TrimRight(pathPart, "/")
  152. queryPart = e.signiture[i+1:]
  153. //Extract Query Parameters
  154. for pos, str1 := range strings.Split(queryPart, "&") {
  155. if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") {
  156. parName, typeName := getVarTypePair(str1, e.signiture)
  157. for _, par := range e.queryParams {
  158. if par.name == parName {
  159. log.Panic("Duplicate Query Parameter name(" + parName + ") in REST path: " + e.signiture)
  160. }
  161. }
  162. //e.queryParams[len(e.queryParams)] = param{pos, parName, typeName}
  163. e.queryParams = append(e.queryParams, param{pos, parName, typeName})
  164. } else {
  165. log.Panic("Please check that your Query Parameters are configured correctly for endpoint: " + e.signiture)
  166. }
  167. }
  168. }
  169. if i := strings.Index(pathPart, "{"); i != -1 {
  170. e.root = pathPart[:i]
  171. } else {
  172. e.root = pathPart
  173. }
  174. //Extract Path Parameters
  175. for pos, str1 := range strings.Split(pathPart, "/") {
  176. e.signitureLen++
  177. if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") { //This just ensures we re dealing with a varibale not normal path.
  178. parName, typeName := getVarTypePair(str1, e.signiture)
  179. if parName == "..." {
  180. e.isVariableLength = true
  181. parName, typeName := getVarTypePair(str1, e.signiture)
  182. e.params = append(e.params, param{pos, parName, typeName})
  183. e.paramLen++
  184. break
  185. }
  186. for _, par := range e.params {
  187. if par.name == parName {
  188. log.Panic("Duplicate Path Parameter name(" + parName + ") in REST path: " + e.signiture)
  189. }
  190. }
  191. e.params = append(e.params, param{pos, parName, typeName})
  192. e.paramLen++
  193. } else {
  194. e.nonParamPathPart[pos] = str1
  195. }
  196. }
  197. e.root = strings.TrimRight(e.root, "/")
  198. if e.isVariableLength && e.paramLen > 1 {
  199. log.Panic("Variable length endpoints can only have one parameter declaration: " + pathPart)
  200. }
  201. for key, ep := range _manager().endpoints {
  202. if ep.root == e.root && ep.signitureLen == e.signitureLen && reflect.DeepEqual(ep.nonParamPathPart, e.nonParamPathPart) && ep.requestMethod == e.requestMethod {
  203. log.Panic("Can not register two endpoints with same request-method(" + ep.requestMethod + ") and same signature: " + e.signiture + " VS " + ep.signiture)
  204. }
  205. if ep.requestMethod == e.requestMethod && pathPart == key {
  206. log.Panic("Endpoint already registered: " + pathPart)
  207. }
  208. if e.isVariableLength && (strings.Index(ep.root+"/", e.root+"/") == 0 || strings.Index(e.root+"/", ep.root+"/") == 0) && ep.requestMethod == e.requestMethod {
  209. log.Panic("Variable length endpoints can only be mounted on a unique root. Root already used: " + ep.root + " <> " + e.root)
  210. }
  211. }
  212. }
  213. func getVarTypePair(part string, sign string) (parName string, typeName string) {
  214. temp := strings.Trim(part, "{}")
  215. ind := 0
  216. if ind = strings.Index(temp, ":"); ind == -1 {
  217. log.Panic("Please ensure that parameter names(" + temp + ") have associated types in REST path: " + sign)
  218. }
  219. parName = temp[:ind]
  220. typeName = temp[ind+1:]
  221. if !isAllowedParamType(typeName) {
  222. log.Panic("Type " + typeName + " is not allowed for Path/Query-parameters in REST path: " + sign)
  223. }
  224. return
  225. }
  226. func isAllowedParamType(typeName string) bool {
  227. for _, s := range aLLOWED_PAR_TYPES {
  228. if s == strings.ToLower(typeName) {
  229. return true
  230. }
  231. }
  232. return false
  233. }
  234. func getEndPointByUrl(method string, url string) (endPointStruct, map[string]string, map[string]string, string, bool) {
  235. //println("Getting:",url)
  236. pathPart := url
  237. queryPart := ""
  238. if i := strings.Index(url, "?"); i != -1 {
  239. pathPart = url[:i]
  240. queryPart = url[i+1:]
  241. }
  242. pathPart = strings.Trim(pathPart, "/")
  243. totalParts := strings.Count(pathPart, "/")
  244. totalParts++
  245. epRet := new(endPointStruct)
  246. pathArgs := make(map[string]string, 0)
  247. queryArgs := make(map[string]string, 0)
  248. var ep *endPointStruct
  249. EPLOOP:
  250. for _, loopEp := range _manager().endpoints {
  251. // println(method, ":", loopEp.requestMethod, pathPart, ":", loopEp.root, totalParts, ":", loopEp.signitureLen, "Variable?", loopEp.isVariableLength)
  252. if loopEp.isVariableLength && (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.requestMethod == method {
  253. ep = &loopEp
  254. varsPart := strings.Trim(pathPart[len(loopEp.root):], "/")
  255. // println("::::::::::::::::Root", pathPart, ">>>>>>>Vars", varsPart)
  256. for upos, str1 := range strings.Split(varsPart, "/") {
  257. pathArgs[string(upos)] = strings.Trim(str1, " ")
  258. }
  259. } else if (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.signitureLen == totalParts && loopEp.requestMethod == method {
  260. ep = &loopEp
  261. //We first make sure that the other parts of the path that are not parameters do actully match with the signature.
  262. //If not we exit. We do not have to cary on looking since we only allow one registration per root and length.
  263. for pos, name := range ep.nonParamPathPart {
  264. for upos, str1 := range strings.Split(pathPart, "/") {
  265. if upos == pos {
  266. if name != str1 {
  267. //Even though the beginning of the path matched, some other part didn't, keep looking.
  268. ep = nil
  269. continue EPLOOP
  270. }
  271. break
  272. }
  273. }
  274. }
  275. //Extract Path Arguments
  276. for _, par := range ep.params {
  277. for upos, str1 := range strings.Split(pathPart, "/") {
  278. if par.positionInPath == upos {
  279. pathArgs[par.name] = strings.Trim(str1, " ")
  280. break
  281. }
  282. }
  283. }
  284. }
  285. if ep != nil {
  286. xsrft := ""
  287. //Extract Query Arguments: These are optional in the query, so some or all of them might not be there.
  288. //Also, if they are there, they do not have to be in the same order they were sepcified in on the declaration signature.
  289. for _, str1 := range strings.Split(queryPart, "&") {
  290. if i := strings.Index(str1, "="); i != -1 {
  291. pName := str1[:i]
  292. dataString := str1[i+1:]
  293. if pName == XSXRF_PARAM_NAME {
  294. xsrft = strings.Trim(dataString, " ")
  295. log.Print("Session Id:", xsrft)
  296. } else {
  297. for _, par := range ep.queryParams {
  298. if par.name == pName {
  299. queryArgs[pName] = strings.Trim(dataString, " ")
  300. break
  301. }
  302. }
  303. }
  304. }
  305. }
  306. return *ep, pathArgs, queryArgs, xsrft, true //Path found
  307. }
  308. }
  309. return *epRet, pathArgs, queryArgs, "", false //Path not found
  310. }