[Pkg-javascript-commits] [node-stream-http] 01/208: Initial version

Bastien Roucariès rouca at moszumanska.debian.org
Sun Aug 13 13:39:22 UTC 2017


This is an automated email from the git hooks/post-receive script.

rouca pushed a commit to branch master
in repository node-stream-http.

commit c2789535d45aa14edf517dd01d52948c1aa280a4
Author: John Hiesey <john at hiesey.com>
Date:   Tue Jun 23 21:21:54 2015 -0700

    Initial version
---
 capability.js |  29 ++++++++++
 index.html    |  24 +++++++++
 index.js      |  30 +++++++++++
 package.json  |  15 ++++++
 request.js    | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 response.js   | 131 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 400 insertions(+)

diff --git a/capability.js b/capability.js
new file mode 100644
index 0000000..09bffca
--- /dev/null
+++ b/capability.js
@@ -0,0 +1,29 @@
+var util = require('util')
+
+exports.fetch = util.isFunction(window.fetch)
+
+function checkTypeSupport (type) {
+	var xhr = new XMLHttpRequest()
+	xhr.open('GET', '/')
+	try {
+		xhr.responseType = type
+		return xhr.responseType === type
+	} catch (e) {}
+	return false;
+}
+
+exports.arraybuffer = checkTypeSupport('arraybuffer')
+exports.msstream = checkTypeSupport('ms-stream')
+exports.mozchunkedarraybuffer = checkTypeSupport('moz-chunked-arraybuffer')
+
+/*if (exports.fetch) {
+	exports.mode = 'fetch'
+} else*/ if (exports.mozchunkedarraybuffer) {
+	exports.mode = 'moz-chunked-arraybuffer'
+} else if (exports.msstream) {
+	exports.mode = 'ms-stream'
+// } else if (exports.arraybuffer) {
+// 	exports.mode = 'arraybuffer'
+} else {
+	exports.mode = 'text'
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..341a029
--- /dev/null
+++ b/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="bundle.js"></script>
+</head>
+<body>
+
+<script>
+var req = http.request('/bundle.js')
+req.on('response', function (res) {
+	res.on('data', function (buf) {
+		console.warn('len:', buf.length)
+		console.log('chunk:', buf.toString())
+	})
+	res.on('end', function () {
+		console.log('done!')
+	})
+})
+req.end()
+</script>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..2b808ca
--- /dev/null
+++ b/index.js
@@ -0,0 +1,30 @@
+var ClientRequest = require('./request')
+var url = require('url')
+var util = require('util')
+
+exports.request = function request (opts, cb) {
+	if (typeof opts === 'string')
+		opts = url.parse(opts)
+
+	opts.method = opts.method || 'GET'
+	opts.headers = opts.headers || {}
+	opts.protocol = opts.protocol || window.location.protocol
+	opts.hostname = opts.hostname || window.location.hostname
+	opts.path = opts.path || '/'
+	opts.port = opts.port || parseInt(window.location.port, 10)
+
+	// also valid: port, auth, credentials
+
+	var req = new ClientRequest(opts)
+	if (cb)
+		req.on('response', cb)
+	return req
+}
+
+exports.get = function get (opts, cb) {
+	opts.method = 'GET'
+	var req = request(opts, cb)
+	req.end()
+	return req
+}
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..93b6188
--- /dev/null
+++ b/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "httpstream",
+  "version": "1.0.0",
+  "description": "Streaming http in the browser",
+  "main": "index.js",
+  "scripts": {
+    "build": "browserify index.js -s http > bundle.js",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "John Hiesey",
+  "license": "MIT",
+  "dependencies": {
+    "browserify": "^10.2.4"
+  }
+}
diff --git a/request.js b/request.js
new file mode 100644
index 0000000..30c2788
--- /dev/null
+++ b/request.js
@@ -0,0 +1,171 @@
+// var Base64 = require('Base64')
+var capability = require('./capability')
+var response = require('./response')
+var stream = require('stream')
+var util = require('util')
+
+var IncomingMessage = response.IncomingMessage
+var rStates = response.readyStates
+
+// function copy (to, from) {
+// 	if (!Array.isArray(from))
+// 		from = [from]
+// 	from.forEach(function (obj) {
+// 		Object.keys(function (key), {
+// 			to[key] = from[key]
+// 		})
+// 	})
+// 	return to
+// }
+
+var ClientRequest = module.exports = function (opts) {
+	var self = this
+	stream.Writable.call(self)
+
+	self._opts = opts
+	self._body = []
+	self._fullHeaders = {}
+	Object.keys(opts.headers).forEach(function (name) {
+		self.setHeader(name, opts.headers[name])
+	})
+
+	self._mode = capability.mode
+
+	self.on('finish', self._onFinish.bind(self))
+}
+
+util.inherits(ClientRequest, stream.Writable)
+
+ClientRequest.prototype.setHeader = function (name, value) {
+	var self = this
+	self._fullHeaders[name.toLowerCase()] = value
+}
+
+ClientRequest.prototype.getHeader = function (name) {
+	var self = this
+	return self._fullHeaders[name.toLowerCase()]
+}
+
+ClientRequest.prototype.removeHeader = function (name) {
+	var self = this
+	delete self._fullHeaders[name.toLowerCase()]
+}
+
+ClientRequest.prototype._onFinish = function () {
+	var self = this
+
+	var opts = self._opts
+	var url = opts.protocol + '//' + opts.hostname
+		+ (opts.port ? ':' + opts.port : '') + opts.path
+
+	var user, pass
+	if (opts.auth) {
+		var authMatch = opts.auth.match(/^([^:]*):(.*)$)/)
+		user = authMatch[0]
+		pass = authMatch[1]
+	}
+
+	// process and send data
+	var fullHeaders = self._fullHeaders
+	var body
+	if (typeof Blob === 'function') {
+		body = new Blob(self._body.map(function (buffer) {
+			return buffer.toArrayBuffer()
+		}), {
+			type: fullHeaders['content-type'] || ''
+		});		
+	} else {
+		// get utf8 string
+		body = Buffer.concat(self._body).toString()
+	}
+
+	if (self._mode === 'fetch') {
+		var headers = Object.keys(fullHeaders).map(function (name) {
+			return [name, fullHeaders[name]]
+		})
+
+		fetch(url, {
+			method: self._opts.method,
+			headers: headers,
+			body: body,
+			mode: 'cors',
+			credentials: opts.credentials ? 'include' : 'omit'
+		}).then(function (response) {
+			self._fetchResponse = response
+			self._connect()
+		})
+	} else {
+		var xhr = self._xhr = new XMLHttpRequest() // TODO: old IE
+		xhr.open(self._opts.method, url, true, user, pass)
+
+		// Can't set responseType on really old browsers
+		if ('responseType' in xhr)
+			xhr.responseType = self._mode
+
+		if ('withCredentials' in xhr)
+			xhr.withCredentials = !!opts.credentials
+
+		if (self._mode === 'text' && 'overrideMimeType' in xhr)
+			xhr.overrideMimeType('text/plain; charset=x-user-defined')
+
+		Object.keys(fullHeaders, function (name) {
+			xhr.setRequestHeader(name, headers[name])
+		})
+
+		xhr.onreadystatechange = function () {
+			switch (xhr.readyState) {
+				case rStates.HEADERS_RECEIVED:
+					self._connect()
+					break
+				case rStates.LOADING:
+				case rStates.DONE:
+					self._response._onXHRReadyStateChange()
+					break
+			}
+		}
+
+		xhr.send(body)
+		// This is the best approximation to where 'socket' should be fired
+		process.nextTick(function () {
+			self.emit('socket')
+		})
+	}
+}
+
+ClientRequest.prototype._connect = function () {
+	var self = this
+
+	self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode)
+	self.emit('response', self._response)
+}
+
+ClientRequest.prototype._write = function (chunk, encoding, cb) {
+	var self = this
+
+	self._body.push(chunk)
+	cb()
+}
+
+ClientRequest.prototype.abort = function () {
+	var self = this
+	if (self._xhr)
+		self._xhr.abort()
+}
+
+ClientRequest.prototype.end = function (data, encoding, cb) {
+	var self = this
+	if (typeof data === 'function') {
+		cb = data
+		data = undefined
+	}
+
+	if (data)
+		stream.Writable.push.call(self, data, encoding)
+
+	stream.Writable.prototype.end.call(self, cb)
+}
+
+ClientRequest.prototype.flushHeaders = function () {}
+ClientRequest.prototype.setTimeout = function () {}
+ClientRequest.prototype.setNoDelay = function () {}
+ClientRequest.prototype.setSocketKeepAlive = function () {}
diff --git a/response.js b/response.js
new file mode 100644
index 0000000..d6e7639
--- /dev/null
+++ b/response.js
@@ -0,0 +1,131 @@
+var stream = require('stream')
+var util = require('util')
+
+var rStates = exports.readyStates = {
+	UNSENT: 0,
+	OPENED: 1,
+	HEADERS_RECEIVED: 2,
+	LOADING: 3,
+	DONE: 4
+}
+
+var IncomingMessage = exports.IncomingMessage = function (xhr, fetchResponse, mode) {
+	var self = this
+	stream.Readable.call(self)
+
+	self._mode = mode
+	self.headers = {}
+	self.rawHeaders = []
+	self.trailers = {}
+	self.rawTrailers = []
+
+	if (mode === 'fetch') {
+		self._fetchResponse = fetchResponse
+
+		self.statusCode = response.status
+		self.statusMessage = response.statusText
+		// backwards compatible version of for (<item> of <iterable>):
+		// for (var <item>,_i,_it = <iterable>[Symbol.iterator](); <item> = (_i = _it.next()).value,!_i.done;)
+		for (var header,_i,_it = response.headers[Symbol.iterator](); header = (_i = _it.next()).value,!_i.done;) {
+			self.headers[header[0].toLowerCase()] = header[1]
+			self.rawHeeaders.push(header)
+		}
+
+		// TODO: this doesn't respect backpressure. Once WritableStream is available, this can be fixed
+		var reader = response.body.getReader()
+		reader.read().then(function (result) {
+			if (result.done) {
+				self.push(null)
+				self.emit('close')
+				return
+			}
+			self.push(new Buffer(result.value))
+		})
+
+	} else {
+		self._xhr = xhr
+		self._pos = 0
+
+		self.statusCode = xhr.status
+		self.statusMessage = xhr.statusText.match('/^[0-9]{3} (.*)$')[1]
+		var headers = xhr.getAllResponseHeaders().split(/\r?\n/)
+		headers.forEach(function (header) {
+			var matches = header.match(/^([^:]+):\s*(.*)/)
+			if (matches) {
+				var key = matches[1].toLowerCase()
+				self.headers[key.toLowerCase()] = matches[2] // TODO: some headers should use commas, some arrays
+				self.rawHeaders.push(matches[1], matches[2])
+			}
+		})
+
+		self._charset = 'x-user-defined'
+		if (typeof xhr.overrideMimeType !== 'function') {
+			var mimeType = self.rawHeaders['mime-type']
+			if (mimeType) {
+				var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/)
+				if (charsetMatch) {
+					self._charset = charsetMatch[1].toLowerCase()
+				}
+			}
+			if (!self._charset)
+				self._charset = 'utf-8' // best guess
+		}
+	}
+}
+
+util.inherits(IncomingMessage, stream.Readable)
+
+IncomingMessage.prototype._read = function () {}
+
+IncomingMessage.prototype._onXHRReadyStateChange = function () {
+	var self = this
+
+	var xhr = self._xhr
+
+	switch (self._mode) {
+		case 'text': // slice
+			if (xhr.response.length > self._pos) {
+				var newData = xhr.response.substr(self._pos)
+				if (self._charset === 'x-user-defined') {
+					var buffer = new Buffer(newData.length)
+					for (var i = 0; i < newData.length; i++)
+						buffer[i] = newData.charCodeAt(i)
+
+					self.push(buffer)
+				} else {
+					self.push(newData, self._charset)
+				}
+				self._pos = xhr.response.length
+			}
+			break
+		case 'moz-chunked-arraybuffer': // take whole
+			if (xhr.readyState !== rStates.LOADING)
+				break
+			self.push(new Buffer(xhr.response))
+			break
+		case 'ms-stream':
+			// 
+			if (xhr.readyState !== rStates.LOADING)
+				break
+			var reader = new MSStreamReader()
+			reader.onprogress = function () {
+				if (reader.result.byteLength > self._pos) {
+					self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos))))
+					self._pos = reader.result.byteLength
+				}
+			}
+			reader.onload = function () {
+				self.push(null)
+				self.emit('close')
+			}
+			// reader.onerror = ??? // TODO: this
+			reader.readAsArrayBuffer(xhr.response)
+			break
+	}
+
+	// The ms-stream case handles end separately in reader.onload()
+	if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') {
+		self.push(null)
+		self.emit('close')
+	}
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-stream-http.git



More information about the Pkg-javascript-commits mailing list