@@ -23,6 +23,93 @@ class InvalidRouteRegexError(StackInABoxServiceErrors):
23
23
pass
24
24
25
25
26
+ class StackInABoxServiceRouter (object ):
27
+
28
+ def __init__ (self , name , uri , obj , parent_obj ):
29
+ self .service_name = name
30
+ self .uri = uri
31
+ self .obj = obj
32
+ self .parent_obj = parent_obj
33
+ self .methods = {}
34
+
35
+ # Ensure we do not have any circular references
36
+ assert (self .obj != self .parent_obj )
37
+
38
+ @property
39
+ def is_subservice (self ):
40
+ if self .obj is not None :
41
+ return True
42
+
43
+ return False
44
+
45
+ def set_subservice (self , obj ):
46
+ if self .obj is not None :
47
+ raise RouteAlreadyRegisteredError (
48
+ 'Service Router ({0} - {1}): Route {2} already has a '
49
+ 'sub-service handler'
50
+ .format (id (self ), self .service_name , self .uri ))
51
+
52
+ if len (self .methods ):
53
+ logger .debug (
54
+ 'WARNING: Service Router ({0} - {1}): Methods detected '
55
+ 'on Route {2}. Sub-Service {3} may be hidden.'
56
+ .format (id (self ), self .service_name , self .uri , obj .name ))
57
+
58
+ # Ensure we do not have any circular references
59
+ assert (obj != self .parent_obj )
60
+ self .obj = obj
61
+ self .obj .base_url = '{0}/{1}' .format (self .uri , self .service_name )
62
+
63
+ def update_uris (self , new_uri ):
64
+ self .uri = new_uri
65
+ if self .obj :
66
+ self .obj .base_url = '{0}/{1}' .format (self .uri , self .service_name )
67
+
68
+ def register_method (self , method , fn ):
69
+ if method not in self .methods .keys ():
70
+ logger .debug ('Service Router ({0} - {1}): Adding method {2} on '
71
+ 'route {3}'
72
+ .format (id (self ),
73
+ self .service_name ,
74
+ method ,
75
+ self .uri ))
76
+ self .methods [method ] = fn
77
+
78
+ else :
79
+ raise RouteAlreadyRegisteredError (
80
+ 'Service Router ({0} - {1}): Method {2} already registered '
81
+ 'on Route {3}'
82
+ .format (id (self ),
83
+ self .service_name ,
84
+ method ,
85
+ self .uri ))
86
+
87
+ def __call__ (self , method , request , uri , headers ):
88
+ if method in self .methods :
89
+ logger .debug ('Service Router ({0} - {1}): Located Method {2} on '
90
+ 'Route {3}. Calling...'
91
+ .format (id (self ),
92
+ self .service_name ,
93
+ method ,
94
+ self .uri ))
95
+ return self .methods [method ](self .parent_obj ,
96
+ request ,
97
+ uri ,
98
+ headers )
99
+ else :
100
+ logger .debug ('Service Router ({0} - {1}): Located Subservice {2} '
101
+ 'on Route {3}. Calling...'
102
+ .format (id (self ),
103
+ self .service_name ,
104
+ self .obj .name ,
105
+ self .uri ))
106
+
107
+ return self .obj .sub_request (method ,
108
+ request ,
109
+ uri ,
110
+ headers )
111
+
112
+
26
113
class StackInABoxService (object ):
27
114
DELETE = 'DELETE'
28
115
GET = 'GET'
@@ -51,33 +138,41 @@ def __init__(self, name):
51
138
.format (self .__id , self .name ))
52
139
53
140
@staticmethod
54
- def __is_regex (uri ):
141
+ def is_regex (uri ):
55
142
regex_type = type (re .compile ('' ))
56
143
return isinstance (uri , regex_type )
57
144
58
145
@staticmethod
59
- def validate_regex (regex ):
146
+ def validate_regex (regex , sub_service ):
60
147
# The regex generated by stackinabox starts with ^
61
148
# and ends with $. Enforce that the provided regex does the same.
62
149
63
150
if regex .pattern .startswith ('^' ) is False :
64
151
logger .debug ('StackInABoxService: Pattern must start with ^' )
65
152
raise InvalidRouteRegexError ('Pattern must start with ^' )
66
153
67
- if regex .pattern .endswith ('$' ) is False :
154
+ # Note: pattern may end with $ even if sub_service is True
155
+ if regex .pattern .endswith ('$' ) is False and sub_service is False :
68
156
logger .debug ('StackInABoxService: Pattern must end with $' )
69
157
raise InvalidRouteRegexError ('Pattern must end with $' )
70
158
159
+ # Enforce that if the pattern does not end with $ that it is a service
160
+ if regex .pattern .endswith ('$' ) is True and sub_service is True :
161
+ logger .debug (
162
+ 'StackInABoxService: Sub-Service RegEx Pattern must not '
163
+ 'end with $' )
164
+ raise InvalidRouteRegexError ('Pattern must end with $' )
165
+
71
166
@staticmethod
72
- def __get_service_regex (base_url , service_url ):
167
+ def get_service_regex (base_url , service_url , sub_service ):
73
168
# if the specified service_url is already a regex
74
169
# then just use. Otherwise create what we need
75
- if StackInABoxService .__is_regex (service_url ):
170
+ if StackInABoxService .is_regex (service_url ):
76
171
logger .debug ('StackInABoxService: Received regex {0} for use...'
77
172
.format (service_url .pattern ))
78
173
79
174
# Validate the regex against StackInABoxService requirement
80
- StackInABoxService .validate_regex (service_url )
175
+ StackInABoxService .validate_regex (service_url , sub_service )
81
176
82
177
return service_url
83
178
else :
@@ -100,8 +195,10 @@ def base_url(self, value):
100
195
value ))
101
196
self .__base_url = value
102
197
for k , v in six .iteritems (self .routes ):
103
- v ['regex' ] = StackInABoxService .__get_service_regex (value ,
104
- v ['uri' ])
198
+ v ['regex' ] = StackInABoxService .get_service_regex (
199
+ value ,
200
+ v ['uri' ],
201
+ v ['handlers' ].is_subservice )
105
202
106
203
def reset (self ):
107
204
logger .debug ('StackInABoxService ({0}): Reset'
@@ -110,10 +207,8 @@ def reset(self):
110
207
logger .debug ('StackInABoxService ({0}): Hosting Service {1}'
111
208
.format (self .__id , self .name ))
112
209
113
- def request (self , method , request , uri , headers ):
114
- logger .debug ('StackInABoxService ({0}:{1}): Received {2} - {3}'
115
- .format (self .__id , self .name , method , uri ))
116
- uri_path = uri
210
+ def try_handle_route (self , route_uri , method , request , uri , headers ):
211
+ uri_path = route_uri
117
212
if '?' in uri :
118
213
logger .debug ('StackInABoxService ({0}:{1}): Found query string '
119
214
'removing for match operation.'
@@ -137,38 +232,47 @@ def request(self, method, request, uri, headers):
137
232
logger .debug ('StackInABoxService ({0}:{1}): Checking if '
138
233
'route {2} handles method {2}...'
139
234
.format (self .__id , self .name , v ['uri' ], method ))
140
- if method in v ['handlers' ]:
141
- logger .debug ('StackInABoxService ({0}:{1}): Calling '
142
- 'handler for route {2} on method {3}...'
143
- .format (self .__id ,
144
- self .name ,
145
- v ['uri' ],
146
- method ))
147
- return v ['handlers' ][method ](self ,
148
- request ,
149
- uri ,
150
- headers )
235
+ return v ['handlers' ](method ,
236
+ request ,
237
+ uri ,
238
+ headers )
151
239
return (500 , headers , 'Server Error' )
152
240
153
- def register (self , method , uri , call_back ):
154
- found = False
241
+ def request (self , method , request , uri , headers ):
242
+ logger .debug ('StackInABoxService ({0}:{1}): Request Received {2} - {3}'
243
+ .format (self .__id , self .name , method , uri ))
244
+ return self .try_handle_route (uri , method , request , uri , headers )
245
+
246
+ def sub_request (self , method , request , uri , headers ):
247
+ logger .debug ('StackInABoxService ({0}:{1}): Sub-Request Received '
248
+ '{2} - {3}'
249
+ .format (self .__id , self .name , method , uri ))
250
+ return self .request (method , request , uri , headers )
155
251
252
+ def create_route (self , uri , sub_service ):
156
253
if uri not in self .routes .keys ():
157
254
logger .debug ('Service ({0}): Creating routes'
158
255
.format (self .name ))
159
256
self .routes [uri ] = {
160
- 'regex' : StackInABoxService .__get_service_regex (self .base_url ,
161
- uri ),
257
+ 'regex' : StackInABoxService .get_service_regex (self .base_url ,
258
+ uri ,
259
+ sub_service ),
162
260
'uri' : uri ,
163
- 'handlers' : {
164
- }
261
+ 'handlers' : StackInABoxServiceRouter (self .name ,
262
+ uri ,
263
+ None ,
264
+ self )
165
265
}
166
266
167
- if method not in self .routes [uri ]['handlers' ].keys ():
168
- logger .debug ('Service ({0}): Adding route for {1}'
169
- .format (self .name , method ))
170
- self .routes [uri ]['handlers' ][method ] = call_back
171
- else :
172
- raise RouteAlreadyRegisteredError (
173
- 'Service ({0}): Route {1} already registered'
174
- .format (self .name , uri ))
267
+ def register (self , method , uri , call_back ):
268
+ found = False
269
+
270
+ self .create_route (uri , False )
271
+ self .routes [uri ]['handlers' ].register_method (method ,
272
+ call_back )
273
+
274
+ def register_subservice (self , uri , service ):
275
+ found = False
276
+
277
+ self .create_route (uri , True )
278
+ self .routes [uri ]['handlers' ].set_subservice (service )
0 commit comments