3 * @brief HTTP class for the EJ System Object Model
5 /********************************** Copyright *********************************/
7 * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved.
9 /********************************** Includes **********************************/
14 /*********************************** Defines **********************************/
16 #define EJS_WEB_PROPERTY "-web"
17 #define EJS_HTTP_PROPERTY "-http"
19 #define EJS_HTTP_DISPOSED 550
22 * Control structure for one HTTP request structure
24 typedef struct HTTPControl {
27 AEECallback *callback;
31 MprTime requestStarted;
35 /****************************** Forward Declarations **************************/
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);
44 /******************************************************************************/
49 int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
51 if (argc != 0 && argc != 2) {
52 ejsError(ejs, EJS_ARG_ERROR,
53 "Bad usage: HTTP([obj = this, method = onComplete]);");
57 if (createWeb(ejs, thisObj) < 0) {
61 setCallback(ejs, thisObj, argc, argv);
65 /******************************************************************************/
67 static int createWeb(Ejs *ejs, EjsVar *thisObj)
75 * Create one instance of IWeb for the entire application. Do it here
76 * so only widgets that require HTTP incurr the overhead.
78 web = mprGetKeyValue(ejs, "bpWeb");
80 if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) {
81 ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB");
85 mprSetKeyValue(ejs, "bpWeb", web);
89 /******************************************************************************/
90 /************************************ Methods *********************************/
91 /******************************************************************************/
93 * function setCallback(obj, methodString);
96 static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
99 ejsSetProperty(ejs, thisObj, "obj", argv[0]);
101 ejsSetProperty(ejs, thisObj, "obj", thisObj);
105 ejsSetProperty(ejs, thisObj, "method", argv[1]);
107 ejsSetPropertyToString(ejs, thisObj, "method", "onComplete");
113 /******************************************************************************/
118 static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
125 if (argc != 1 || !ejsVarIsString(argv[0])) {
126 ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)");
130 app = mprGetApp(ejs);
131 web = (IWeb*) mprGetKeyValue(ejs, "bpWeb");
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
143 * WEBREQUEST_REDIRECT redirect transparently
147 hp = mprAllocType(ejs, HTTPControl);
154 hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF);
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.
166 hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY);
167 ejsSetVarName(ejs, hp->thisObj, "internalHttp");
170 * Must keep a reference to the http object
172 ejsMakeObjPermanent(hp->thisObj, 1);
175 * Make a property so we can access the HTTPControl structure from other
178 pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0);
179 ejsMakePropertyEnumerable(pp, 0);
180 ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor);
182 hp->url = mprStrdup(hp, argv[0]->string);
184 hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout");
185 mprGetTime(hp, &hp->requestStarted);
187 hp->callback = mprAllocTypeZeroed(hp, AEECallback);
188 CALLBACK_Init(hp->callback, brewCallback, hp);
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,
200 ejsSetPropertyToString(ejs, thisObj, "status", "active");
205 /******************************************************************************/
207 * Called whenver the http object is deleted.
210 static int httpDestructor(Ejs *ejs, EjsVar *thisObj)
215 * If the httpCallback has run, then this property will not exist
217 hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY);
226 /******************************************************************************/
228 * Stop the request immediately without calling the callback
231 static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
235 hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY);
244 /******************************************************************************/
246 * Brew HTTP callback. Invoked for any return data.
249 static void brewCallback(HTTPControl *hp)
255 char data[MPR_BUF_INCR];
259 mprAssert(hp->webResp);
261 info = IWEBRESP_GetInfo(hp->webResp);
265 /* should not happen */
270 thisObj = hp->thisObj;
272 if (! WEB_ERROR_SUCCEEDED(info->nCode)) {
273 ejsSetPropertyToString(ejs, thisObj, "status", "error");
274 httpCallback(hp, info->nCode);
279 if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) {
280 ejsSetPropertyToString(ejs, thisObj, "status", "timeout");
281 httpCallback(hp, 504);
289 source = info->pisMessage;
292 bytes = ISOURCE_Read(source, data, sizeof(data));
295 case ISOURCE_WAIT: // No data yet
296 ISOURCE_Readable(source, hp->callback);
300 ejsSetPropertyToString(ejs, thisObj, "status", "error");
301 httpCallback(hp, info->nCode);
305 mprAddNullToBuf(hp->buf);
306 ejsSetPropertyToString(ejs, thisObj, "status", "complete");
307 httpCallback(hp, info->nCode);
312 if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) {
313 ejsSetPropertyToString(ejs, thisObj, "status", "partialData");
314 httpCallback(hp, 500);
317 ISOURCE_Readable(source, hp->callback);
322 /******************************************************************************/
324 * Invoke the HTTP completion method
327 static void httpCallback(HTTPControl *hp, int responseCode)
330 EjsVar *thisObj, *callbackObj;
333 const char *callbackMethod;
336 mprAssert(hp->webResp);
338 thisObj = hp->thisObj;
341 ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode);
342 if (mprGetBufLength(hp->buf) > 0) {
343 ejsSetPropertyToBinaryString(ejs, thisObj, "responseData",
344 mprGetBufStart(hp->buf), mprGetBufLength(hp->buf));
347 callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj");
348 callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method");
350 if (callbackObj != 0 && callbackMethod != 0) {
352 args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS);
353 mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY));
355 if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) {
356 msg = ejsGetErrorMsg(ejs);
357 mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg);
359 ejsFreeMethodArgs(ejs, args);
361 } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) {
362 msg = ejsGetErrorMsg(ejs);
363 mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg);
369 /******************************************************************************/
374 static void cleanup(HTTPControl *hp)
381 mprAssert(hp->webResp);
386 rc = IWEBRESP_Release(hp->webResp);
387 // mprAssert(rc == 0);
392 CALLBACK_Cancel(hp->callback);
393 mprFree(hp->callback);
398 * Once the property is deleted, then if the destructor runs, it will
399 * notice that the EJS_HTTP_PROPERTY is undefined.
401 ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY);
404 * Allow garbage collection to work on thisObj
406 ejsMakeObjPermanent(hp->thisObj, 0);
407 ejsFreeVar(ejs, hp->thisObj);
414 app = mprGetApp(ejs);
417 ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0);
420 /******************************************************************************/
421 /******************************** Initialization ******************************/
422 /******************************************************************************/
424 int ejsDefineHTTPClass(Ejs *ejs)
429 ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor);
430 if (httpClass == 0) {
431 return MPR_ERR_CANT_INITIALIZE;
437 ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0);
438 ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0);
439 ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0);
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);
453 ejsSetPropertyToString(ejs, httpClass, "status", "inactive");
455 /* This default should come from player.xml */
457 ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000);
458 ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0);
460 return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0;
463 /******************************************************************************/
465 void ejsTermHTTPClass(Ejs *ejs)
470 web = (IWeb*) mprGetKeyValue(ejs, "bpWeb");
472 rc = IWEB_Release(web);
478 /******************************************************************************/
486 * vim600: sw=4 ts=4 fdm=marker