-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSES.cfc
343 lines (305 loc) · 12.4 KB
/
SES.cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
<!-----------------------------------------------------------------------
********************************************************************************
Copyright 2005-2008 ColdBox Framework by Luis Majano and Ortus Solutions, Corp
www.coldboxframework.com | www.luismajano.com | www.ortussolutions.com
********************************************************************************
Author : Luis Majano
Date : 9/28/2007
Description :
This is an interceptor for ses support. This code is based almost totally on
Adam Fortuna's ColdCourse cfc, which is an AMAZING SES component
All credits go to him: http://coldcourse.riaforge.com
Modified for MyOpenbox framework 1/11/2010
----------------------------------------------------------------------->
<cfcomponent hint="This interceptor provides complete SES and URL mappings support to ColdBox Applications"
output="false">
<!------------------------------------------- CONSTRUCTOR ------------------------------------------->
<cfscript>
// Reserved Keys as needed for cleanups
instance.RESERVED_KEYS = "circuit,fuse";
instance.RESERVED_ROUTE_ARGUMENTS = "";
instance.config={ EventVariable="Fuseaction", DefaultHandler="Home", DefaultAction="Home" };
</cfscript>
<cffunction name="configure" access="public" returntype="void" hint="This is where the ses plugin configures itself." output="false" >
<cfargument name="AppendStruct" required="false" default="#StructNew()#" type="struct" />
<cfscript>
StructAppend(instance.config, arguments.AppendStruct, true);
// Setup the default interceptor properties
setRoutes( ArrayNew(1) );
</cfscript>
</cffunction>
<!------------------------------------------- PUBLIC ------------------------------------------->
<!--- Add a new Route --->
<cffunction name="addRoute" access="public" returntype="void" hint="Adds a route to dispatch" output="false">
<!--- ************************************************************* --->
<cfargument name="pattern" type="string" required="true" hint="The pattern to match against the URL." />
<cfargument name="circuit" type="string" required="false" hint="The handler to execute if pattern matched.">
<cfargument name="fuse" type="any" required="false" hint="The action in a handler to execute if a pattern is matched. This can also be a structure or JSON structured based on the HTTP method(GET,POST,PUT,DELETE). ex: {GET:'show', PUT:'update', DELETE:'delete', POST:'save'}">
<cfargument name="vars" type="array" default="#ArrayNew(1)#" required="false" />
<!--- ************************************************************* --->
<cfscript>
var thisRoute = structNew();
var thisPattern = "";
var thisPatternParam = "";
var arg = 0;
var x =1;
var thisRegex = 0;
var patternType = "";
// Process all incoming arguments
for(arg in arguments){
if( structKeyExists(arguments,arg) ){ thisRoute[arg] = arguments[arg]; }
}
// Add trailing / to make it easier to parse
if( left(thisRoute.pattern,1) IS NOT "/" ){
thisRoute.pattern = "/" & thisRoute.pattern;
}
// Cleanup initial /
if( Len(thisRoute.pattern) GT 1 AND right(thisRoute.pattern,1) IS "/" ){
/*
if( thisRoute.pattern eq "/" ){
$throw(message="Pattern is empty, please verify the pattern is valid. Route: #thisRoute.toString()#",type="SES.InvalidRoute");
}
*/
thisRoute.pattern = left(thisRoute.pattern,len(thisRoute.pattern)-1);
}
// Check if we have optional args by looking for a ?
if( findnocase("?",thisRoute.pattern) ){
processRouteOptionals(thisRoute);
return;
}
// Init the regexpattern
thisRoute.regexPattern = "";
thisRoute.patternParams = arrayNew(1);
thisRoute.partCount = listLen(thisRoute.pattern,"/");
// Process the route as a regex pattern
for(x=1; x lte thisRoute.partCount;x=x+1){
// Pattern and Pattern Param
thisPattern = listGetAt(thisRoute.pattern,x,"/");
thisPatternParam = replace(listFirst(thisPattern,"-"),":","");
// Detect Optional Types
patternType = "alphanumeric";
if( findnoCase("-guid",thisPattern) ){ patternType = "guid"; }
if( findnoCase("-numeric",thisPattern) ){ patternType = "numeric"; }
if( findnoCase("-alpha",thisPattern) ){ patternType = "alpha"; }
switch(patternType){
// ALPHANUMERICAL OPTIONAL
case "alphanumeric" : {
if( find(":",thisPattern) ){
thisRegex = "(" & REReplace(thisPattern,":(.[^-]*)",".");
// Check Digits Repetions
if( find("{",thisPattern) ){
thisRegex = listFirst(thisRegex,"{") & "{#listLast(thisPattern,"{")#)";
arrayAppend(thisRoute.patternParams,replace(listFirst(thisPattern,"{"),":",""));
}
else{
thisRegex = thisRegex & "+";
if (x LT thisRoute.partCount) {
thisRegex = thisRegex & "?";
}
thisRegex = thisRegex & ")";
arrayAppend(thisRoute.patternParams,thisPatternParam);
}
}
else{
thisRegex = thisPattern;
}
break;
}
// GUID OPTIONAL
case "guid" : {
// Convert to Regex Pattern
thisRegex = "(" & REReplace(thisPattern, ":.*?-guid", "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") & ")";
// Add Route Param
arrayAppend(thisRoute.patternParams,thisPatternParam);
break;
}
// NUMERICAL OPTIONAL
case "numeric" : {
// Convert to Regex Pattern
thisRegex = "(" & REReplace(thisPattern, ":.*?-numeric", "[0-9]");
// Check Digits
if( find("{",thisPattern) ){
thisRegex = listFirst(thisRegex,"{") & "{#listLast(thisPattern,"{")#)";
}
else{
thisRegex = thisRegex & "+";
if (x LT thisRoute.partCount) {
thisRegex = thisRegex & "?";
}
thisRegex = thisRegex & ")";
}
// Add Route Param
arrayAppend(thisRoute.patternParams,thisPatternParam);
break;
}
// ALPHA OPTIONAL
case "alpha" : {
// Convert to Regex Pattern
thisRegex = "(" & REReplace(thisPattern, ":.*?-alpha", "[a-zA-Z]");
// Check Digits
if( find("{",thisPattern) ){
thisRegex = listFirst(thisRegex,"{") & "{#listLast(thisPattern,"{")#)";
}
else{
thisRegex = thisRegex & "+";
if (x LT thisRoute.partCount) {
thisRegex = thisRegex & "?";
}
thisRegex = thisRegex & ")";
}
// Add Route Param
arrayAppend(thisRoute.patternParams,thisPatternParam);
break;
}
} //end pattern type detection switch
// Add Regex Created To Pattern
thisRoute.regexPattern = thisRoute.regexPattern & "/" & thisRegex;
} // end looping of pattern optionals
thisRoute.vars=arguments.vars;
// Finally add it to the routing table
ArrayAppend(getRoutes(), thisRoute);
</cfscript>
</cffunction>
<!--- Getter routes --->
<cffunction name="getRoutes" access="public" output="false" returntype="Array" hint="Get the array containing all the routes">
<cfreturn instance.Routes/>
</cffunction>
<!------------------------------------------- PRIVATE ------------------------------------------->
<!--- Set Routes --->
<cffunction name="setRoutes" access="private" output="false" returntype="void" hint="Internal override of the routes array">
<cfargument name="Routes" type="Array" required="true"/>
<cfset instance.Routes = arguments.Routes/>
</cffunction>
<!--- CGI Element Facade. --->
<cffunction name="getCGIElement" access="private" returntype="string" hint="The cgi element facade method" output="false" >
<cfargument name="cgielement" required="true" type="string" hint="The cgi element to retrieve">
<cfscript>
return cgi[arguments.cgielement];
</cfscript>
</cffunction>
<!--- Fix Ending IIS funkyness --->
<cffunction name="fixIISURLVars" access="private" returntype="string" hint="Clean up some IIS funkyness" output="false" >
<cfargument name="requestString" type="any" required="true" hint="The request string">
<cfargument name="rc" type="any" required="true" hint="The request collection">
<cfscript>
var varMatch = 0;
var qsValues = 0;
var qsVal = 0;
var x = 1;
// Find a Matching position of IIS ?
varMatch = REFind("\?.*=",arguments.requestString,1,"TRUE");
if( varMatch.pos[1] ){
// Copy values to the RC
qsValues = REreplacenocase(arguments.requestString,"^.*\?","","all");
// loop and create
for(x=1; x lte listLen(qsValues,"&"); x=x+1){
qsVal = listGetAt(qsValues,x,"&");
rc[listFirst(qsVal,"=")] = listLast(qsVal,"=");
}
// Clean the request string
arguments.requestString = Mid(arguments.requestString, 1, (varMatch.pos[1]-1));
}
return arguments.requestString;
</cfscript>
</cffunction>
<!--- Find a route --->
<cffunction name="findRoute" access="public" output="false" returntype="Struct" hint="Figures out which route matches this request">
<!--- ************************************************************* --->
<cfargument name="action" required="true" type="any" hint="The action evaluated by the path_info">
<!--- ************************************************************* --->
<cfset var requestString = arguments.action />
<cfset var packagedRequestString = "">
<cfset var match = structNew() />
<cfset var foundRoute = structNew() />
<cfset var params = structNew() />
<cfset var key = "" />
<cfset var i = 1 />
<cfset var x = 1 >
<cfset var _routes = getRoutes()>
<cfset var _routesLength = ArrayLen(_routes)>
<cfscript>
// fix URL vars after ?
//requestString = fixIISURLVars(requestString,rc);
//Remove the leading slash
if( len(requestString) GT 1 AND right(requestString,1) eq "/" ){
requestString = left(requestString,len(requestString)-1);
}
// Add ending slash
if( left(requestString,1) IS NOT "/" ){
requestString = "/" & requestString;
}
params.requestString=requestString;
// Let's Find a Route, Loop over all the routes array
for(i=1; i lte _routesLength; i=i+1){
// Match The route to request String
match = reFindNoCase("^" & _routes[i].regexPattern & "$",requestString,1,true);
//writedump(match);
if( match.len[1] IS NOT 0 AND match.pos[1] EQ 1 ){
// Setup the found Route
foundRoute = _routes[i];
break;
}
}//end finding routes
</cfscript>
<!--- <cfdump var="#foundRoute#" /><cfabort /> --->
<cfscript>
// Check if we found a route, else just return empty params struct
if( structIsEmpty(foundRoute) ){ return params; }
// Populate the params, with variables found in the request string
for(x=1; x lte arrayLen(foundRoute.patternParams); x=x+1){
params[foundRoute.patternParams[x]] = mid(requestString, match.pos[x+1], match.len[x+1]);
}
// Now setup all found variables in the param struct, so we can return
for(key in foundRoute){
if( NOT listFindNoCase(instance.RESERVED_ROUTE_ARGUMENTS,key) ){
params[key] = foundRoute[key];
}
else if (key eq "matchVariables"){
for(i=1; i lte listLen(foundRoute.matchVariables); i = i+1){
params[listFirst(listGetAt(foundRoute.matchVariables,i),"=")] = listLast(listGetAt(foundRoute.matchVariables,i),"=");
}
}
}
</cfscript>
<!--- <cfdump var="#params#" /><cfabort /> --->
<cfscript>
return params;
</cfscript>
</cffunction>
<cffunction name="processRouteOptionals" access="private" returntype="void" hint="Process route optionals" output="false" >
<cfargument name="thisRoute" type="struct" required="true" hint="The route struct">
<cfscript>
var x=1;
var thisPattern = 0;
var base = "";
var optionals = "";
var routeList = "";
// Parse our base & optionals
for(x=1; x lte listLen(arguments.thisRoute.pattern,"/"); x=x+1){
thisPattern = listgetAt(arguments.thisRoute.pattern,x,"/");
// Check for ?
if( not findnocase("?",thisPattern) ){
base = base & thisPattern & "/";
}
else{
optionals = optionals & replacenocase(thisPattern,"?","","all") & "/";
}
}
// Register our routeList
routeList = base & optionals;
// Recurse and register in reverse order
for(x=1; x lte listLen(optionals,"/"); x=x+1){
// Create new route
arguments.thisRoute.pattern = routeList;
// Register route
addRoute(argumentCollection=arguments.thisRoute);
// Remove last bit
routeList = listDeleteat(routeList,listlen(routeList,"/"),"/");
}
// Setup the base route again
arguments.thisRoute.pattern = base;
// Register the final route
addRoute(argumentCollection=arguments.thisRoute);
</cfscript>
</cffunction>
</cfcomponent>