1 /**
  2  * @fileOverview Helpers for the HTTP response.
  3  */
  4 
  5 /** @ignore */
  6 var _ajn = appjet._native;
  7 
  8 /**
  9  * @type object
 10  * @constructor
 11  * @class Helpers for the HTTP response.  
 12  */
 13 var response = {};
 14 
 15 /**
 16  * Halts the program immediately and returns 403 Forbidden error to the user.
 17  */
 18 response.forbid = function() {
 19   _ajn.response_error(403, "Forbidden");
 20 };
 21 
 22 /**
 23  * Halts the program immediately, optionally printing the page so far.
 24  *
 25  * @param {boolean} renderCurrentPage if false, an empty page will be rendered,
 26  *   otherwise calls to print() so far will be displayed.  Either way, no more
 27  *   code will be executed.
 28  */
 29 response.stop = function(renderCurrentPage) {
 30   if (renderCurrentPage !== false) {
 31     var p = page.render();
 32     if (p.length > 0)
 33       _ajn.write(p.join(''));
 34   }
 35   _ajn.response_stop();
 36 };
 37 
 38 /**
 39  * Halts the program immediately and returns a 404 not found error to the user.
 40  */
 41 response.notFound = function() {
 42   _ajn.response_error(404, "Not found");
 43 };
 44 
 45 /**
 46  * Halts the program immediately and sends an HTTP redirect response (302),
 47  * redirecting to the given path (relative or absolute).
 48  *
 49  * @param {string} path The new path
 50  */
 51 response.redirect = function(path) {
 52   if ((! path) && path != "")
 53     throw new Error("Invalid redirect: "+path)
 54   _ajn.response_redirect(path);
 55 };
 56 
 57 /**
 58  * Sets the status code in the HTTP response.
 59  *
 60  * @param {number} newCode
 61  */
 62 response.setStatusCode = function(newCode) {
 63   _ajn.response_setStatusCode(newCode);
 64 };
 65 
 66 /**
 67  * Sets any header of the HTTP response.
 68  *
 69  * @example
 70 response.setHeader('Cache-Control', 'no-cache');
 71  *
 72  * @param {string} name
 73  * @param {string} value
 74  */
 75 response.setHeader = function(name, value) {
 76   _ajn.response_setHeader(name, value);
 77 };
 78 
 79 /**
 80  * Adds the name,value pair to the headers.  Useful for headers that are
 81  * allowed to repeat, such as Set-Cookie.
 82  *
 83  * @param {string} name
 84  * @param {string} value
 85  */
 86 response.addHeader = function(name, value) {
 87   _ajn.response_addHeader(name, value);
 88 };
 89 
 90 /**
 91  * Low-level hook for writing raw data to the response.
 92  * @param {string} data will be written, verbatim, to the HTTP resonse.
 93  */
 94 response.write = function(data) {
 95   _ajn.write(data);
 96 };
 97 
 98 /**
 99  * Low-level hook for writing raw byte data to the response. Especially
100  * useful for writing the result of a <code>wget</code> of image data,
101  * or writing an uploaded file.
102  * @param {string} data will be written, verbatim, to the HTTP resonse.
103  */
104 response.writeBytes = function(data) {
105   _ajn.writeBytes(data);
106 };
107 
108 //----------------------------------------------------------------
109 // Cookies!
110 //----------------------------------------------------------------
111 
112 /**
113  * Set a cookie in the response.
114  *
115  * @example
116 response.setCookie({
117   name: "SessionID",
118   value: "25",
119   secure: true,
120   expires: 14 // 14 days
121 });
122  *
123  * @param {object} cookieObject This may contain any of the following:
124 <ul>
125   <li>name (required): The name of the cookie</li>
126   <li>value (required): The value of the cookie.  (Note: this value will be escaped).
127   <li>expires (optional): If an integer, means number of days until it expires;
128         if a Date object, means exact date on which to expire.</li>
129   <li>domain (optional): The cookie domain</li>
130   <li>path (optional): To restrict the cookie to a specific path.</li>
131   <li>secure (optional): Whether this cookie should only be sent securely.</li>
132 </ul>
133  */
134 response.setCookie = function(cookieObject) {
135   response.addHeader('Set-Cookie', _cookiestring(cookieObject));
136 };
137 
138 
139 /**
140  * Tells the client to delete the cookie of the given name (by setting
141  * its expiration time to zero).
142  * @param {string} name The name of the cookie to delete.
143  */
144 response.deleteCookie = function(name) {
145   response.setCookie({name: name, value: '', expires: 0});
146 };
147 
148 /**
149  * Sets the Content-Type header of the response.  If the content-type includes
150  * a charset, that charset is used to send the response.
151  * @param {string} contentType the new content-type
152  */
153 response.setContentType = function(contentType) {
154   _ajn.response_setContentType(contentType);
155 }
156 
157 /** @ignore */
158 function _cookiestring(c) {
159   var x = '';
160   if (!c.name) { throw new Error('cookie name is required'); }
161   if (!c.value) { c.value = ''; }
162   x += (c.name + '=' + escape(c.value));
163 
164   // expires
165   if (c.expires instanceof Date) {
166     x += ('; expires='+_cookiedate(c.expires));
167   }
168   if (typeof(c.expires) == 'number') {
169     var today = (new Date()).valueOf();
170     var d = new Date(today + 86400000*c.expires);
171     x += ('; expires='+_cookiedate(d));
172   }
173 
174   // domain
175   if (c.domain) { x += ('; domain='+c.domain); }
176 
177   // path
178   if (c.path) { x += ('; path='+c.path); }
179 
180   // secure
181   if (c.secure == true) { x += '; secure'; }
182 
183   return x;
184 }
185 
186 /** @ignore */
187 function _cookiedate(d) {
188   var x = d.toGMTString();
189   var p = x.split(' ');
190   return [p[0], [p[1], p[2], p[3]].join('-'), p[4], p[5]].join(' ');
191 }
192 
193 /**
194  * Tells the client to cache the page. By default, clients are told to
195  * not never cache pages. (To send no caching-related headers at all, pass
196  * <code>undefined</code>.)
197  * @param {boolean} cacheable
198  */
199 response.setCacheable = function(cacheable) {
200   appjet._internal.cacheable = cacheable;
201 }
202 
203 /** @ignore */
204 appjet._internal.cacheable = false;
205