import ClientDriver from './client-driver.js' export default class XHRClientDriver extends ClientDriver{ /* (1) Creates a client driver * * @_resource Target resource * (typically a hostname without trailing HTTP:// nor HTTPS://) * @_auth Authentication object * * * [fornat::AuthObject] * { * ssl: * } * ---------------------------------------------------------*/ constructor(_resource, _auth={ssl:true}){ /* (0) Parent check */ if( super(_resource, _auth).error ){ this.force_close = true; return; } /* (1) Create useful attributes */ this.xhr = null; /* (2) Manage _resource format (remove http://, https://) */ this.resource = _resource.replace(/^https?:\/\//, ''); /* (3) Manage @proto default values */ this.proto = typeof _auth !== 'object' && _auth['ssl'] != null && _auth.ssl === true; this.proto = (this.proto) ? 'https://' : 'http://'; } /* (2) Binds the client to the resource * * @return bound Whether the binding has been successful * ---------------------------------------------------------*/ bind(){ /* (0) Parent check */ if( !super.bind() ) return false; /* (1) Create XHR instance */ this.xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttpRequest'); /* (2) Manage after request callbacks */ this.xhr.onreadystatechange = function(){ switch(this.xhr.readyState){ // Request sent case 4: // Got response if( this.xhr.status === 200 ){ this.event.onreceive(this.xhr.responseText); this.event.onclose(); // Request error }else this.event.onclose(); break; } }.bind(this); /* (3) State is now CONNECTED (ready to send) */ this.state = ClientDriver.STATE.CONNECTED; /* (4) Bind to callback onconnected() */ this.event.onconnected(); /* (5) Return success */ return true; } /* (3) Send request * * @_request Request data * * @return sent Whether the request has been successful * ---------------------------------------------------------*/ send(_request){ /* (1) Fast argument check ---------------------------------------------------------*/ /* (1) Parent check */ if( !super.send(_request) ) return false; /* (2) Error: invalid _request.path */ if( typeof _request.path !== 'string' ){ this.state = ClientDriver.STATE.CONNECTED; return false; } /* (3) Default @http_token value */ let http_token = null; if( typeof _request.http_token === 'string' ) http_token = _request.http_token; /* (2) Manage _request.path argument ---------------------------------------------------------*/ /* (1) Error: invalid path format */ if( !/^([A-Z]+) (.+)/i.test(_request.path) ){ this.state = ClientDriver.STATE.CONNECTED; return; } /* (2) Extract path data */ let http_method = RegExp.$1; let http_uri = RegExp.$2; /* (3) Manage _request.form argument ---------------------------------------------------------*/ /* (1) Helpers */ let is_object = typeof _request.form === 'object'; let is_formdata = is_object && _request.form instanceof FormData; /* (2) Create default value for _request.form */ var form_data = new FormData(); /* (3) If already FormData object -> store as it is */ if( is_object && is_formdata ) form_data = _request.form; /* (4) If just a raw object -> convert to FormData */ if( is_object && !is_formdata ) Object.keys(_request.form).map( (k) => form_data.append(k, _request.form[k]) ); /* (4) Open connection ---------------------------------------------------------*/ /* (1) Build full request URI */ let request_uri = this.resource.split(/\/$/).concat(http_uri.split(/^\//)).filter((v) => v.trim().length).join('/'); /* (2) Build protocol (http, https) and http_token */ let protocol = http_token != null ? `${this.proto}${http_token}@` : this.proto; /* (3) Open connection */ this.xhr.open(http_method, `${protocol}${request_uri}`, true); /* (4) Send request */ this.xhr.send(form_data); return true; } }