Add header.
[sfrench/samba-autobuild/.git] / source4 / lib / appweb / ejs-2.0 / ejs / system / WIN / ejsHTTP.c
1 /*
2  *      @file   ejsHTTP.c
3  *      @brief  HTTP class for the EJ System Object Model
4  */
5 /********************************** Copyright *********************************/
6 /*
7  *      Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved.
8  */
9 /********************************** Includes **********************************/
10
11 #include        "ejs.h"
12
13 #if UNUSED
14 /*********************************** Defines **********************************/
15
16 #define EJS_WEB_PROPERTY        "-web"
17 #define EJS_HTTP_PROPERTY       "-http"
18
19 #define EJS_HTTP_DISPOSED       550
20
21 /*
22  *      Control structure for one HTTP request structure
23  */
24 typedef struct HTTPControl {
25         Ejs                             *ejs;
26         IWebResp                *webResp;
27         AEECallback             *callback;
28         MprBuf                  *buf;
29         EjsVar                  *thisObj;
30         char                    *url;
31         MprTime                 requestStarted;
32         uint                    timeout;
33 } HTTPControl;
34
35 /****************************** Forward Declarations **************************/
36
37 static void cleanup(HTTPControl *hp);
38 static int      createWeb(Ejs *ejs, EjsVar *thisObj);
39 static void brewCallback(HTTPControl *hp);
40 static int      httpDestructor(Ejs *ejs, EjsVar *vp);
41 static void httpCallback(HTTPControl *hp, int responseCode);
42 static int      setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv);
43
44 /******************************************************************************/
45 /*
46  *      Constructor
47  */
48
49 int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
50 {
51         if (argc != 0 && argc != 2) {
52                 ejsError(ejs, EJS_ARG_ERROR, 
53                         "Bad usage: HTTP([obj = this, method = onComplete]);");
54                 return -1;
55         }
56
57         if (createWeb(ejs, thisObj) < 0) {
58                 return -1;
59         }
60
61         setCallback(ejs, thisObj, argc, argv);
62         return 0;
63 }
64
65 /******************************************************************************/
66
67 static int createWeb(Ejs *ejs, EjsVar *thisObj)
68 {
69         MprApp  *app;
70         void    *web;
71
72         app = mprGetApp(ejs);
73
74         /*
75          *      Create one instance of IWeb for the entire application. Do it here
76          *      so only widgets that require HTTP incurr the overhead.
77          */
78         web = mprGetKeyValue(ejs, "bpWeb");
79         if (web == 0) {
80                 if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) {
81                         ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB");
82                         return -1;
83                 }
84         }
85         mprSetKeyValue(ejs, "bpWeb", web);
86         return 0;
87 }
88
89 /******************************************************************************/
90 /************************************ Methods *********************************/
91 /******************************************************************************/
92 /*
93  *      function setCallback(obj, methodString);
94  */
95
96 static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
97 {
98         if (argc >= 1) {
99                 ejsSetProperty(ejs, thisObj, "obj", argv[0]);
100         } else {
101                 ejsSetProperty(ejs, thisObj, "obj", thisObj);
102         }
103
104         if (argc >= 2) {
105                 ejsSetProperty(ejs, thisObj, "method", argv[1]);
106         } else {
107                 ejsSetPropertyToString(ejs, thisObj, "method", "onComplete");
108         }
109
110         return 0;
111 }
112
113 /******************************************************************************/
114 /*
115  *      function fetch();
116  */
117
118 static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
119 {
120         HTTPControl             *hp;
121         EjsProperty     *pp;
122         MprApp                  *app;
123         IWeb                    *web;
124
125         if (argc != 1 || !ejsVarIsString(argv[0])) {
126                 ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)");
127                 return -1;
128         }
129
130         app = mprGetApp(ejs);
131         web = (IWeb*) mprGetKeyValue(ejs, "bpWeb");
132
133         /*
134          *      Web options
135          *
136          *      WEBOPT_USERAGENT (char*)                sets user agent
137          *      WEBOPT_HANDLERDATA (void*)
138          *      WEBOPT_CONNECTTIMEOUT (uint)    msec
139          *      WEBOPT_CONTENTLENGTH (long)
140          *      WEBOPT_IDLECONNTIMEOUT (int)
141          *      WEBOPT_ACTIVEXACTIONST (uint)   Number of active requests
142          *
143          *      WEBREQUEST_REDIRECT                             redirect transparently
144          *
145          */
146
147         hp = mprAllocType(ejs, HTTPControl);
148         if (hp == 0) {
149                 ejsMemoryError(ejs);
150                 return -1;
151         }
152
153         hp->ejs = ejs;
154         hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF);
155         if (hp->buf == 0) {
156                 mprFree(hp);
157                 ejsMemoryError(ejs);
158                 return -1;
159         }
160
161         /*
162          *      We copy thisObj because we need to preserve both the var and the object.
163          *      We pass the var to brewCallback and so it must persist. The call to
164          *      ejsMakeObjPermanent will stop the GC from collecting the object.
165          */
166         hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY);
167         ejsSetVarName(ejs, hp->thisObj, "internalHttp");
168
169         /*
170          *      Must keep a reference to the http object
171          */
172         ejsMakeObjPermanent(hp->thisObj, 1);
173
174         /*
175          *      Make a property so we can access the HTTPControl structure from other
176          *      methods.
177          */
178         pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0);
179         ejsMakePropertyEnumerable(pp, 0);
180         ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor);
181
182         hp->url = mprStrdup(hp, argv[0]->string);
183
184         hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout");
185         mprGetTime(hp, &hp->requestStarted);
186
187         hp->callback = mprAllocTypeZeroed(hp, AEECallback);
188         CALLBACK_Init(hp->callback, brewCallback, hp);
189
190         hp->webResp = 0;
191         IWEB_GetResponse(web, 
192                 (web, &hp->webResp, hp->callback, hp->url,
193                 WEBOPT_HANDLERDATA, hp, 
194                 WEBOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", 
195                 WEBOPT_CONNECTTIMEOUT, hp->timeout,
196                 WEBOPT_COPYOPTS, TRUE,
197                 WEBOPT_CONTENTLENGTH, 0,
198                 WEBOPT_END));
199
200         ejsSetPropertyToString(ejs, thisObj, "status", "active");
201
202         return 0;
203 }
204
205 /******************************************************************************/
206 /*
207  *      Called whenver the http object is deleted. 
208  */
209
210 static int httpDestructor(Ejs *ejs, EjsVar *thisObj)
211 {
212         HTTPControl             *hp;
213
214         /*
215          *      If the httpCallback has run, then this property will not exist
216          */
217         hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY);
218
219         if (hp) {
220                 cleanup(hp);
221         }
222
223         return 0;
224 }
225
226 /******************************************************************************/
227 /*
228  *      Stop the request immediately without calling the callback
229  */
230
231 static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
232 {
233         HTTPControl             *hp;
234
235         hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY);
236
237         if (hp) {
238                 cleanup(hp);
239         }
240
241         return 0;
242 }
243
244 /******************************************************************************/
245 /*
246  *      Brew HTTP callback. Invoked for any return data.
247  */
248
249 static void brewCallback(HTTPControl *hp)
250 {
251         Ejs                             *ejs;
252         EjsVar                  *thisObj;
253         ISource                 *source;
254         WebRespInfo             *info;
255         char                    data[MPR_BUF_INCR];
256         int                     bytes;
257
258         mprAssert(hp);
259         mprAssert(hp->webResp);
260
261         info = IWEBRESP_GetInfo(hp->webResp);
262
263         if (info == 0) {
264                 mprAssert(info);
265                 /* should not happen */
266                 return;
267         }
268
269         ejs = hp->ejs;
270         thisObj = hp->thisObj;
271
272         if (! WEB_ERROR_SUCCEEDED(info->nCode)) {
273                 ejsSetPropertyToString(ejs, thisObj, "status", "error");
274                 httpCallback(hp, info->nCode);
275                 return;
276         }
277
278         if (hp->timeout) {
279                 if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) {
280                         ejsSetPropertyToString(ejs, thisObj, "status", "timeout");
281                         httpCallback(hp, 504);
282                         return;
283                 }
284         }
285
286         /*
287          *      Normal success
288          */
289         source = info->pisMessage;
290         mprAssert(source);
291
292         bytes = ISOURCE_Read(source, data, sizeof(data));
293
294         switch (bytes) {
295         case ISOURCE_WAIT:                                                              // No data yet
296                 ISOURCE_Readable(source, hp->callback);
297                 break;
298
299         case ISOURCE_ERROR:
300                 ejsSetPropertyToString(ejs, thisObj, "status", "error");
301                 httpCallback(hp, info->nCode);
302                 break;
303
304         case ISOURCE_END:
305                 mprAddNullToBuf(hp->buf);
306                 ejsSetPropertyToString(ejs, thisObj, "status", "complete");
307                 httpCallback(hp, info->nCode);
308                 break;
309
310         default:
311                 if (bytes > 0) {
312                         if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) {
313                                 ejsSetPropertyToString(ejs, thisObj, "status", "partialData");
314                                 httpCallback(hp, 500);
315                         }
316                 }
317                 ISOURCE_Readable(source, hp->callback);
318                 break;
319         }
320 }
321
322 /******************************************************************************/
323 /*
324  *      Invoke the HTTP completion method
325  */
326
327 static void httpCallback(HTTPControl *hp, int responseCode)
328 {
329         Ejs                             *ejs;
330         EjsVar                  *thisObj, *callbackObj;
331         MprArray                *args;
332         char                    *msg;
333         const char              *callbackMethod;
334
335         mprAssert(hp);
336         mprAssert(hp->webResp);
337
338         thisObj = hp->thisObj;
339         ejs = hp->ejs;
340
341         ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode);
342         if (mprGetBufLength(hp->buf) > 0) {
343                 ejsSetPropertyToBinaryString(ejs, thisObj, "responseData", 
344                         mprGetBufStart(hp->buf), mprGetBufLength(hp->buf));
345         }
346
347         callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj");
348         callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method");
349
350         if (callbackObj != 0 && callbackMethod != 0) {
351
352                 args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS);
353                 mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY));
354
355                 if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) {
356                         msg = ejsGetErrorMsg(ejs);
357                         mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg);
358                 }
359                 ejsFreeMethodArgs(ejs, args);
360
361         } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) {
362                 msg = ejsGetErrorMsg(ejs);
363                 mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg);
364         }
365
366         cleanup(hp);
367 }
368
369 /******************************************************************************/
370 /*
371  *      Cleanup
372  */
373
374 static void cleanup(HTTPControl *hp)
375 {
376         Ejs                     *ejs;
377         MprApp          *app;
378         int                     rc;
379
380         mprAssert(hp);
381         mprAssert(hp->webResp);
382
383         ejs = hp->ejs;
384
385         if (hp->webResp) {
386                 rc = IWEBRESP_Release(hp->webResp);
387                 // mprAssert(rc == 0);
388                 hp->webResp = 0;
389         }
390
391         if (hp->callback) {
392                 CALLBACK_Cancel(hp->callback);
393                 mprFree(hp->callback);
394                 hp->callback = 0;
395         }               
396
397         /*
398          *      Once the property is deleted, then if the destructor runs, it will
399          *      notice that the EJS_HTTP_PROPERTY is undefined.
400          */
401         ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY);
402
403         /*
404          *      Allow garbage collection to work on thisObj
405          */
406         ejsMakeObjPermanent(hp->thisObj, 0);
407         ejsFreeVar(ejs, hp->thisObj);
408
409         mprFree(hp->buf);
410         mprFree(hp->url);
411
412         mprFree(hp);
413
414         app = mprGetApp(ejs);
415
416
417         ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0);
418 }
419
420 /******************************************************************************/
421 /******************************** Initialization ******************************/
422 /******************************************************************************/
423
424 int ejsDefineHTTPClass(Ejs *ejs)
425 {
426         EjsVar  *httpClass;
427
428         httpClass =  
429                 ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor);
430         if (httpClass == 0) {
431                 return MPR_ERR_CANT_INITIALIZE;
432         }
433
434         /*
435          *      Define the methods
436          */
437         ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0);
438         ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0);
439         ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0);
440
441 #if FUTURE
442         ejsDefineCMethod(ejs, httpClass, "put", put, 0);
443         ejsDefineCMethod(ejs, httpClass, "upload", upload, 0);
444         ejsDefineCMethod(ejs, httpClass, "addUploadFile", addUploadFile, 0);
445         ejsDefineCMethod(ejs, httpClass, "addPostData", addPostData, 0);
446         ejsDefineCMethod(ejs, httpClass, "setUserPassword", setUserPassword, 0);
447         ejsDefineCMethod(ejs, httpClass, "addCookie", addCookie, 0);
448 #endif
449
450         /*
451          *      Define properties 
452          */
453         ejsSetPropertyToString(ejs, httpClass, "status", "inactive");
454
455         /*      This default should come from player.xml */
456
457         ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000);
458         ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0);
459
460         return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0;
461 }
462
463 /******************************************************************************/
464
465 void ejsTermHTTPClass(Ejs *ejs)
466 {
467         IWeb            *web;
468         int                     rc;
469
470         web = (IWeb*) mprGetKeyValue(ejs, "bpWeb");
471         if (web) {
472                 rc = IWEB_Release(web);
473                 mprAssert(rc == 0);
474         }
475 }
476
477 #endif
478 /******************************************************************************/
479
480 /*
481  * Local variables:
482  * tab-width: 4
483  * c-basic-offset: 4
484  * End:
485  * vim:tw=78
486  * vim600: sw=4 ts=4 fdm=marker
487  * vim<600: sw=4 ts=4
488  */