1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | "use strict";
|
14 |
|
15 | var fluid = fluid || require("infusion"),
|
16 | kettle = fluid.registerNamespace("kettle");
|
17 |
|
18 | fluid.defaults("kettle.requests", {
|
19 | gradeNames: ["fluid.component"],
|
20 | events: {
|
21 | createRequest: null
|
22 | },
|
23 | dynamicComponents: {
|
24 | request: {
|
25 | createOnEvent: "createRequest",
|
26 | type: "{arguments}.0.type",
|
27 | options: "{arguments}.0.options"
|
28 | }
|
29 | }
|
30 | });
|
31 |
|
32 |
|
33 | kettle.requestMemberName = fluid.typeNameToMemberName("kettle.request");
|
34 |
|
35 | kettle.getCurrentRequest = function () {
|
36 | return fluid.resolveRootComponent[kettle.requestMemberName];
|
37 | };
|
38 |
|
39 | kettle.markActiveRequest = function (request) {
|
40 | var parent = fluid.resolveRootComponent,
|
41 | memberName = kettle.requestMemberName,
|
42 | instantiator = fluid.globalInstantiator,
|
43 | innerRequest = kettle.getCurrentRequest();
|
44 | if (request && innerRequest && innerRequest !== request) {
|
45 | fluid.fail("Error marking thread to request " + request.id + " - this thread is already marked to request " + innerRequest.id + " . Make sure to invoke this request asynchronously.");
|
46 | }
|
47 | if (request) {
|
48 | if (innerRequest !== request) {
|
49 | if (fluid.isDestroyed(request)) {
|
50 | fluid.fail("Error marking thread to request " + request.id + " which has already been destroyed");
|
51 | }
|
52 | instantiator.recordKnownComponent(parent, request, memberName, false);
|
53 | }
|
54 | } else {
|
55 | if (parent[memberName]) {
|
56 | instantiator.clearComponent(parent, memberName);
|
57 | }
|
58 | }
|
59 | return innerRequest;
|
60 | };
|
61 |
|
62 |
|
63 |
|
64 | kettle.withRequest = function (request, callback) {
|
65 | return function wrappedCallback() {
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | if (request && fluid.isDestroyed(request)) {
|
71 | fluid.log("Failing to resume callback for request " + request.id + " which has already concluded");
|
72 | return;
|
73 | }
|
74 | var innerRequest = kettle.markActiveRequest(request);
|
75 | if (innerRequest) {
|
76 | return callback.apply(null, arguments);
|
77 | } else {
|
78 | try {
|
79 | return callback.apply(null, arguments);
|
80 | } catch (err) {
|
81 | fluid.onUncaughtException.fire(err);
|
82 | } finally {
|
83 | kettle.markActiveRequest(null);
|
84 | }
|
85 | }
|
86 | };
|
87 | };
|
88 |
|
89 |
|
90 |
|
91 | kettle.wrapCallback = function (callback) {
|
92 | var request = kettle.getCurrentRequest();
|
93 | return kettle.withRequest(request, callback);
|
94 | };
|
95 |
|
96 | fluid.defaults("kettle.request", {
|
97 | gradeNames: ["fluid.component"],
|
98 | mergePolicy: {
|
99 | requestMiddleware: "noexpand"
|
100 | },
|
101 | invokers: {
|
102 | handleRequest: "fluid.notImplemented",
|
103 | handleFullRequest: "fluid.notImplemented"
|
104 | },
|
105 | events: {
|
106 | onHandle: null,
|
107 | onSuccess: null,
|
108 | onError: null,
|
109 | onRequestEnd: null,
|
110 | onRequestSuccess: null,
|
111 | onRequestError: null
|
112 | },
|
113 |
|
114 | req: "{arguments}.1",
|
115 | res: "{arguments}.2",
|
116 | next: "{arguments}.3",
|
117 | members: {
|
118 | req: "{that}.options.req",
|
119 | res: "{that}.options.res",
|
120 | next: "{that}.options.next",
|
121 | |
122 |
|
123 |
|
124 |
|
125 | handlerPromise: "@expand:fluid.promise()"
|
126 | },
|
127 | listeners: {
|
128 | "onCreate.activate": "kettle.request.activate",
|
129 | "onHandle.handleRequest": {
|
130 | funcName: "kettle.request.handleRequest",
|
131 | args: "{that}"
|
132 | },
|
133 | "onSuccess.forward": "kettle.request.forwardPromise(resolve, {that}.handlerPromise, {arguments}.0)",
|
134 | "onError.forward": "kettle.request.forwardPromise(reject, {that}.handlerPromise, {arguments}.0)"
|
135 | }
|
136 | });
|
137 |
|
138 |
|
139 | kettle.request.notFoundHandler = function (request) {
|
140 |
|
141 | if (!request.handlerPromise.disposition) {
|
142 | request.handlerPromise.reject({statusCode: 404, message: "Cannot " + request.req.method + " " + request.req.originalUrl});
|
143 | }
|
144 | };
|
145 |
|
146 | kettle.request.forwardPromise = function (method, promise, val) {
|
147 | if (!promise.disposition) {
|
148 | promise[method](val);
|
149 | } else {
|
150 |
|
151 | fluid.log("Error in forwarding result ", val, " to promise " + method + ": promise has already received " + promise.disposition);
|
152 | }
|
153 | };
|
154 |
|
155 | kettle.request.activate = function (that) {
|
156 | that.req.fluidRequest = that;
|
157 | kettle.markActiveRequest(that);
|
158 | };
|
159 |
|
160 | kettle.request.clear = function () {
|
161 | kettle.markActiveRequest(null);
|
162 | };
|
163 |
|
164 | kettle.request.handleRequest = function (that) {
|
165 | try {
|
166 | that.handleRequest(that);
|
167 | } catch (err) {
|
168 | if (!that.handlerPromise.disposition) {
|
169 | that.handlerPromise.reject({message: err.message, stack: err.stack});
|
170 | }
|
171 | } finally {
|
172 | kettle.markActiveRequest(null);
|
173 | }
|
174 | };
|
175 |
|
176 |
|
177 |
|
178 | kettle.request.handleRequestTask = function (request) {
|
179 | if (!request.res.finished) {
|
180 | request.events.onHandle.fire(request);
|
181 | }
|
182 | return request.handlerPromise;
|
183 | };
|
184 |
|
185 | fluid.defaults("kettle.request.mismatch", {
|
186 | requestMiddleware: {
|
187 | mismatch: {
|
188 | middleware: "{middlewareHolder}.mismatch",
|
189 | priority: "first"
|
190 | }
|
191 | }
|
192 | });
|
193 |
|
194 | fluid.defaults("kettle.request.http", {
|
195 | gradeNames: ["kettle.request"],
|
196 | invokers: {
|
197 | handleFullRequest: "kettle.request.http.handleFullRequest"
|
198 | },
|
199 | listeners: {
|
200 | "onCreate.ensureResponseDisposes": {
|
201 | funcName: "kettle.request.http.ensureResponseDisposes",
|
202 | priority: "before:handleRequest"
|
203 | },
|
204 | "onRequestError.handle": {
|
205 | funcName: "kettle.request.http.errorHandler",
|
206 | args: ["{that}.res", "{arguments}.0"]
|
207 | },
|
208 | "onRequestSuccess.handle": {
|
209 | funcName: "kettle.request.http.successHandler",
|
210 | args: ["{that}", "{arguments}.0"]
|
211 | }
|
212 | }
|
213 | });
|
214 |
|
215 |
|
216 | fluid.defaults("kettle.request.http.mismatch", {
|
217 | gradeNames: ["kettle.request.http", "kettle.request.mismatch"],
|
218 | invokers: {
|
219 | handleRequest: "fluid.identity"
|
220 | }
|
221 | });
|
222 |
|
223 | kettle.request.http.handleFullRequest = function (request, fullRequestPromise, next) {
|
224 | fullRequestPromise.then(function (response) {
|
225 | request.events.onRequestSuccess.fire(response);
|
226 | next();
|
227 | }, function (err) {
|
228 | request.events.onRequestError.fire(err);
|
229 |
|
230 | next();
|
231 | });
|
232 | };
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | kettle.request.http.errorHandler = function (res, error) {
|
240 | var outError = fluid.extend(true, {
|
241 | isError: true,
|
242 | message: "Unknown error",
|
243 | statusCode: 500
|
244 | }, error);
|
245 | if (error.message) {
|
246 | outError.message = error.message;
|
247 | }
|
248 | res.status(outError.statusCode).json(fluid.censorKeys(outError, ["statusCode"]));
|
249 | return outError;
|
250 | };
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 | kettle.request.http.successHandler = function (request, response) {
|
258 | if (request.req.method.toLowerCase() === "options") {
|
259 | request.res.status(200).end();
|
260 | return;
|
261 | }
|
262 | if (typeof(response) !== "string") {
|
263 | request.res.json(response);
|
264 | } else {
|
265 | request.res.status(200).send(response);
|
266 | }
|
267 | };
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | kettle.request.http.ensureResponseDisposes = function (that) {
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | fluid.each(["close", "finish", "end", "error"], function addListener(event) {
|
280 | that.res.on(event, function eventListener() {
|
281 |
|
282 | if (!fluid.isDestroyed(that)) {
|
283 | that.events.onRequestEnd.fire();
|
284 | that.destroy();
|
285 | }
|
286 | });
|
287 | });
|
288 | };
|