Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nested and on-the-fly functions removed, bind introduced instead #43

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
153 changes: 75 additions & 78 deletions lib/request.js
Expand Up @@ -24,16 +24,20 @@ function decideMode (preferBinary) {
}

var ClientRequest = module.exports = function (opts) {
var self = this
stream.Writable.call(self)
stream.Writable.call(this)

self._opts = opts
self._body = []
self._headers = {}
this._opts = opts
this._body = []
this._headers = {}

///using _notdestroyed for successfull cleaning in destroy: will set this._notdestroyed to null in order to release variable and will be false of if's ....
this._notdestroyed = true
this._xhr = null
this._response = null
if (opts.auth)
self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64'))
this.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64'))
Object.keys(opts.headers).forEach(function (name) {
self.setHeader(name, opts.headers[name])
this.setHeader(name, opts.headers[name])
})

var preferBinary
Expand All @@ -50,138 +54,130 @@ var ClientRequest = module.exports = function (opts) {
} else {
throw new Error('Invalid value for opts.mode')
}
self._mode = decideMode(preferBinary)
this._mode = decideMode(preferBinary)

self.on('finish', function () {
self._onFinish()
})
this.on('finish', this._onFinish.bind(this));
}

inherits(ClientRequest, stream.Writable)

ClientRequest.prototype.setHeader = function (name, value) {
var self = this
var lowerName = name.toLowerCase()
// This check is not necessary, but it prevents warnings from browsers about setting unsafe
// headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but
// http-browserify did it, so I will too.
if (unsafeHeaders.indexOf(lowerName) !== -1)
return

self._headers[lowerName] = {
this._headers[lowerName] = {
name: name,
value: value
}
}

ClientRequest.prototype.getHeader = function (name) {
var self = this
return self._headers[name.toLowerCase()].value
return this._headers[name.toLowerCase()].value
}

ClientRequest.prototype.removeHeader = function (name) {
var self = this
delete self._headers[name.toLowerCase()]
delete this._headers[name.toLowerCase()]
}

ClientRequest.prototype._onFinish = function () {
var self = this
function _toArrayBuffer(buffer) {
return toArrayBuffer(buffer)
}

if (self._destroyed)
function _onFetchSuccess (cr, response){
cr._fetchResponse = response
cr._connect()
}

ClientRequest.prototype._onFinish = function () {
if (!this._notdestroyed)
return
var opts = self._opts
var opts = this._opts

var headersObj = self._headers
var headersObj = this._headers
var body
if (opts.method === 'POST' || opts.method === 'PUT' || opts.method === 'PATCH') {
if (capability.blobConstructor) {
body = new global.Blob(self._body.map(function (buffer) {
return toArrayBuffer(buffer)
}), {
body = new global.Blob(this._body.map(_toArrayBuffer), {
type: (headersObj['content-type'] || {}).value || ''
})
} else {
// get utf8 string
body = Buffer.concat(self._body).toString()
body = Buffer.concat(this._body).toString()
}
}

if (self._mode === 'fetch') {
if (this._mode === 'fetch') {
var headers = Object.keys(headersObj).map(function (name) {
return [headersObj[name].name, headersObj[name].value]
})

global.fetch(self._opts.url, {
method: self._opts.method,
global.fetch(this._opts.url, {
method: this._opts.method,
headers: headers,
body: body,
mode: 'cors',
credentials: opts.withCredentials ? 'include' : 'same-origin'
}).then(function (response) {
self._fetchResponse = response
self._connect()
}, function (reason) {
self.emit('error', reason)
})
}).then(_onFetchSuccess.bind(null, this)), this.emit.bind(this, 'error');
} else {
var xhr = self._xhr = new global.XMLHttpRequest()
var xhr = this._xhr = new global.XMLHttpRequest()
try {
xhr.open(self._opts.method, self._opts.url, true)
xhr.open(this._opts.method, this._opts.url, true)
} catch (err) {
process.nextTick(function () {
self.emit('error', err)
})
process.nextTick(this.emit.bind(this, 'error', err));
return
}

// Can't set responseType on really old browsers
if ('responseType' in xhr)
xhr.responseType = self._mode.split(':')[0]
xhr.responseType = this._mode.split(':')[0]

if ('withCredentials' in xhr)
xhr.withCredentials = !!opts.withCredentials

if (self._mode === 'text' && 'overrideMimeType' in xhr)
if (this._mode === 'text' && 'overrideMimeType' in xhr)
xhr.overrideMimeType('text/plain; charset=x-user-defined')

Object.keys(headersObj).forEach(function (name) {
xhr.setRequestHeader(headersObj[name].name, headersObj[name].value)
})

self._response = null
this._response = null
xhr.onreadystatechange = function () {
switch (xhr.readyState) {
case rStates.LOADING:
case rStates.DONE:
self._onXHRProgress()
this._onXHRProgress()
break
}
}
// Necessary for streaming in Firefox, since xhr.response is ONLY defined
// in onprogress, not in onreadystatechange with xhr.readyState = 3
if (self._mode === 'moz-chunked-arraybuffer') {
xhr.onprogress = function () {
self._onXHRProgress()
}
if (this._mode === 'moz-chunked-arraybuffer') {
xhr.onprogress = this._onXHRProgress.bind(this);
}

xhr.onerror = function () {
if (self._destroyed)
return
self.emit('error', new Error('XHR error'))
}
xhr.onerror = this._onError.bind(this);

try {
xhr.send(body)
} catch (err) {
process.nextTick(function () {
self.emit('error', err)
})
process.nextTick(this.emit.bind(this, 'error', err));
return
}
}
}

ClientRequest.prototype._onError = function () {
if (!this._notdestroyed)
return
this.emit('error', new Error('XHR error'))

};

/**
* Checks if xhr.status is readable and non-zero, indicating no error.
* Even though the spec says it should be available in readyState 3,
Expand All @@ -197,53 +193,54 @@ function statusValid (xhr) {
}

ClientRequest.prototype._onXHRProgress = function () {
var self = this

if (!statusValid(self._xhr) || self._destroyed)
if (!statusValid(this._xhr) || !this._notdestroyed)
return

if (!self._response)
self._connect()
if (!this._response)
this._connect()

self._response._onXHRProgress()
this._response._onXHRProgress()
}

ClientRequest.prototype._connect = function () {
var self = this

if (self._destroyed)
if (!this._notdestroyed)
return

self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode)
self.emit('response', self._response)
this._response = new IncomingMessage(this._xhr, this._fetchResponse, this._mode)
this.emit('response', this._response)
}

ClientRequest.prototype._write = function (chunk, encoding, cb) {
var self = this

self._body.push(chunk)
this._body.push(chunk)
cb()
}

ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () {
var self = this
self._destroyed = true
if (self._response)
self._response._destroyed = true
if (self._xhr)
self._xhr.abort()
this._notdestroyed = null
if (this._response)
this._response._destroyed = true
if (this._xhr)
this._xhr.abort()

/*
this._body = null;
this._headers = null;
this._xhr = null;
this._response = null;
this._opts = null;
this._mode = null;
*/
// Currently, there isn't a way to truly abort a fetch.
// If you like bikeshedding, see https://github.com/whatwg/fetch/issues/27
}

ClientRequest.prototype.end = function (data, encoding, cb) {
var self = this
if (typeof data === 'function') {
cb = data
data = undefined
}

stream.Writable.prototype.end.call(self, data, encoding, cb)
stream.Writable.prototype.end.call(this, data, encoding, cb)
}

ClientRequest.prototype.flushHeaders = function () {}
Expand Down