[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