1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Original Code is gContactSync.
 15  *
 16  * The Initial Developer of the Original Code is
 17  * Josh Geenen <gcontactsync@pirules.org>.
 18  * Portions created by the Initial Developer are Copyright (C) 2008-2009
 19  * the Initial Developer. All Rights Reserved.
 20  *
 21  * Contributor(s):
 22  *
 23  * Alternatively, the contents of this file may be used under the terms of
 24  * either the GNU General Public License Version 2 or later (the "GPL"), or
 25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 26  * in which case the provisions of the GPL or the LGPL are applicable instead
 27  * of those above. If you wish to allow use of your version of this file only
 28  * under the terms of either the GPL or the LGPL, and not to allow others to
 29  * use your version of this file under the terms of the MPL, indicate your
 30  * decision by deleting the provisions above and replace them with the notice
 31  * and other provisions required by the GPL or the LGPL. If you do not delete
 32  * the provisions above, a recipient may use your version of this file under
 33  * the terms of any one of the MPL, the GPL or the LGPL.
 34  *
 35  * ***** END LICENSE BLOCK ***** */
 36 
 37 if (!com) var com = {}; // A generic wrapper variable
 38 // A wrapper for all GCS functions and variables
 39 if (!com.gContactSync) com.gContactSync = {};
 40 
 41 /**
 42  * Sets up an HTTP request.<br>
 43  * The constructor is not all that useful so extend this class if you must
 44  * make repetitive HTTP requests.<br><br>
 45  * You may setup callbacks based on different HTTP status codes:
 46  * <ul>
 47  * <li>0 (offline): use <b>mOnError</b></li>
 48  * <li>200 (OK): use <b>mOnSuccess</b></li>
 49  * <li>201 (CREATED): use <b>mOnCreated</b></li>
 50  * <li>401 (UNAUTHORIZED): use <b>mOn401</b></li>
 51  * <li><anything else>: use <b>mOnError</b></li>
 52  * </ul>
 53  * <br>Sample usage:
 54  * <pre>
 55  * // Create and setup a new HttpRequest
 56  * var myHttpRequest   = new com.gContactSync.HttpRequest();
 57  * myHttpRequest.mUrl  = "http://www.pirules.org";
 58  * myHttpRequest.mType = "GET";
 59  * myHttpRequest.addHeaderItem("Content-length", 0);
 60  * // setup the callbacks
 61  * myHttpRequest.mOnSuccess = function myRequestSuccess(aHttpReq) {
 62  *   com.gContactSync.alert("Request succeeded.  Content:\n\n" + aHttpReq.statusText);
 63  * };
 64  * myHttpRequest.mOnOffline = function myRequestOffline(aHttpReq) {
 65  *   com.gContactSync.alert("You are offline");
 66  * };
 67  * myHttpRequest.mOnError   = function myRequestError(aHttpReq) {
 68  *   com.gContactSync.alert("Request failed...Status: " + aHttpReq.status); 
 69  * };
 70  * // send the request
 71  * myHttpRequest.send();
 72  * </pre>
 73  * @constructor
 74  * @class
 75  */
 76 com.gContactSync.HttpRequest = function gCS_HttpRequest() {
 77   if (window.XMLHttpRequest)
 78     this.mHttpRequest = new XMLHttpRequest();
 79   if (!this.mHttpRequest)
 80     throw "Error - could not create an XMLHttpRequest" +
 81           com.gContactSync.StringBundle.getStr("pleaseReport");
 82 };
 83 
 84 com.gContactSync.HttpRequest.prototype = {
 85   /** Content types */
 86   CONTENT_TYPES: {
 87     /** URL encoded */
 88     URL_ENC: "application/x-www-form-urlencoded",
 89     /** ATOM/XML */
 90     ATOM:    "application/atom+xml",
 91     /** XML */
 92     XML:     "application/xml"
 93   },
 94   /**
 95    * Adds a content override to the header in case a firewall blocks DELETE or
 96    * PUT requests.
 97    * @param aType {string} The type of override.  Must be DELETE or PUT.
 98    */
 99   addContentOverride: function HttpRequest_addContentOverride(aType) {
100     switch (aType) {
101     case "delete":
102     case "DELETE":
103       this.addHeaderItem("X-HTTP-Method-Override", "DELETE");
104       break;
105     case "put":
106     case "PUT":
107       this.addHeaderItem("X-HTTP-Method-Override", "PUT");
108       break;
109     default:
110       throw "Error - type sent to addContentOverride must be DELETE or PUT";
111     }
112   },
113   /**
114    * Adds a header label/value pair to the arrays of header information
115    * @param aLabel {string} The label for the header.
116    * @param aValue {string} The value for the header.
117    */
118   addHeaderItem: function HttpRequest_addHeaderItem(aLabel, aValue) {
119     if (!this.mHeaderLabels) {
120       this.mHeaderLabels = [];
121       this.mHeaderValues = [];
122     }
123     this.mHeaderLabels.push(aLabel);
124     this.mHeaderValues.push(aValue);
125   },
126   /**
127    * Sends the HTTP Request with the information stored in the object.<br>
128    * Note: Setup everything, including the callbacks for different statuses
129    *       including mOnSuccess, mOnError, mOnFail, and mOnCreated first.<br>
130    * See the class documentation for a sample request.
131    */
132   send: function HttpRequest_send() {
133     // log the basic info for debugging purposes
134     com.gContactSync.LOGGER.VERBOSE_LOG("HTTP Request being formed");
135     com.gContactSync.LOGGER.VERBOSE_LOG(" * Caller is: " + this.send.caller.name);
136     com.gContactSync.LOGGER.VERBOSE_LOG(" * URL: " + this.mUrl);
137     com.gContactSync.LOGGER.VERBOSE_LOG(" * Type: " + this.mType);
138     com.gContactSync.LOGGER.VERBOSE_LOG(" * Content-Type: " + this.mContentType);
139     
140     this.mHttpRequest.open(this.mType, this.mUrl, true); // open the request
141 
142     // set the header
143     this.addHeaderItem("Content-Type", this.mContentType);
144     com.gContactSync.LOGGER.VERBOSE_LOG(" * Setting up the header: ");
145 
146     for (var i = 0; i < this.mHeaderLabels.length; i++) {
147       com.gContactSync.LOGGER.VERBOSE_LOG("   o " + this.mHeaderLabels[i] +
148                                           " " + this.mHeaderValues[i]);
149       this.mHttpRequest.setRequestHeader(this.mHeaderLabels[i],
150                                          this.mHeaderValues[i]);
151     }
152     this.mHttpRequest.send(this.mBody); // send the request
153     com.gContactSync.LOGGER.VERBOSE_LOG(" * Request Sent");
154     var httpReq   = this.mHttpRequest,
155         onSuccess = this.mOnSuccess,
156         onOffline = this.mOnOffline,
157         onFail    = this.mOnError,
158         onCreated = this.mOnCreated,
159         on401     = this.mOn401;
160 
161     httpReq.onreadystatechange = function httpReq_readyState() {
162       var callback = [],
163           i;
164       // if the request is done then check the status
165       if (httpReq.readyState === 4) {
166         // this may be called after the address book window is closed
167         // if the window is closed there will be an exception thrown as
168         // explained here - https://www.mozdev.org/bugs/show_bug.cgi?id=20527
169         com.gContactSync.LOGGER.VERBOSE_LOG(" * The request has finished with status: " +
170                                             httpReq.status + "/" +
171                                             (httpReq.status ? httpReq.statusText : "offline"));
172         if (httpReq.status) {
173           com.gContactSync.LOGGER.VERBOSE_LOG(" * Headers:\n" +
174                                               httpReq.getAllResponseHeaders() + "\n");
175         }
176           
177         switch (httpReq.status) { 
178         case 0: // the user is offline
179           callback = onOffline;
180           break;
181         case 201: // 201 CREATED
182           callback = onCreated;
183           break;
184         case 200: // 200 OK
185           callback = onSuccess;
186           break;
187         case 401: // 401 Unauthorized (Token Expired in Gmail)
188           callback = on401;
189           break;
190         default: // other status
191           callback = onFail;
192         }
193         if (callback) {
194           com.gContactSync.LOGGER.VERBOSE_LOG(" * Running the function callback");
195           callback.call(this, httpReq);
196         }
197       } // end of readyState
198     };
199   }
200 };
201