PageRenderTime 69ms CodeModel.GetById 10ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 26package gorest
 27
 28import (
 29	"log"
 30	"reflect"
 31	"strings"
 32)
 33
 34type argumentData struct {
 35	parameter param
 36	data      string
 37}
 38type param struct {
 39	positionInPath int
 40	name           string
 41	typeName       string
 42}
 43
 44var aLLOWED_PAR_TYPES = []string{"string", "int", "int32", "int64", "bool", "float32", "float64"}
 45
 46func prepServiceMetaData(root string, tags reflect.StructTag, i interface{}, name string) serviceMetaData {
 47	md := new(serviceMetaData)
 48
 49	if tag := tags.Get("root"); tag != "" {
 50		md.root = tag
 51	}
 52	if root != "" {
 53		md.root = root + md.root
 54	}
 55	log.Println("All EndPoints for service [", name, "] , registered under root path: ", md.root)
 56	if tag := tags.Get("consumes"); tag != "" {
 57		md.consumesMime = tag
 58		if GetMarshallerByMime(tag) == nil {
 59			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
 60		}
 61
 62	} else {
 63		md.consumesMime = Application_Json //Default
 64	}
 65	if tag := tags.Get("produces"); tag != "" {
 66		md.producesMime = tag
 67		if GetMarshallerByMime(tag) == nil {
 68			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
 69		}
 70	} else {
 71		md.producesMime = Application_Json //Default
 72	}
 73
 74	if tag := tags.Get("realm"); tag != "" {
 75		md.realm = tag
 76		if GetAuthorizer(tag) == nil {
 77			log.Panic("The realm:[" + tag + "], is not registered. Please register this realm before registering your service.")
 78		}
 79	}
 80
 81	md.template = i
 82	return *md
 83}
 84
 85func makeEndPointStruct(tags reflect.StructTag, serviceRoot string) endPointStruct {
 86
 87	ms := new(endPointStruct)
 88
 89	if tag := tags.Get("method"); tag != "" {
 90		if tag == "GET" {
 91			ms.requestMethod = GET
 92		} else if tag == "POST" {
 93			ms.requestMethod = POST
 94		} else if tag == "PUT" {
 95			ms.requestMethod = PUT
 96		} else if tag == "DELETE" {
 97			ms.requestMethod = DELETE
 98		} else if tag == "HEAD" {
 99			ms.requestMethod = HEAD
100		} else if tag == "OPTIONS" {
101			ms.requestMethod = OPTIONS
102		} else {
103			log.Panic("Unknown method type:[" + tag + "] in endpoint declaration. Allowed types {GET,POST,PUT,DELETE,HEAD,OPTIONS}")
104		}
105
106		if tag := tags.Get("path"); tag != "" {
107			serviceRoot = strings.TrimRight(serviceRoot, "/")
108			ms.signiture = serviceRoot + "/" + strings.Trim(tag, "/")
109		} else {
110			log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
111		}
112
113		if tag := tags.Get("output"); tag != "" {
114			ms.outputType = tag
115			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
116				ms.outputTypeIsArray = true
117				ms.outputType = ms.outputType[2:]
118			}
119			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!
120
121				if ms.outputType[4:10] == "string" {
122					ms.outputTypeIsMap = true
123					ms.outputType = ms.outputType[11:]
124				} else {
125					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [output] tag. Endpoint: " + ms.signiture)
126				}
127
128			}
129		}
130
131		if tag := tags.Get("input"); tag != "" {
132			ms.inputMime = tag
133		}
134		if tag := tags.Get("role"); tag != "" {
135			ms.role = tag
136		}
137
138		if tag := tags.Get("postdata"); tag != "" {
139			ms.postdataType = tag
140			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
141				ms.postdataTypeIsArray = true
142				ms.postdataType = ms.postdataType[2:]
143			}
144			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!
145
146				if ms.postdataType[4:10] == "string" {
147					ms.postdataTypeIsMap = true
148					ms.postdataType = ms.postdataType[11:]
149				} else {
150					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [postdata] tag. Endpoint: " + ms.signiture)
151				}
152
153			}
154		}
155
156		parseParams(ms)
157		return *ms
158	}
159
160	log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
161	return *ms //Should not get here
162
163}
164func parseParams(e *endPointStruct) {
165	e.signiture = strings.Trim(e.signiture, "/")
166	e.params = make([]param, 0)
167	e.queryParams = make([]param, 0)
168	e.nonParamPathPart = make(map[int]string, 0)
169
170	pathPart := e.signiture
171	queryPart := ""
172
173	if i := strings.Index(e.signiture, "?"); i != -1 {
174
175		pathPart = e.signiture[:i]
176		//e.root = pathPart
177		pathPart = strings.TrimRight(pathPart, "/")
178		queryPart = e.signiture[i+1:]
179
180		//Extract Query Parameters
181
182		for pos, str1 := range strings.Split(queryPart, "&") {
183			if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") {
184				parName, typeName := getVarTypePair(str1, e.signiture)
185
186				for _, par := range e.queryParams {
187					if par.name == parName {
188						log.Panic("Duplicate Query Parameter name(" + parName + ") in REST path: " + e.signiture)
189					}
190				}
191				//e.queryParams[len(e.queryParams)] = param{pos, parName, typeName}
192				e.queryParams = append(e.queryParams, param{pos, parName, typeName})
193			} else {
194				log.Panic("Please check that your Query Parameters are configured correctly for endpoint: " + e.signiture)
195			}
196		}
197	}
198
199	if i := strings.Index(pathPart, "{"); i != -1 {
200		e.root = pathPart[:i]
201	} else {
202		e.root = pathPart
203	}
204
205	//Extract Path Parameters
206	for pos, str1 := range strings.Split(pathPart, "/") {
207		e.signitureLen++
208
209		if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") { //This just ensures we re dealing with a varibale not normal path.
210
211			parName, typeName := getVarTypePair(str1, e.signiture)
212
213			if parName == "..." {
214				e.isVariableLength = true
215				parName, typeName := getVarTypePair(str1, e.signiture)
216				e.params = append(e.params, param{pos, parName, typeName})
217				e.paramLen++
218				break
219			}
220			for _, par := range e.params {
221				if par.name == parName {
222					log.Panic("Duplicate Path Parameter name(" + parName + ") in REST path: " + e.signiture)
223				}
224			}
225
226			e.params = append(e.params, param{pos, parName, typeName})
227			e.paramLen++
228		} else {
229			e.nonParamPathPart[pos] = str1
230
231		}
232	}
233
234	e.root = strings.TrimRight(e.root, "/")
235
236	if e.isVariableLength && e.paramLen > 1 {
237		log.Panic("Variable length endpoints can only have one parameter declaration: " + pathPart)
238	}
239
240	for key, ep := range _manager().endpoints {
241		if ep.root == e.root && ep.signitureLen == e.signitureLen && reflect.DeepEqual(ep.nonParamPathPart, e.nonParamPathPart) && ep.requestMethod == e.requestMethod {
242			log.Panic("Can not register two endpoints with same request-method(" + ep.requestMethod + ") and same signature: " + e.signiture + " VS " + ep.signiture)
243		}
244		if ep.requestMethod == e.requestMethod && pathPart == key {
245			log.Panic("Endpoint already registered: " + pathPart)
246		}
247		if e.isVariableLength && (strings.Index(ep.root+"/", e.root+"/") == 0 || strings.Index(e.root+"/", ep.root+"/") == 0) && ep.requestMethod == e.requestMethod {
248			log.Panic("Variable length endpoints can only be mounted on a unique root. Root already used: " + ep.root + " <> " + e.root)
249		}
250	}
251}
252
253func getVarTypePair(part string, sign string) (parName string, typeName string) {
254
255	temp := strings.Trim(part, "{}")
256	ind := 0
257	if ind = strings.Index(temp, ":"); ind == -1 {
258		log.Panic("Please ensure that parameter names(" + temp + ") have associated types in REST path: " + sign)
259	}
260	parName = temp[:ind]
261	typeName = temp[ind+1:]
262
263	if !isAllowedParamType(typeName) {
264		log.Panic("Type " + typeName + " is not allowed for Path/Query-parameters in REST path: " + sign)
265	}
266
267	return
268}
269
270func isAllowedParamType(typeName string) bool {
271	for _, s := range aLLOWED_PAR_TYPES {
272		if s == strings.ToLower(typeName) {
273			return true
274		}
275	}
276	return false
277}
278
279func getEndPointByUrl(method string, url string) (endPointStruct, map[string]string, map[string]string, string, bool) {
280	//println("Getting:",url)
281
282	pathPart := url
283	queryPart := ""
284
285	if i := strings.Index(url, "?"); i != -1 {
286		pathPart = url[:i]
287		queryPart = url[i+1:]
288	}
289
290	pathPart = strings.Trim(pathPart, "/")
291	totalParts := strings.Count(pathPart, "/")
292	totalParts++
293
294	epRet := new(endPointStruct)
295	pathArgs := make(map[string]string, 0)
296	queryArgs := make(map[string]string, 0)
297
298	var ep *endPointStruct
299
300EPLOOP:
301	for _, loopEp := range _manager().endpoints {
302		//              println(method, ":", loopEp.requestMethod, pathPart, ":", loopEp.root, totalParts, ":", loopEp.signitureLen, "Variable?", loopEp.isVariableLength)
303		if loopEp.isVariableLength && (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.requestMethod == method {
304			ep = &loopEp
305			varsPart := strings.Trim(pathPart[len(loopEp.root):], "/")
306			//                      println("::::::::::::::::Root", pathPart, ">>>>>>>Vars", varsPart)
307			for upos, str1 := range strings.Split(varsPart, "/") {
308				pathArgs[string(upos)] = strings.Trim(str1, " ")
309			}
310		} else if (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.signitureLen == totalParts && loopEp.requestMethod == method {
311			ep = &loopEp
312			//We first make sure that the other parts of the path that are not parameters do actully match with the signature.
313			//If not we exit. We do not have to cary on looking since we only allow one registration per root and length.
314			for pos, name := range ep.nonParamPathPart {
315				for upos, str1 := range strings.Split(pathPart, "/") {
316					if upos == pos {
317						if name != str1 {
318							//Even though the beginning of the path matched, some other part didn't, keep looking.
319							ep = nil
320							continue EPLOOP
321						}
322						break
323					}
324				}
325			}
326			//Extract Path Arguments
327			for _, par := range ep.params {
328				for upos, str1 := range strings.Split(pathPart, "/") {
329
330					if par.positionInPath == upos {
331						pathArgs[par.name] = strings.Trim(str1, " ")
332						break
333					}
334				}
335			}
336		}
337
338		if ep != nil {
339			xsrft := ""
340			//Extract Query Arguments: These are optional in the query, so some or all of them might not be there.
341			//Also, if they are there, they do not have to be in the same order they were sepcified in on the declaration signature.
342			for _, str1 := range strings.Split(queryPart, "&") {
343				if i := strings.Index(str1, "="); i != -1 {
344					pName := str1[:i]
345					dataString := str1[i+1:]
346					if pName == XSXRF_PARAM_NAME {
347						xsrft = strings.Trim(dataString, " ")
348						log.Print("Session Id:", xsrft)
349					} else {
350						for _, par := range ep.queryParams {
351							if par.name == pName {
352								queryArgs[pName] = strings.Trim(dataString, " ")
353								break
354							}
355						}
356					}
357
358				}
359			}
360
361			return *ep, pathArgs, queryArgs, xsrft, true //Path found
362		}
363	}
364
365	return *epRet, pathArgs, queryArgs, "", false //Path not found
366}