From 80b5d427b3bc2027cb73bb52df8e070947d1163e Mon Sep 17 00:00:00 2001 From: Sami Spets <savasp@utu.fi> Date: Mon, 9 Dec 2019 08:25:17 +0200 Subject: [PATCH] Refactoring the folder structure --- .../{server/src => }/public/css/index.css | 0 .../{server/src => }/public/index.html | 0 .../{server/src => }/public/js/bundle.js | 4052 ++++++++--------- .../{server/src => }/public/js/index.js | 2 +- .../src => }/public/js/lib/VideoPlayer.js | 0 .../{server/src => }/public/js/lib/configs.js | 0 .../src => }/public/js/lib/libde265min.js | 0 web-service/server/src/index.js | 6 +- web-service/server/src/peer.js | 10 +- 9 files changed, 2034 insertions(+), 2036 deletions(-) rename web-service/{server/src => }/public/css/index.css (100%) rename web-service/{server/src => }/public/index.html (100%) rename web-service/{server/src => }/public/js/bundle.js (98%) rename web-service/{server/src => }/public/js/index.js (96%) rename web-service/{server/src => }/public/js/lib/VideoPlayer.js (100%) rename web-service/{server/src => }/public/js/lib/configs.js (100%) rename web-service/{server/src => }/public/js/lib/libde265min.js (100%) diff --git a/web-service/server/src/public/css/index.css b/web-service/public/css/index.css similarity index 100% rename from web-service/server/src/public/css/index.css rename to web-service/public/css/index.css diff --git a/web-service/server/src/public/index.html b/web-service/public/index.html similarity index 100% rename from web-service/server/src/public/index.html rename to web-service/public/index.html diff --git a/web-service/server/src/public/js/bundle.js b/web-service/public/js/bundle.js similarity index 98% rename from web-service/server/src/public/js/bundle.js rename to web-service/public/js/bundle.js index 901db4035..d260a3145 100644 --- a/web-service/server/src/public/js/bundle.js +++ b/web-service/public/js/bundle.js @@ -1,4 +1,425 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ +const Peer = require('../../server/src/peer') +const VideoPlayer = require('./lib/VideoPlayer') + +let current_data = {}; +let peer; +let decoder; +let player; + +/** + * Validates that the user is logged in + */ +checkIfLoggedIn = async () => { + // const token = window.localStorage.getItem('token') + // console.log(token) + // if(!token){ + // console.log("You need to login") + // renderLogin() + // }else{ + + // //Check if the token is valid + // const response = await fetch('http://localhost:8080/auth/validation', { + // method: 'POST', + // headers: {'Authorization': token} + // }) + // console.log('RESPONSE', response) + + // //Token is valid, show available streams + // if(response.status === 200){ + // console.log("SUCCESS") + renderThumbnails() + + // } + // } +} + +//Redirects the user to google authentication +handleLogin = () => { + window.location.href="/google"; +} + +/** + * Returns a list of available streams + */ +getAvailableStreams = async () => { + try{ + const streamsInJson = await fetch('http://localhost:8080/streams'); + const streams = await streamsInJson.json(); + console.log('AVAILABLE', streams) + return streams; + }catch(err){ + console.log(err) + } +} + + +createVideoPlayer = () => { + const containerDiv = document.getElementById('container') + containerDiv.innerHTML = `<h1>Stream ${current_data.uri} is live right here!</h1><br><button onclick="renderThumbnails(); closeStream()">Go back</button><br> + <canvas id="ftlab-stream-video" width="640" height="360"></canvas>`; + containerDiv.innerHTML += '<br>' + containerDiv.innerHTML += '' + createPeer(); + const canvas = document.getElementById("ftlab-stream-video") + player = new VideoPlayer(canvas) + console.log("PLAYER", player) + connectToStream(); +} + +/** + * Creates thumbnail (image) for all available streams and adds them to div class='container' + */ +renderThumbnails = async () => { + const thumbnails = await getAvailableStreams(); + // console.log('THUMBNAILS', thumbnails) + const containerDiv = document.getElementById('container') + containerDiv.innerHTML = ''; + containerDiv.innerHTML = `<button onClick="configs()">change configs</button>` + containerDiv.innerHTML += `<div class="ftlab-stream-thumbnails"></div>` + // console.log(containerDiv) + for(var i=0; i<thumbnails.length; i++){ + const encodedURI = encodeURIComponent(thumbnails[i]) + current_data.uri = thumbnails[i] + console.log("THUMBNAIL[i]", thumbnails[i]) + try{ + const someData = await fetch(`http://localhost:8080/stream/rgb?uri=${encodedURI}`) + console.log('SOME DATA', someData) + if(!someData.ok){ + throw new Error('Image not found') + } + const myBlob = await someData.blob(); + console.log('BLOB', myBlob) + const objectURL = URL.createObjectURL(myBlob); + // containerDiv.innerHTML += createCard() + containerDiv.innerHTML += createCard(objectURL, i+4) + }catch(err){ + console.log("Couldn't create thumbnail"); + console.log(err) + } + } +} + + +/** + * Renders button that will redirect to google login + */ +renderLogin = () => { + const containerDiv = document.getElementById('container'); + containerDiv.innerHTML = + `<div id='Login'> + <h2>Welcome to Future Technology Lab</h2> + <h3>Please login!</h3> + <a className="button" onClick="handleLogin()"> + <div> + <span class="svgIcon t-popup-svg"> + <svg class="svgIcon-use" width="25" height="37" viewBox="0 0 25 25"> + <g fill="none" fill-rule="evenodd"> + <path d="M20.66 12.693c0-.603-.054-1.182-.155-1.738H12.5v3.287h4.575a3.91 3.91 0 0 1-1.697 2.566v2.133h2.747c1.608-1.48 2.535-3.65 2.535-6.24z" fill="#4285F4"/> + <path d="M12.5 21c2.295 0 4.22-.76 5.625-2.06l-2.747-2.132c-.76.51-1.734.81-2.878.81-2.214 0-4.088-1.494-4.756-3.503h-2.84v2.202A8.498 8.498 0 0 0 12.5 21z" fill="#34A853"/> + <path d="M7.744 14.115c-.17-.51-.267-1.055-.267-1.615s.097-1.105.267-1.615V8.683h-2.84A8.488 8.488 0 0 0 4 12.5c0 1.372.328 2.67.904 3.817l2.84-2.202z" fill="#FBBC05"/> + <path d="M12.5 7.38c1.248 0 2.368.43 3.25 1.272l2.437-2.438C16.715 4.842 14.79 4 12.5 4a8.497 8.497 0 0 0-7.596 4.683l2.84 2.202c.668-2.01 2.542-3.504 4.756-3.504z" fill="#EA4335"/> + </g> + </svg> + </span> + <span class="button-label">Sign in with Google</span> + </div> + </a> + </div>` +} + + +/** + * Method to create a single thumbnail + */ +createCard = (url, viewers) => { + return `<div class='ftlab-card-component' > + <img src='${url}' class="thumbnail-img" alt="Hups" width="250px"></img> + <p>Viewers: ${viewers}</p> + <button onclick="createVideoPlayer()">button</button> + </div>` +} + + +createPeer = () => { + const ws = new WebSocket('ws://localhost:8080/'); + ws.binaryType = "arraybuffer"; + peer = new Peer(ws) +} + + +connectToStream = () => { + const uri = current_data.uri + const decodedURI = decodeURIComponent(current_data.uri); + player.playback(peer, decodedURI, uri); +} + +closeStream = () => { + peer.sock.close() +} + + + +/** + * ************** + * CONFIGURATIONS + * ************** + */ + +current_data.configURI = "ftl://utu.fi#reconstruction_snap8/net" + +configs = () => { + const container = document.getElementById("container"); + container.innerHTML = `<div class="ftlab-configurations"></div>`; + renderConfigOptions(); +} + + +renderConfigOptions = () => { + const input = `<p>input1</p><br>ftl://utu.fi#<input type="text">` + const doc = document.getElementsByClassName('ftlab-configurations')[0]; + doc.innerHTML = input; +} + +/** + * + */ +loadConfigs = async (str) => { + const configURI = encodeURIComponent(`ftl://utu.fi#reconstruction_snap8${str}`); + const uri = encodeURIComponent(current_data.uri) + const rawResp = await fetch(`http://localhost:8080/stream/config?settings=${configURI}&uri=${uri}`) + const response = await rawResp.json(); + const content = JSON.parse(response); + container.innerHTML += `<p>${response}</p>`; + console.log(content) +} + +// current_data.configData = '{"peers": 1}'; + +/** + * Method to send configurations to backend + */ +saveConfigs = async () => { + let {uri, configURI, configData} = current_data + const rawResp = await fetch('http://localhost:8080/stream/config', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({peerURI: uri, configURI, data: configData, saveToCPP: true}) + }); + const content = await rawResp.json(); + console.log(content) +} +},{"../../server/src/peer":27,"./lib/VideoPlayer":2}],2:[function(require,module,exports){ + +/** + * VideoPlayer for our stream + * + */ + + + +function VideoPlayer(canvas) { + this.canvas = canvas; + this.ctx = canvas.getContext("2d"); + this.status_cb = null; + this.error_cb = null; + this.ratio = null; + this.filters = false; + this._reset() +} + +VideoPlayer.prototype._reset = function() { + this.start = null; + this.frames = 0; + this.image_data = null; + this.running = false; + this.pending_image_data = null; +} + + +/** @expose */ +VideoPlayer.prototype.set_status_callback = function(callback) { + this.status_cb = callback; +}; + +VideoPlayer.prototype._set_status = function() { + if (this.status_cb) { + this.status_cb.apply(this.status_cb, arguments); + } +}; + +/** @expose */ +VideoPlayer.prototype.set_error_callback = function(callback) { + this.error_cb = callback; +}; + +VideoPlayer.prototype._set_error = function(error, message) { + if (this.error_cb) { + this.error_cb(error, message); + } +}; + +VideoPlayer.prototype._display_image = function(image) { + if (!this.start) { + this.start = new Date(); + this._set_status("playing"); + } else { + this.frames += 1; + var duration = (new Date()) - this.start; + if (duration > 1000) { + this._set_status("fps", this.frames / (duration * 0.001)); + } + } + + var w = image.get_width(); + var h = image.get_height(); + if (w != this.canvas.width || h != this.canvas.height || !this.image_data) { + this.canvas.width = w; + this.canvas.height = h; + this.image_data = this.ctx.createImageData(w, h); + var image_data = this.image_data.data; + for (var i=0; i<w*h; i++) { + image_data[i*4+3] = 255; + } + } + + var that = this; + image.display(this.image_data, function(display_image_data) { + if (window.requestAnimationFrame) { + that.pending_image_data = display_image_data; + window.requestAnimationFrame(function() { + if (that.pending_image_data) { + that.ctx.putImageData(that.pending_image_data, 0, 0); + that.pending_image_data = null; + } + }); + } else { + that.ctx.putImageData(display_image_data, 0, 0); + } + }); +}; + + + + +VideoPlayer.prototype._handle_onload = function(peer, decodedURI, uri) { + var that = this; + this._set_status("initializing"); + + var decoder = new libde265.Decoder(); + decoder.set_image_callback(function(image) { + that._display_image(image); + image.free(); + }); + var ratio = null; + var filters = false; + + + var decode = function(pckg) { + if (!that.running) { return; } + console.log("PACKAGE", pckg) + var err; + if (pckg == null) { + return; + } else { + try { + var tmp = pckg + err = decoder.push_data(tmp); + console.log("ERR VALUE INSIDE TRY", err, tmp) + } catch(e) { + console.log(e); + err = decoder.flush(); + return; + } + } + console.log("ERR VALUE AFTER ELSE", err) + if (!libde265.de265_isOK(err)) { + that._set_error(err, libde265.de265_get_error_text(err)); + return; + } + + if (that.ratio !== ratio) { + decoder.set_framerate_ratio(that.ratio); + ratio = that.ratio; + } + + if (that.filters !== filters) { + decoder.disable_filters(that.filters); + filters = that.filters; + } + + /** + * Here's the bug + * For some reason the decode function evaluates cbErr + * to number 13 which is the case number for waiting for input data + */ + decoder.decode(function(cbErr) { + console.log("paramErr SHOULD BE 0, BUT IT'S", cbErr) + switch(cbErr) { + case libde265.DE265_ERROR_WAITING_FOR_INPUT_DATA: + console.log("DE265_ERROR_WAITING_FOR_INPUT_DATA"); + return; + default: + if (!libde265.de265_isOK(cbErr)) { + that._set_error(err, libde265.de265_get_error_text(paramErr)); + return; + } + } + + if (decoder.has_more()) { + console.log("has more"); + return; + } + + decoder.free(); + that.stop(); + console.log("SHOULD LOG THIS"); + }); + } + + + peer.bind(decodedURI, (latency, streampckg, pckg) => { + console.log(pckg[0]) + if(pckg[0] === 0){ + decode(pckg[5]); + }; + }) + // Start the transaction + peer.send("get_stream", (uri, 10, 0, uri)); +}; + +/** @expose */ +VideoPlayer.prototype.playback = function(peer, decodedURI, uri) { + this._reset(); + + console.log(peer); + console.log(uri) + this._handle_onload(peer, decodedURI, uri) + this._set_status("loading"); + this.running = true; +}; + +/** @expose */ +VideoPlayer.prototype.stop = function() { + this._set_status("stopped"); + this._reset(); +}; + +/** @expose */ +VideoPlayer.prototype.set_framerate_ratio = function(ratio) { + this.ratio = ratio; +}; + +/** @expose */ +VideoPlayer.prototype.disable_filters = function(disable) { + this.filters = disable; +}; + +module.exports = VideoPlayer; +},{}],3:[function(require,module,exports){ 'use strict' var DuplexStream = require('readable-stream').Duplex , util = require('util') @@ -383,7 +804,7 @@ BufferList.prototype._match = function(offset, search) { module.exports = BufferList -},{"readable-stream":18,"safe-buffer":19,"util":44}],2:[function(require,module,exports){ +},{"readable-stream":21,"safe-buffer":22,"util":44}],4:[function(require,module,exports){ (function (Buffer){ // Copyright Joyent, Inc. and other Node contributors. // @@ -493,8 +914,8 @@ function objectToString(o) { return Object.prototype.toString.call(o); } -}).call(this,{"isBuffer":require("../../../../../../../../../../../usr/lib/node_modules/watchify/node_modules/is-buffer/index.js")}) -},{"../../../../../../../../../../../usr/lib/node_modules/watchify/node_modules/is-buffer/index.js":38}],3:[function(require,module,exports){ +}).call(this,{"isBuffer":require("../../../../../../../../../usr/local/lib/node_modules/browserify/node_modules/is-buffer/index.js")}) +},{"../../../../../../../../../usr/local/lib/node_modules/browserify/node_modules/is-buffer/index.js":38}],5:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -519,14 +940,14 @@ if (typeof Object.create === 'function') { } } -},{}],4:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],5:[function(require,module,exports){ +},{}],7:[function(require,module,exports){ 'use strict' var Buffer = require('safe-buffer').Buffer @@ -613,7 +1034,7 @@ function msgpack (options) { module.exports = msgpack -},{"./lib/decoder":6,"./lib/encoder":7,"./lib/streams":8,"assert":29,"bl":1,"safe-buffer":19}],6:[function(require,module,exports){ +},{"./lib/decoder":8,"./lib/encoder":9,"./lib/streams":10,"assert":29,"bl":3,"safe-buffer":22}],8:[function(require,module,exports){ 'use strict' var bl = require('bl') @@ -1051,7 +1472,7 @@ module.exports = function buildDecode (decodingTypes) { module.exports.IncompleteBufferError = IncompleteBufferError -},{"bl":1,"util":44}],7:[function(require,module,exports){ +},{"bl":3,"util":44}],9:[function(require,module,exports){ 'use strict' var Buffer = require('safe-buffer').Buffer @@ -1396,7 +1817,7 @@ function encodeFloat (obj, forceFloat64) { return buf } -},{"bl":1,"safe-buffer":19}],8:[function(require,module,exports){ +},{"bl":3,"safe-buffer":22}],10:[function(require,module,exports){ 'use strict' var Transform = require('readable-stream').Transform @@ -1488,7 +1909,7 @@ Decoder.prototype._transform = function (buf, enc, done) { module.exports.decoder = Decoder module.exports.encoder = Encoder -},{"bl":1,"inherits":3,"readable-stream":18}],9:[function(require,module,exports){ +},{"bl":3,"inherits":5,"readable-stream":21}],11:[function(require,module,exports){ (function (process){ 'use strict'; @@ -1537,7 +1958,7 @@ function nextTick(fn, arg1, arg2, arg3) { }).call(this,require('_process')) -},{"_process":40}],10:[function(require,module,exports){ +},{"_process":40}],12:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -1669,7 +2090,7 @@ Duplex.prototype._destroy = function (err, cb) { pna.nextTick(cb, err); }; -},{"./_stream_readable":12,"./_stream_writable":14,"core-util-is":2,"inherits":3,"process-nextick-args":9}],11:[function(require,module,exports){ +},{"./_stream_readable":14,"./_stream_writable":16,"core-util-is":4,"inherits":5,"process-nextick-args":11}],13:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -1717,7 +2138,7 @@ function PassThrough(options) { PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; -},{"./_stream_transform":13,"core-util-is":2,"inherits":3}],12:[function(require,module,exports){ +},{"./_stream_transform":15,"core-util-is":4,"inherits":5}],14:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -2708,254 +3129,38 @@ function copyFromBuffer(n, list) { } list.length -= c; return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - - // If we get here before consuming all the bytes, then that is a - // bug in node. Should never happen. - if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); - - if (!state.endEmitted) { - state.ended = true; - pna.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - // Check that we didn't get one last unshift. - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - } -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; -} -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./_stream_duplex":10,"./internal/streams/BufferList":15,"./internal/streams/destroy":16,"./internal/streams/stream":17,"_process":40,"core-util-is":2,"events":36,"inherits":3,"isarray":4,"process-nextick-args":9,"safe-buffer":19,"string_decoder/":20,"util":34}],13:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. - -'use strict'; - -module.exports = Transform; - -var Duplex = require('./_stream_duplex'); - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -util.inherits(Transform, Duplex); - -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - - var cb = ts.writecb; - - if (!cb) { - return this.emit('error', new Error('write callback called multiple times')); - } - - ts.writechunk = null; - ts.writecb = null; - - if (data != null) // single equals check for both `null` and `undefined` - this.push(data); - - cb(er); - - var rs = this._readableState; - rs.reading = false; - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } -} - -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - - Duplex.call(this, options); - - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; - - // start out asking for a readable event once data is transformed. - this._readableState.needReadable = true; - - // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - this._readableState.sync = false; - - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - - if (typeof options.flush === 'function') this._flush = options.flush; - } - - // When the writable side finishes, then flush out anything remaining. - this.on('prefinish', prefinish); -} - -function prefinish() { - var _this = this; - - if (typeof this._flush === 'function') { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } -} - -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; - -// This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. -Transform.prototype._transform = function (chunk, encoding, cb) { - throw new Error('_transform() is not implemented'); -}; - -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; - -// Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. -Transform.prototype._read = function (n) { - var ts = this._transformState; - - if (ts.writechunk !== null && ts.writecb && !ts.transforming) { - ts.transforming = true; - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; - -Transform.prototype._destroy = function (err, cb) { - var _this2 = this; - - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - _this2.emit('close'); - }); -}; +} -function done(stream, er, data) { - if (er) return stream.emit('error', er); +function endReadable(stream) { + var state = stream._readableState; - if (data != null) // single equals check for both `null` and `undefined` - stream.push(data); + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); + if (!state.endEmitted) { + state.ended = true; + pna.nextTick(endReadableNT, state, stream); + } +} - if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } +} - return stream.push(null); +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; } -},{"./_stream_duplex":10,"core-util-is":2,"inherits":3}],14:[function(require,module,exports){ -(function (process,global,setImmediate){ +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./_stream_duplex":12,"./internal/streams/BufferList":17,"./internal/streams/destroy":18,"./internal/streams/stream":19,"_process":40,"core-util-is":4,"events":36,"inherits":5,"isarray":6,"process-nextick-args":11,"safe-buffer":22,"string_decoder/":20,"util":34}],15:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -2977,2080 +3182,1875 @@ function done(stream, er, data) { // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. 'use strict'; -/*<replacement>*/ - -var pna = require('process-nextick-args'); -/*</replacement>*/ - -module.exports = Writable; - -/* <replacement> */ -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} - -// It seems a linked list but it is not -// there will be only 2 of these for each stream -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* </replacement> */ - -/*<replacement>*/ -var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; -/*</replacement>*/ - -/*<replacement>*/ -var Duplex; -/*</replacement>*/ +module.exports = Transform; -Writable.WritableState = WritableState; +var Duplex = require('./_stream_duplex'); /*<replacement>*/ var util = require('core-util-is'); util.inherits = require('inherits'); /*</replacement>*/ -/*<replacement>*/ -var internalUtil = { - deprecate: require('util-deprecate') -}; -/*</replacement>*/ - -/*<replacement>*/ -var Stream = require('./internal/streams/stream'); -/*</replacement>*/ - -/*<replacement>*/ - -var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = global.Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/*</replacement>*/ - -var destroyImpl = require('./internal/streams/destroy'); - -util.inherits(Writable, Stream); - -function nop() {} - -function WritableState(options, stream) { - Duplex = Duplex || require('./_stream_duplex'); - - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Duplex; - - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; - - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - - // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var writableHwm = options.writableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); - - // if _final has been called - this.finalCalled = false; - - // drain event flag. - this.needDrain = false; - // at the start of calling end() - this.ending = false; - // when end() has been called, and returned - this.ended = false; - // when 'finish' is emitted - this.finished = false; - - // has it been destroyed - this.destroyed = false; - - // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; +util.inherits(Transform, Duplex); - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; - // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - this.length = 0; + var cb = ts.writecb; - // a flag to see when we're in the middle of a write. - this.writing = false; + if (!cb) { + return this.emit('error', new Error('write callback called multiple times')); + } - // when true all writes will be buffered until .uncork() call - this.corked = 0; + ts.writechunk = null; + ts.writecb = null; - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - this.sync = true; + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); - // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - this.bufferProcessing = false; + cb(er); - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; + var rs = this._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); - // the amount that is being written when _write is called. - this.writelen = 0; + Duplex.call(this, options); - this.bufferedRequest = null; - this.lastBufferedRequest = null; + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; - // count buffered requests - this.bufferedRequestCount = 0; + if (typeof options.flush === 'function') this._flush = options.flush; + } - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); + // When the writable side finishes, then flush out anything remaining. + this.on('prefinish', prefinish); } -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; +function prefinish() { + var _this = this; -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function () { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + if (typeof this._flush === 'function') { + this._flush(function (er, data) { + done(_this, er, data); }); - } catch (_) {} -})(); - -// Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. -var realHasInstance; -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function (object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function (object) { - return object instanceof this; - }; + } else { + done(this, null, null); + } } -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function (chunk, encoding, cb) { + throw new Error('_transform() is not implemented'); +}; - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { - return new Writable(options); +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); } +}; - this._writableState = new WritableState(options, this); +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function (n) { + var ts = this._transformState; - // legacy. - this.writable = true; + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; - if (options) { - if (typeof options.write === 'function') this._write = options.write; +Transform.prototype._destroy = function (err, cb) { + var _this2 = this; - if (typeof options.writev === 'function') this._writev = options.writev; + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this2.emit('close'); + }); +}; - if (typeof options.destroy === 'function') this._destroy = options.destroy; +function done(stream, er, data) { + if (er) return stream.emit('error', er); - if (typeof options.final === 'function') this._final = options.final; - } + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); - Stream.call(this); -} + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - this.emit('error', new Error('Cannot pipe, not readable')); -}; + if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); -function writeAfterEnd(stream, cb) { - var er = new Error('write after end'); - // TODO: defer error events consistently everywhere, not just the cb - stream.emit('error', er); - pna.nextTick(cb, er); + return stream.push(null); } +},{"./_stream_duplex":12,"core-util-is":4,"inherits":5}],16:[function(require,module,exports){ +(function (process,global,setImmediate){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var valid = true; - var er = false; - - if (chunk === null) { - er = new TypeError('May not write null values to stream'); - } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - if (er) { - stream.emit('error', er); - pna.nextTick(cb, er); - valid = false; - } - return valid; -} +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = !state.objectMode && _isUint8Array(chunk); +'use strict'; - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } +/*<replacement>*/ - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } +var pna = require('process-nextick-args'); +/*</replacement>*/ - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; +module.exports = Writable; - if (typeof cb !== 'function') cb = nop; +/* <replacement> */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} - if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; - return ret; -}; + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* </replacement> */ -Writable.prototype.cork = function () { - var state = this._writableState; +/*<replacement>*/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; +/*</replacement>*/ - state.corked++; -}; +/*<replacement>*/ +var Duplex; +/*</replacement>*/ -Writable.prototype.uncork = function () { - var state = this._writableState; +Writable.WritableState = WritableState; - if (state.corked) { - state.corked--; +/*<replacement>*/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/*</replacement>*/ - if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } +/*<replacement>*/ +var internalUtil = { + deprecate: require('util-deprecate') }; +/*</replacement>*/ -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; +/*<replacement>*/ +var Stream = require('./internal/streams/stream'); +/*</replacement>*/ -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; +/*<replacement>*/ + +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; } -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._writableState.highWaterMark; - } -}); +/*</replacement>*/ -// if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - var len = state.objectMode ? 1 : chunk.length; +var destroyImpl = require('./internal/streams/destroy'); - state.length += len; +util.inherits(Writable, Stream); - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; +function nop() {} - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } +function WritableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); - return ret; -} + options = options || {}; -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - pna.nextTick(cb, er); - // this can emit finish, and it will always happen - // after error - pna.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - // this can emit finish, but finish must - // always follow error - finishMaybe(stream, state); - } -} + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var writableHwm = options.writableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; - onwriteStateUpdate(state); + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state); + // if _final has been called + this.finalCalled = false; - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; - if (sync) { - /*<replacement>*/ - asyncWrite(afterWrite, stream, state, finished, cb); - /*</replacement>*/ - } else { - afterWrite(stream, state, finished, cb); - } - } -} + // has it been destroyed + this.destroyed = false; -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; -// Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; + // a flag to see when we're in the middle of a write. + this.writing = false; - var count = 0; - var allBuffers = true; - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - buffer.allBuffers = allBuffers; + // when true all writes will be buffered until .uncork() call + this.corked = 0; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; - // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - state.pendingcb++; - state.lastBufferedRequest = null; - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; - // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - if (state.writing) { - break; - } - } + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; - if (entry === null) state.lastBufferedRequest = null; - } + // the amount that is being written when _write is called. + this.writelen = 0; - state.bufferedRequest = entry; - state.bufferProcessing = false; -} + this.bufferedRequest = null; + this.lastBufferedRequest = null; -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new Error('_write() is not implemented')); -}; + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; -Writable.prototype._writev = null; + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } + // count buffered requests + this.bufferedRequestCount = 0; - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; } - - // ignore unnecessary end() calls. - if (!state.ending && !state.finished) endWritable(this, state, cb); + return out; }; -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - if (err) { - stream.emit('error', err); +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); + +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; + + return object && object._writableState instanceof WritableState; } - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); }); -} -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function') { - state.pendingcb++; - state.finalCalled = true; - pna.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } +} else { + realHasInstance = function (object) { + return object instanceof this; + }; } -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - prefinish(stream, state); - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - } - } - return need; -} +function Writable(options) { + Duplex = Duplex || require('./_stream_duplex'); -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } - if (state.corkedRequestsFree) { - state.corkedRequestsFree.next = corkReq; - } else { - state.corkedRequestsFree = corkReq; + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); } -} -Object.defineProperty(Writable.prototype, 'destroyed', { - get: function () { - if (this._writableState === undefined) { - return false; - } - return this._writableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } + this._writableState = new WritableState(options, this); - // backward compatibility, the user is explicitly - // managing destroyed - this._writableState.destroyed = value; - } -}); + // legacy. + this.writable = true; -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; -Writable.prototype._destroy = function (err, cb) { - this.end(); - cb(err); -}; -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) -},{"./_stream_duplex":10,"./internal/streams/destroy":16,"./internal/streams/stream":17,"_process":40,"core-util-is":2,"inherits":3,"process-nextick-args":9,"safe-buffer":19,"timers":41,"util-deprecate":21}],15:[function(require,module,exports){ -'use strict'; + if (options) { + if (typeof options.write === 'function') this._write = options.write; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + if (typeof options.writev === 'function') this._writev = options.writev; -var Buffer = require('safe-buffer').Buffer; -var util = require('util'); + if (typeof options.destroy === 'function') this._destroy = options.destroy; -function copyBuffer(src, target, offset) { - src.copy(target, offset); + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); } -module.exports = function () { - function BufferList() { - _classCallCheck(this, BufferList); +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; - this.head = null; - this.tail = null; - this.length = 0; - } +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + pna.nextTick(cb, er); +} - BufferList.prototype.push = function push(v) { - var entry = { data: v, next: null }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - }; +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; - BufferList.prototype.unshift = function unshift(v) { - var entry = { data: v, next: this.head }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - }; + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + if (er) { + stream.emit('error', er); + pna.nextTick(cb, er); + valid = false; + } + return valid; +} - BufferList.prototype.shift = function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - }; +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = !state.objectMode && _isUint8Array(chunk); - BufferList.prototype.clear = function clear() { - this.head = this.tail = null; - this.length = 0; - }; + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } - BufferList.prototype.join = function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; - while (p = p.next) { - ret += s + p.data; - }return ret; - }; + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } - BufferList.prototype.concat = function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - if (this.length === 1) return this.head.data; - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - return ret; - }; + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - return BufferList; -}(); + if (typeof cb !== 'function') cb = nop; -if (util && util.inspect && util.inspect.custom) { - module.exports.prototype[util.inspect.custom] = function () { - var obj = util.inspect({ length: this.length }); - return this.constructor.name + ' ' + obj; - }; -} -},{"safe-buffer":19,"util":34}],16:[function(require,module,exports){ -'use strict'; + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } -/*<replacement>*/ + return ret; +}; -var pna = require('process-nextick-args'); -/*</replacement>*/ +Writable.prototype.cork = function () { + var state = this._writableState; -// undocumented cb() API, needed for core, not for public API -function destroy(err, cb) { - var _this = this; + state.corked++; +}; - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; +Writable.prototype.uncork = function () { + var state = this._writableState; - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { - pna.nextTick(emitErrorNT, this, err); - } - return this; + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); } +}; - // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; - if (this._readableState) { - this._readableState.destroyed = true; +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); } + return chunk; +} - // if this is a duplex stream mark the writable part as destroyed as well - if (this._writableState) { - this._writableState.destroyed = true; +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; } +}); - this._destroy(err || null, function (err) { - if (!cb && err) { - pna.nextTick(emitErrorNT, _this, err); - if (_this._writableState) { - _this._writableState.errorEmitted = true; - } - } else if (cb) { - cb(err); +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; } - }); + } + var len = state.objectMode ? 1 : chunk.length; - return this; -} + state.length += len; -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); } -} -function emitErrorNT(self, err) { - self.emit('error', err); + return ret; } -module.exports = { - destroy: destroy, - undestroy: undestroy -}; -},{"process-nextick-args":9}],17:[function(require,module,exports){ -module.exports = require('events').EventEmitter; - -},{"events":36}],18:[function(require,module,exports){ -exports = module.exports = require('./lib/_stream_readable.js'); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = require('./lib/_stream_writable.js'); -exports.Duplex = require('./lib/_stream_duplex.js'); -exports.Transform = require('./lib/_stream_transform.js'); -exports.PassThrough = require('./lib/_stream_passthrough.js'); +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} -},{"./lib/_stream_duplex.js":10,"./lib/_stream_passthrough.js":11,"./lib/_stream_readable.js":12,"./lib/_stream_transform.js":13,"./lib/_stream_writable.js":14}],19:[function(require,module,exports){ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + pna.nextTick(cb, er); + // this can emit finish, and it will always happen + // after error + pna.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); } } -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; } -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} + onwriteStateUpdate(state); -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + /*<replacement>*/ + asyncWrite(afterWrite, stream, state, finished, cb); + /*</replacement>*/ } else { - buf.fill(fill) + afterWrite(stream, state, finished, cb); } - } else { - buf.fill(0) } - return buf } -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); } -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); } - return buffer.SlowBuffer(size) } -},{"buffer":35}],20:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; -'use strict'; + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; -/*<replacement>*/ + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + buffer.allBuffers = allBuffers; -var Buffer = require('safe-buffer').Buffer; -/*</replacement>*/ + doWrite(stream, state, true, state.length, buffer, '', holder.finish); -var isEncoding = Buffer.isEncoding || function (encoding) { - encoding = '' + encoding; - switch (encoding && encoding.toLowerCase()) { - case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': - return true; - default: - return false; - } -}; + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; -function _normalizeEncoding(enc) { - if (!enc) return 'utf8'; - var retried; - while (true) { - switch (enc) { - case 'utf8': - case 'utf-8': - return 'utf8'; - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return 'utf16le'; - case 'latin1': - case 'binary': - return 'latin1'; - case 'base64': - case 'ascii': - case 'hex': - return enc; - default: - if (retried) return; // undefined - enc = ('' + enc).toLowerCase(); - retried = true; + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } } + + if (entry === null) state.lastBufferedRequest = null; } -}; -// Do not cache `Buffer.isEncoding` when checking encoding names as some -// modules monkey-patch it to support additional encodings -function normalizeEncoding(enc) { - var nenc = _normalizeEncoding(enc); - if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); - return nenc || enc; + state.bufferedRequest = entry; + state.bufferProcessing = false; } -// StringDecoder provides an interface for efficiently splitting a series of -// buffers into a series of JS strings without breaking apart multi-byte -// characters. -exports.StringDecoder = StringDecoder; -function StringDecoder(encoding) { - this.encoding = normalizeEncoding(encoding); - var nb; - switch (this.encoding) { - case 'utf16le': - this.text = utf16Text; - this.end = utf16End; - nb = 4; - break; - case 'utf8': - this.fillLast = utf8FillLast; - nb = 4; - break; - case 'base64': - this.text = base64Text; - this.end = base64End; - nb = 3; - break; - default: - this.write = simpleWrite; - this.end = simpleEnd; - return; +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; } - this.lastNeed = 0; - this.lastTotal = 0; - this.lastChar = Buffer.allocUnsafe(nb); -} -StringDecoder.prototype.write = function (buf) { - if (buf.length === 0) return ''; - var r; - var i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ''; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ''; -}; -StringDecoder.prototype.end = utf8End; + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + pna.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} -// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer -StringDecoder.prototype.fillLast = function (buf) { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -}; - -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. If an invalid byte is detected, -2 is returned. -function utf8CheckByte(byte) { - if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; - return byte >> 6 === 0x02 ? -1 : -2; + return need; } -// Checks at most 3 bytes at the end of a Buffer in order to detect an -// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) -// needed to complete the UTF-8 character (if applicable) are returned. -function utf8CheckIncomplete(self, buf, i) { - var j = buf.length - 1; - if (j < i) return 0; - var nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0;else self.lastNeed = nb - 3; - } - return nb; + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; } - return 0; } -// Validates as many continuation bytes for a multi-byte UTF-8 character as -// needed or are available. If we see a non-continuation byte where we expect -// one, we "replace" the validated continuation bytes we've seen so far with -// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding -// behavior. The continuation byte check is included three times in the case -// where all of the continuation bytes for a character exist in the same buffer. -// It is also done this way as a slight performance increase instead of using a -// loop. -function utf8CheckExtraBytes(self, buf, p) { - if ((buf[0] & 0xC0) !== 0x80) { - self.lastNeed = 0; - return '\ufffd'; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'; +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'; - } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; } +}); + +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); +}; +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) +},{"./_stream_duplex":12,"./internal/streams/destroy":18,"./internal/streams/stream":19,"_process":40,"core-util-is":4,"inherits":5,"process-nextick-args":11,"safe-buffer":22,"timers":41,"util-deprecate":23}],17:[function(require,module,exports){ +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Buffer = require('safe-buffer').Buffer; +var util = require('util'); + +function copyBuffer(src, target, offset) { + src.copy(target, offset); } -// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. -function utf8FillLast(buf) { - var p = this.lastTotal - this.lastNeed; - var r = utf8CheckExtraBytes(this, buf, p); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} -// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a -// partial character, the character's bytes are buffered until the required -// number of bytes are available. -function utf8Text(buf, i) { - var total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString('utf8', i); - this.lastTotal = total; - var end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString('utf8', i, end); -} + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; -// For UTF-8, a replacement character is added when ending on a partial -// character. -function utf8End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + '\ufffd'; - return r; -} + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; -// UTF-16LE typically needs two bytes per character, but even if we have an even -// number of bytes available, we need to check if we end on a leading/high -// surrogate. In that case, we need to wait for the next two bytes in order to -// decode the last character properly. -function utf16Text(buf, i) { - if ((buf.length - i) % 2 === 0) { - var r = buf.toString('utf16le', i); - if (r) { - var c = r.charCodeAt(r.length - 1); - if (c >= 0xD800 && c <= 0xDBFF) { - this.lastNeed = 2; - this.lastTotal = 4; - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - return r.slice(0, -1); - } + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; + + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; + + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; + + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; } - return r; - } - this.lastNeed = 1; - this.lastTotal = 2; - this.lastChar[0] = buf[buf.length - 1]; - return buf.toString('utf16le', i, buf.length - 1); -} + return ret; + }; -// For UTF-16LE we do not explicitly append special replacement characters if we -// end on a partial character, we simply let v8 handle that. -function utf16End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) { - var end = this.lastTotal - this.lastNeed; - return r + this.lastChar.toString('utf16le', 0, end); - } - return r; -} + return BufferList; +}(); -function base64Text(buf, i) { - var n = (buf.length - i) % 3; - if (n === 0) return buf.toString('base64', i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString('base64', i, buf.length - n); +if (util && util.inspect && util.inspect.custom) { + module.exports.prototype[util.inspect.custom] = function () { + var obj = util.inspect({ length: this.length }); + return this.constructor.name + ' ' + obj; + }; } +},{"safe-buffer":22,"util":34}],18:[function(require,module,exports){ +'use strict'; -function base64End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); - return r; -} +/*<replacement>*/ -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} +var pna = require('process-nextick-args'); +/*</replacement>*/ -function simpleEnd(buf) { - return buf && buf.length ? this.write(buf) : ''; -} -},{"safe-buffer":19}],21:[function(require,module,exports){ -(function (global){ +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; -/** - * Module exports. - */ + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; -module.exports = deprecate; + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + pna.nextTick(emitErrorNT, this, err); + } + return this; + } -/** - * Mark that a method should not be used. - * Returns a modified function which warns once by default. - * - * If `localStorage.noDeprecation = true` is set, then it is a no-op. - * - * If `localStorage.throwDeprecation = true` is set, then deprecated functions - * will throw an Error when invoked. - * - * If `localStorage.traceDeprecation = true` is set, then deprecated functions - * will invoke `console.trace()` instead of `console.error()`. - * - * @param {Function} fn - the function to deprecate - * @param {String} msg - the string to print to the console when `fn` is invoked - * @returns {Function} a new "deprecated" version of `fn` - * @api public - */ + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks -function deprecate (fn, msg) { - if (config('noDeprecation')) { - return fn; + if (this._readableState) { + this._readableState.destroyed = true; } - var warned = false; - function deprecated() { - if (!warned) { - if (config('throwDeprecation')) { - throw new Error(msg); - } else if (config('traceDeprecation')) { - console.trace(msg); - } else { - console.warn(msg); + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + pna.nextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; } - warned = true; + } else if (cb) { + cb(err); } - return fn.apply(this, arguments); - } + }); - return deprecated; + return this; } -/** - * Checks `localStorage` for boolean values for the given `name`. - * - * @param {String} name - * @returns {Boolean} - * @api private - */ +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } -function config (name) { - // accessing global.localStorage can trigger a DOMException in sandboxed iframes - try { - if (!global.localStorage) return false; - } catch (_) { - return false; + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; } - var val = global.localStorage[name]; - if (null == val) return false; - return String(val).toLowerCase() === 'true'; } -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],22:[function(require,module,exports){ -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -var byteToHex = []; -for (var i = 0; i < 256; ++i) { - byteToHex[i] = (i + 0x100).toString(16).substr(1); +function emitErrorNT(self, err) { + self.emit('error', err); } -function bytesToUuid(buf, offset) { - var i = offset || 0; - var bth = byteToHex; - // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 - return ([bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]]]).join(''); -} +module.exports = { + destroy: destroy, + undestroy: undestroy +}; +},{"process-nextick-args":11}],19:[function(require,module,exports){ +module.exports = require('events').EventEmitter; -module.exports = bytesToUuid; +},{"events":36}],20:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. -},{}],23:[function(require,module,exports){ -// Unique ID creation requires a high quality random # generator. In the -// browser this is a little complicated due to unknown quality of Math.random() -// and inconsistent support for the `crypto` API. We do the best we can via -// feature-detection +'use strict'; -// getRandomValues needs to be invoked in a context where "this" is a Crypto -// implementation. Also, find the complete implementation of crypto on IE11. -var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) || - (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto)); +/*<replacement>*/ -if (getRandomValues) { - // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto - var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef +var Buffer = require('safe-buffer').Buffer; +/*</replacement>*/ - module.exports = function whatwgRNG() { - getRandomValues(rnds8); - return rnds8; - }; -} else { - // Math.random()-based (RNG) - // - // If all else fails, use Math.random(). It's fast, but is of unspecified - // quality. - var rnds = new Array(16); +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; - module.exports = function mathRNG() { - for (var i = 0, r; i < 16; i++) { - if ((i & 0x03) === 0) r = Math.random() * 0x100000000; - rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; } + } +}; - return rnds; - }; +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; } -},{}],24:[function(require,module,exports){ -var rng = require('./lib/rng'); -var bytesToUuid = require('./lib/bytesToUuid'); - -function v4(options, buf, offset) { - var i = buf && offset || 0; +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} - if (typeof(options) == 'string') { - buf = options === 'binary' ? new Array(16) : null; - options = null; +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; } - options = options || {}; + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; - var rnds = options.random || (options.rng || rng)(); +StringDecoder.prototype.end = utf8End; - // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - rnds[6] = (rnds[6] & 0x0f) | 0x40; - rnds[8] = (rnds[8] & 0x3f) | 0x80; +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; - // Copy bytes to buffer, if provided - if (buf) { - for (var ii = 0; ii < 16; ++ii) { - buf[i + ii] = rnds[ii]; - } +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; - return buf || bytesToUuid(rnds); +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; } -module.exports = v4; - -},{"./lib/bytesToUuid":22,"./lib/rng":23}],25:[function(require,module,exports){ -(function (Buffer){ -const msgpack = require('msgpack5')() - , encode = msgpack.encode - , decode = msgpack.decode; -const uuidv4 = require('uuid/v4') -const uuidParser = require('./utils/uuidParser') - -const kConnecting = 1; -const kConnected = 2; -const kDisconnected = 3; - -// Generate a unique id for this webservice -let cpp_my_uuid = uuidv4(); -console.log(cpp_my_uuid) -let my_uuid = uuidParser.parse(uuidv4()) -my_uuid = new Uint8Array(cpp_my_uuid); -// my_uuid[0] = 44; -my_uuid = Buffer.from(my_uuid); - -const kMagic = 0x0009340053640912; -const kVersion = 0; - - -/** - * Wrap a web socket with a MsgPack RCP protocol that works with our C++ version. - * @param {websocket} ws Websocket object - */ -function Peer(ws) { - this.sock = ws; - this.status = kConnecting; - this.id = null; - this.string_id = ""; - this.bindings = {}; - this.proxies = {}; - this.events = {}; - this.callbacks = {}; - this.cbid = 0; - - this.uri = "unknown"; - this.name = "unknown"; - this.master = false; - - let message = (raw) => { - // console.log(raw) - //Gets right data for client - if(this.sock.on === undefined){ - raw = raw.data; - } - let msg = decode(raw); - // console.log('MSG', msg) - if (this.status == kConnecting) { - if (msg[1] != "__handshake__") { - console.log("Bad handshake"); - this.close(); - } - } - if (msg[0] == 0) { - console.log("MSG", msg[1]); - // Notification - if (msg.length == 3) { - this._dispatchNotification(msg[1], msg[2]); - // Call - } else { - this._dispatchCall(msg[2], msg[1], msg[3]); - } - } else if (msg[0] == 1) { - this._dispatchResponse(msg[1], msg[3]); - } - } +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} - let close = () => { - this.status = kDisconnected; - this._notify("disconnect", this); - } +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; + } + } + } +} - let error = () => { - console.error("Socket error"); - this.sock.close(); - this.status = kDisconnected; - } +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} - //if undefined, peer is being used by client - if(this.sock.on === undefined){ - this.sock.onmessage = message; - this.sock.onclose = close; - this.sock.onopen = (event) => { - this.send("__handshake__", kMagic, kVersion, [my_uuid]); - } - //else peer is being used by server - }else{ - this.sock.on("message", message); - this.sock.on("close", close); - this.sock.on("error", error); - } +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} - this.bind("__handshake__", (magic, version, id) => { - if (magic == kMagic) { - console.log("Handshake received"); - this.status = kConnected; - this.id = id.buffer; - this.string_id = id.toString('hex'); - this._notify("connect", this); - // if(this.sock.on === undefined){ - // this.send("__handshake__", kMagic, kVersion, [my_uuid]); - // } - } else { - console.log("Magic does not match"); - this.close(); - } - }); - this.send("__handshake__", kMagic, kVersion, [my_uuid]); -} +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} -Peer.uuid = my_uuid; +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} -/** - * @private - */ -Peer.prototype._dispatchNotification = function(name, args) { - if (this.bindings.hasOwnProperty(name)) { - //console.log("Notification for: ", name); - this.bindings[name].apply(this, args); - } else { - console.log("Missing handler for: ", name); - } +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); } -/** - * @private - */ -Peer.prototype._dispatchCall = function(name, id, args) { - if (this.bindings.hasOwnProperty(name)) { - //console.log("Call for:", name, id); +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} - try { - let res = this.bindings[name].apply(this, args); - this.sock.send(encode([1,id,name,res])); - } catch(e) { - console.error("Could to dispatch or return call"); - this.close(); - } - } else if (this.proxies.hasOwnProperty(name)) { - //console.log("Proxy for:", name, id); - args.unshift((res) => { - try { - this.sock.send(encode([1,id,name,res])); - } catch(e) { - this.close(); - } - }); - this.proxies[name].apply(this, args); - } else { - console.log("Missing handler for: ", name); - } +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); } -/** - * @private - */ -Peer.prototype._dispatchResponse = function(id, res) { - if (this.callbacks.hasOwnProperty(id)) { - this.callbacks[id].call(this, res); - delete this.callbacks[id]; - } else { - console.log("Missing callback"); - } +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; } +},{"safe-buffer":22}],21:[function(require,module,exports){ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); -/** - * Register an RPC handler that will be called from a remote machine. Remotely - * passed arguments are provided to the given function as normal arguments, and - * if the function returns a value, it will be returned over the network also. - * - * @param {string} name The name of the function - * @param {function} f A function or lambda to be callable remotely - */ -Peer.prototype.bind = function(name, f) { - if (this.bindings.hasOwnProperty(name)) { - //console.error("Duplicate bind to same procedure"); - this.bindings[name] = f; - } else { - this.bindings[name] = f; - } +},{"./lib/_stream_duplex.js":12,"./lib/_stream_passthrough.js":13,"./lib/_stream_readable.js":14,"./lib/_stream_transform.js":15,"./lib/_stream_writable.js":16}],22:[function(require,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = require('buffer') +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer } -/** - * Allow an RPC call to pass through to another machine with minimal local - * processing. - */ -Peer.prototype.proxy = function(name, f) { - if (this.proxies.hasOwnProperty(name)) { - //console.error("Duplicate proxy to same procedure"); - this.proxies[name] = f; - } else { - this.proxies[name] = f; - } +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) } -/** - * Call a procedure on a remote machine. - * - * @param {string} name Name of the procedure - * @param {function} cb Callback to receive return value as argument - * @param {...} args Any number of arguments to also pass to remote procedure - */ -Peer.prototype.rpc = function(name, cb, ...args) { - let id = this.cbid++; - this.callbacks[id] = cb; +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) - try { - this.sock.send(encode([0, id, name, args])); - } catch(e) { - this.close(); - } +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) } -Peer.prototype.sendB = function(name, args) { - try { - this.sock.send(encode([0, name, args])); - } catch(e) { - this.close(); - } +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf } -/** - * Call a remote procedure but with no return value expected. - * - * @param {string} name Name of the procedure - * @param {...} args Any number of arguments to also pass to remote procedure - */ -Peer.prototype.send = function(name, ...args) { - try { - this.sock.send(encode([0, name, args])); - } catch(e) { - this.close(); - } +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) } +},{"buffer":35}],23:[function(require,module,exports){ +(function (global){ + /** - * Closes the socket + * Module exports. */ -Peer.prototype.close = function() { - if(this.sock.on !== undefined){ - this.sock.close(); - } - this.status = kDisconnected; -} + +module.exports = deprecate; /** - * @private + * Mark that a method should not be used. + * Returns a modified function which warns once by default. + * + * If `localStorage.noDeprecation = true` is set, then it is a no-op. + * + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public */ -Peer.prototype._notify = function(evt, ...args) { - if (this.events.hasOwnProperty(evt)) { - for (let i=0; i<this.events[evt].length; i++) { - let f = this.events[evt][i]; - f.apply(this, args); - } - } -} -/** - * Register a callback for socket events. Events include: 'connect', - * 'disconnect' and 'error'. - * - * @param {string} evt Event name - * @param {function} f Callback on event - */ -Peer.prototype.on = function(evt, f) { - if (!this.events.hasOwnProperty(evt)) { - this.events[evt] = []; - } - this.events[evt].push(f); +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; } /** - * Returns a string of the UUID + * Checks `localStorage` for boolean values for the given `name`. + * + * @param {String} name + * @returns {Boolean} + * @api private */ -Peer.prototype.convertUUID = function() { - const digits = "0123456789abcdef"; - let returnVal = ""; - - //If the char is "-" add it, else add the letter/digit represented in the variable digits - for(let i=0; i<cpp_my_uuid.length; i++){ - returnVal += (cpp_my_uuid[i] == "-") ? "-" : digits[digits.indexOf(cpp_my_uuid[i])] - } - return returnVal; -} - -module.exports = Peer; -}).call(this,require("buffer").Buffer) -},{"./utils/uuidParser":28,"buffer":35,"msgpack5":5,"uuid/v4":24}],26:[function(require,module,exports){ -const Peer = require('../../peer') -const VideoPlayer = require('./lib/VideoPlayer') - -let current_data = {}; -let peer; -let decoder; -let player; - -/** - * Validates that the user is logged in - */ -checkIfLoggedIn = async () => { - // const token = window.localStorage.getItem('token') - // console.log(token) - // if(!token){ - // console.log("You need to login") - // renderLogin() - // }else{ - - // //Check if the token is valid - // const response = await fetch('http://localhost:8080/auth/validation', { - // method: 'POST', - // headers: {'Authorization': token} - // }) - // console.log('RESPONSE', response) - - // //Token is valid, show available streams - // if(response.status === 200){ - // console.log("SUCCESS") - renderThumbnails() - - // } - // } -} - -//Redirects the user to google authentication -handleLogin = () => { - window.location.href="/google"; -} - -/** - * Returns a list of available streams - */ -getAvailableStreams = async () => { - try{ - const streamsInJson = await fetch('http://localhost:8080/streams'); - const streams = await streamsInJson.json(); - console.log('AVAILABLE', streams) - return streams; - }catch(err){ - console.log(err) - } -} - - -createVideoPlayer = () => { - const containerDiv = document.getElementById('container') - containerDiv.innerHTML = `<h1>Stream ${current_data.uri} is live right here!</h1><br><button onclick="renderThumbnails(); closeStream()">Go back</button><br> - <canvas id="ftlab-stream-video" width="640" height="360"></canvas>`; - containerDiv.innerHTML += '<br>' - containerDiv.innerHTML += '' - createPeer(); - const canvas = document.getElementById("ftlab-stream-video") - player = new VideoPlayer(canvas) - console.log("PLAYER", player) - connectToStream(); -} - -/** - * Creates thumbnail (image) for all available streams and adds them to div class='container' - */ -renderThumbnails = async () => { - const thumbnails = await getAvailableStreams(); - // console.log('THUMBNAILS', thumbnails) - const containerDiv = document.getElementById('container') - containerDiv.innerHTML = ''; - containerDiv.innerHTML = `<button onClick="configs()">change configs</button>` - containerDiv.innerHTML += `<div class="ftlab-stream-thumbnails"></div>` - // console.log(containerDiv) - for(var i=0; i<thumbnails.length; i++){ - const encodedURI = encodeURIComponent(thumbnails[i]) - current_data.uri = thumbnails[i] - console.log("THUMBNAIL[i]", thumbnails[i]) - try{ - const someData = await fetch(`http://localhost:8080/stream/rgb?uri=${encodedURI}`) - console.log('SOME DATA', someData) - if(!someData.ok){ - throw new Error('Image not found') - } - const myBlob = await someData.blob(); - console.log('BLOB', myBlob) - const objectURL = URL.createObjectURL(myBlob); - // containerDiv.innerHTML += createCard() - containerDiv.innerHTML += createCard(objectURL, i+4) - }catch(err){ - console.log("Couldn't create thumbnail"); - console.log(err) - } - } -} - - -/** - * Renders button that will redirect to google login - */ -renderLogin = () => { - const containerDiv = document.getElementById('container'); - containerDiv.innerHTML = - `<div id='Login'> - <h2>Welcome to Future Technology Lab</h2> - <h3>Please login!</h3> - <a className="button" onClick="handleLogin()"> - <div> - <span class="svgIcon t-popup-svg"> - <svg class="svgIcon-use" width="25" height="37" viewBox="0 0 25 25"> - <g fill="none" fill-rule="evenodd"> - <path d="M20.66 12.693c0-.603-.054-1.182-.155-1.738H12.5v3.287h4.575a3.91 3.91 0 0 1-1.697 2.566v2.133h2.747c1.608-1.48 2.535-3.65 2.535-6.24z" fill="#4285F4"/> - <path d="M12.5 21c2.295 0 4.22-.76 5.625-2.06l-2.747-2.132c-.76.51-1.734.81-2.878.81-2.214 0-4.088-1.494-4.756-3.503h-2.84v2.202A8.498 8.498 0 0 0 12.5 21z" fill="#34A853"/> - <path d="M7.744 14.115c-.17-.51-.267-1.055-.267-1.615s.097-1.105.267-1.615V8.683h-2.84A8.488 8.488 0 0 0 4 12.5c0 1.372.328 2.67.904 3.817l2.84-2.202z" fill="#FBBC05"/> - <path d="M12.5 7.38c1.248 0 2.368.43 3.25 1.272l2.437-2.438C16.715 4.842 14.79 4 12.5 4a8.497 8.497 0 0 0-7.596 4.683l2.84 2.202c.668-2.01 2.542-3.504 4.756-3.504z" fill="#EA4335"/> - </g> - </svg> - </span> - <span class="button-label">Sign in with Google</span> - </div> - </a> - </div>` -} - - -/** - * Method to create a single thumbnail - */ -createCard = (url, viewers) => { - return `<div class='ftlab-card-component' > - <img src='${url}' class="thumbnail-img" alt="Hups" width="250px"></img> - <p>Viewers: ${viewers}</p> - <button onclick="createVideoPlayer()">button</button> - </div>` -} - - -createPeer = () => { - const ws = new WebSocket('ws://localhost:8080/'); - ws.binaryType = "arraybuffer"; - peer = new Peer(ws) -} - - -connectToStream = () => { - const uri = current_data.uri - const decodedURI = decodeURIComponent(current_data.uri); - player.playback(peer, decodedURI, uri); -} - -closeStream = () => { - peer.sock.close() -} - - - -/** - * ************** - * CONFIGURATIONS - * ************** - */ - -current_data.configURI = "ftl://utu.fi#reconstruction_snap8/net" - -configs = () => { - const container = document.getElementById("container"); - container.innerHTML = `<div class="ftlab-configurations"></div>`; - renderConfigOptions(); -} - - -renderConfigOptions = () => { - const input = `<p>input1</p><br>ftl://utu.fi#<input type="text">` - const doc = document.getElementsByClassName('ftlab-configurations')[0]; - doc.innerHTML = input; -} - -/** - * - */ -loadConfigs = async (str) => { - const configURI = encodeURIComponent(`ftl://utu.fi#reconstruction_snap8${str}`); - const uri = encodeURIComponent(current_data.uri) - const rawResp = await fetch(`http://localhost:8080/stream/config?settings=${configURI}&uri=${uri}`) - const response = await rawResp.json(); - const content = JSON.parse(response); - container.innerHTML += `<p>${response}</p>`; - console.log(content) -} - -// current_data.configData = '{"peers": 1}'; - -/** - * Method to send configurations to backend - */ -saveConfigs = async () => { - let {uri, configURI, configData} = current_data - const rawResp = await fetch('http://localhost:8080/stream/config', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({peerURI: uri, configURI, data: configData, saveToCPP: true}) - }); - const content = await rawResp.json(); - console.log(content) +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { + return false; + } + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; } -},{"../../peer":25,"./lib/VideoPlayer":27}],27:[function(require,module,exports){ +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],24:[function(require,module,exports){ /** - * VideoPlayer for our stream - * + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX */ +var byteToHex = []; +for (var i = 0; i < 256; ++i) { + byteToHex[i] = (i + 0x100).toString(16).substr(1); +} +function bytesToUuid(buf, offset) { + var i = offset || 0; + var bth = byteToHex; + // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 + return ([bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]]]).join(''); +} +module.exports = bytesToUuid; -function VideoPlayer(canvas) { - this.canvas = canvas; - this.ctx = canvas.getContext("2d"); - this.status_cb = null; - this.error_cb = null; - this.ratio = null; - this.filters = false; - this._reset() -} +},{}],25:[function(require,module,exports){ +// Unique ID creation requires a high quality random # generator. In the +// browser this is a little complicated due to unknown quality of Math.random() +// and inconsistent support for the `crypto` API. We do the best we can via +// feature-detection -VideoPlayer.prototype._reset = function() { - this.start = null; - this.frames = 0; - this.image_data = null; - this.running = false; - this.pending_image_data = null; -} +// getRandomValues needs to be invoked in a context where "this" is a Crypto +// implementation. Also, find the complete implementation of crypto on IE11. +var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) || + (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto)); +if (getRandomValues) { + // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto + var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef -/** @expose */ -VideoPlayer.prototype.set_status_callback = function(callback) { - this.status_cb = callback; -}; + module.exports = function whatwgRNG() { + getRandomValues(rnds8); + return rnds8; + }; +} else { + // Math.random()-based (RNG) + // + // If all else fails, use Math.random(). It's fast, but is of unspecified + // quality. + var rnds = new Array(16); -VideoPlayer.prototype._set_status = function() { - if (this.status_cb) { - this.status_cb.apply(this.status_cb, arguments); + module.exports = function mathRNG() { + for (var i = 0, r; i < 16; i++) { + if ((i & 0x03) === 0) r = Math.random() * 0x100000000; + rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; } -}; -/** @expose */ -VideoPlayer.prototype.set_error_callback = function(callback) { - this.error_cb = callback; -}; + return rnds; + }; +} -VideoPlayer.prototype._set_error = function(error, message) { - if (this.error_cb) { - this.error_cb(error, message); - } -}; +},{}],26:[function(require,module,exports){ +var rng = require('./lib/rng'); +var bytesToUuid = require('./lib/bytesToUuid'); -VideoPlayer.prototype._display_image = function(image) { - if (!this.start) { - this.start = new Date(); - this._set_status("playing"); - } else { - this.frames += 1; - var duration = (new Date()) - this.start; - if (duration > 1000) { - this._set_status("fps", this.frames / (duration * 0.001)); - } - } +function v4(options, buf, offset) { + var i = buf && offset || 0; - var w = image.get_width(); - var h = image.get_height(); - if (w != this.canvas.width || h != this.canvas.height || !this.image_data) { - this.canvas.width = w; - this.canvas.height = h; - this.image_data = this.ctx.createImageData(w, h); - var image_data = this.image_data.data; - for (var i=0; i<w*h; i++) { - image_data[i*4+3] = 255; - } + if (typeof(options) == 'string') { + buf = options === 'binary' ? new Array(16) : null; + options = null; + } + options = options || {}; + + var rnds = options.random || (options.rng || rng)(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + for (var ii = 0; ii < 16; ++ii) { + buf[i + ii] = rnds[ii]; } + } - var that = this; - image.display(this.image_data, function(display_image_data) { - if (window.requestAnimationFrame) { - that.pending_image_data = display_image_data; - window.requestAnimationFrame(function() { - if (that.pending_image_data) { - that.ctx.putImageData(that.pending_image_data, 0, 0); - that.pending_image_data = null; - } - }); - } else { - that.ctx.putImageData(display_image_data, 0, 0); - } - }); -}; + return buf || bytesToUuid(rnds); +} + +module.exports = v4; + +},{"./lib/bytesToUuid":24,"./lib/rng":25}],27:[function(require,module,exports){ +(function (Buffer){ +const msgpack = require('msgpack5')() + , encode = msgpack.encode + , decode = msgpack.decode; +const uuidv4 = require('uuid/v4') +const uuidParser = require('./utils/uuidParser') + +const kConnecting = 1; +const kConnected = 2; +const kDisconnected = 3; + +// Generate a unique id for this webservice +let cpp_my_uuid = uuidv4(); +console.log(cpp_my_uuid) +let my_uuid = uuidParser.parse(uuidv4()) +my_uuid = new Uint8Array(cpp_my_uuid); +// my_uuid[0] = 44; +my_uuid = Buffer.from(my_uuid); + +const kMagic = 0x0009340053640912; +const kVersion = 0; + + +/** + * Wrap a web socket with a MsgPack RCP protocol that works with our C++ version. + * @param {websocket} ws Websocket object + */ +function Peer(ws) { + this.sock = ws; + this.status = kConnecting; + this.id = null; + this.string_id = ""; + this.bindings = {}; + this.proxies = {}; + this.events = {}; + this.callbacks = {}; + this.cbid = 0; + + this.uri = "unknown"; + this.name = "unknown"; + this.master = false; + + let message = (raw) => { + // console.log(raw) + //Gets right data for client + if(this.sock.on === undefined){ + raw = raw.data; + } + let msg = decode(raw); + // console.log('MSG', msg) + if (this.status == kConnecting) { + if (msg[1] != "__handshake__") { + console.log("Bad handshake"); + this.close(); + } + } + if (msg[0] == 0) { + console.log("MSG", msg[1]); + // Notification + if (msg.length == 3) { + this._dispatchNotification(msg[1], msg[2]); + // Call + } else { + this._dispatchCall(msg[2], msg[1], msg[3]); + } + } else if (msg[0] == 1) { + this._dispatchResponse(msg[1], msg[3]); + } + } + + let close = () => { + this.status = kDisconnected; + this._notify("disconnect", this); + } + + let error = () => { + console.error("Socket error"); + this.sock.close(); + this.status = kDisconnected; + } + + //if undefined, peer is being used by client + if(this.sock.on === undefined){ + this.sock.onmessage = message; + this.sock.onclose = close; + this.sock.onopen = (event) => { + this.send("__handshake__", kMagic, kVersion, [my_uuid]); + } + //else peer is being used by server + }else{ + this.sock.on("message", message); + this.sock.on("close", close); + this.sock.on("error", error); + } + + this.bind("__handshake__", (magic, version, id) => { + if (magic == kMagic) { + console.log("Handshake received"); + this.status = kConnected; + this.id = id.buffer; + this.string_id = id.toString('hex'); + this._notify("connect", this); + // if(this.sock.on === undefined){ + // this.send("__handshake__", kMagic, kVersion, [my_uuid]); + // } + } else { + console.log("Magic does not match"); + this.close(); + } + }); + this.send("__handshake__", kMagic, kVersion, [my_uuid]); +} +Peer.uuid = my_uuid; +/** + * @private + */ +Peer.prototype._dispatchNotification = function(name, args) { + if (this.bindings.hasOwnProperty(name)) { + //console.log("Notification for: ", name); + this.bindings[name].apply(this, args); + } else { + console.log("Missing handler for: ", name); + } +} -VideoPlayer.prototype._handle_onload = function(peer, decodedURI, uri) { - var that = this; - this._set_status("initializing"); +/** + * @private + */ +Peer.prototype._dispatchCall = function(name, id, args) { + if (this.bindings.hasOwnProperty(name)) { + //console.log("Call for:", name, id); - var decoder = new libde265.Decoder(); - decoder.set_image_callback(function(image) { - that._display_image(image); - image.free(); - }); - var ratio = null; - var filters = false; - + try { + let res = this.bindings[name].apply(this, args); + this.sock.send(encode([1,id,name,res])); + } catch(e) { + console.error("Could to dispatch or return call"); + this.close(); + } + } else if (this.proxies.hasOwnProperty(name)) { + //console.log("Proxy for:", name, id); + args.unshift((res) => { + try { + this.sock.send(encode([1,id,name,res])); + } catch(e) { + this.close(); + } + }); + this.proxies[name].apply(this, args); + } else { + console.log("Missing handler for: ", name); + } +} - var decode = function(pckg) { - if (!that.running) { return; } - console.log("PACKAGE", pckg) - var err; - if (pckg == null) { - return; - } else { - try { - var tmp = pckg - err = decoder.push_data(tmp); - console.log("ERR VALUE INSIDE TRY", err, tmp) - } catch(e) { - console.log(e); - err = decoder.flush(); - return; - } - } - console.log("ERR VALUE AFTER ELSE", err) - if (!libde265.de265_isOK(err)) { - that._set_error(err, libde265.de265_get_error_text(err)); - return; - } +/** + * @private + */ +Peer.prototype._dispatchResponse = function(id, res) { + if (this.callbacks.hasOwnProperty(id)) { + this.callbacks[id].call(this, res); + delete this.callbacks[id]; + } else { + console.log("Missing callback"); + } +} - if (that.ratio !== ratio) { - decoder.set_framerate_ratio(that.ratio); - ratio = that.ratio; - } +/** + * Register an RPC handler that will be called from a remote machine. Remotely + * passed arguments are provided to the given function as normal arguments, and + * if the function returns a value, it will be returned over the network also. + * + * @param {string} name The name of the function + * @param {function} f A function or lambda to be callable remotely + */ +Peer.prototype.bind = function(name, f) { + if (this.bindings.hasOwnProperty(name)) { + //console.error("Duplicate bind to same procedure"); + this.bindings[name] = f; + } else { + this.bindings[name] = f; + } +} - if (that.filters !== filters) { - decoder.disable_filters(that.filters); - filters = that.filters; - } +/** + * Allow an RPC call to pass through to another machine with minimal local + * processing. + */ +Peer.prototype.proxy = function(name, f) { + if (this.proxies.hasOwnProperty(name)) { + //console.error("Duplicate proxy to same procedure"); + this.proxies[name] = f; + } else { + this.proxies[name] = f; + } +} - /** - * Here's the bug - * For some reason the decode function evaluates cbErr - * to number 13 which is the case number for waiting for input data - */ - decoder.decode(function(cbErr) { - console.log("paramErr SHOULD BE 0, BUT IT'S", cbErr) - switch(cbErr) { - case libde265.DE265_ERROR_WAITING_FOR_INPUT_DATA: - console.log("DE265_ERROR_WAITING_FOR_INPUT_DATA"); - return; - default: - if (!libde265.de265_isOK(cbErr)) { - that._set_error(err, libde265.de265_get_error_text(paramErr)); - return; - } - } +/** + * Call a procedure on a remote machine. + * + * @param {string} name Name of the procedure + * @param {function} cb Callback to receive return value as argument + * @param {...} args Any number of arguments to also pass to remote procedure + */ +Peer.prototype.rpc = function(name, cb, ...args) { + let id = this.cbid++; + this.callbacks[id] = cb; - if (decoder.has_more()) { - console.log("has more"); - return; - } + try { + this.sock.send(encode([0, id, name, args])); + } catch(e) { + this.close(); + } +} - decoder.free(); - that.stop(); - console.log("SHOULD LOG THIS"); - }); - } +Peer.prototype.sendB = function(name, args) { + try { + this.sock.send(encode([0, name, args])); + } catch(e) { + this.close(); + } +} +/** + * Call a remote procedure but with no return value expected. + * + * @param {string} name Name of the procedure + * @param {...} args Any number of arguments to also pass to remote procedure + */ +Peer.prototype.send = function(name, ...args) { + try { + this.sock.send(encode([0, name, args])); + } catch(e) { + this.close(); + } +} - peer.bind(decodedURI, (latency, streampckg, pckg) => { - console.log(pckg[0]) - if(pckg[0] === 0){ - decode(pckg[5]); - }; - }) - // Start the transaction - peer.send("get_stream", (uri, 10, 0, uri)); -}; +/** + * Closes the socket + */ +Peer.prototype.close = function() { + if(this.sock.on !== undefined){ + this.sock.close(); + } + this.status = kDisconnected; +} -/** @expose */ -VideoPlayer.prototype.playback = function(peer, decodedURI, uri) { - this._reset(); +/** + * @private + */ +Peer.prototype._notify = function(evt, ...args) { + if (this.events.hasOwnProperty(evt)) { + for (let i=0; i<this.events[evt].length; i++) { + let f = this.events[evt][i]; + f.apply(this, args); + } + } +} - console.log(peer); - console.log(uri) - this._handle_onload(peer, decodedURI, uri) - this._set_status("loading"); - this.running = true; -}; +/** + * Register a callback for socket events. Events include: 'connect', + * 'disconnect' and 'error'. + * + * @param {string} evt Event name + * @param {function} f Callback on event + */ +Peer.prototype.on = function(evt, f) { + if (!this.events.hasOwnProperty(evt)) { + this.events[evt] = []; + } + this.events[evt].push(f); +} -/** @expose */ -VideoPlayer.prototype.stop = function() { - this._set_status("stopped"); - this._reset(); -}; +/** + * Returns a UUID in a string form + */ +Peer.prototype.getUuid = function() { + const digits = "0123456789abcdef"; + let uuid = ""; -/** @expose */ -VideoPlayer.prototype.set_framerate_ratio = function(ratio) { - this.ratio = ratio; -}; + //If the char is "-" add it, else add the letter/digit represented in the variable digits + for(let i=0; i<cpp_my_uuid.length; i++){ + uuid += (cpp_my_uuid[i] == "-") ? "-" : digits[digits.indexOf(cpp_my_uuid[i])] + } + return uuid; +} -/** @expose */ -VideoPlayer.prototype.disable_filters = function(disable) { - this.filters = disable; -}; +module.exports = Peer; -module.exports = VideoPlayer; -},{}],28:[function(require,module,exports){ +}).call(this,require("buffer").Buffer) +},{"./utils/uuidParser":28,"buffer":35,"msgpack5":7,"uuid/v4":26}],28:[function(require,module,exports){ // Maps for number <-> hex string conversion var _byteToHex = []; var _hexToByte = {}; @@ -5616,8 +5616,8 @@ var objectKeys = Object.keys || function (obj) { }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"object-assign":39,"util/":32}],30:[function(require,module,exports){ -arguments[4][3][0].apply(exports,arguments) -},{"dup":3}],31:[function(require,module,exports){ +arguments[4][5][0].apply(exports,arguments) +},{"dup":5}],31:[function(require,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' @@ -9165,9 +9165,9 @@ exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : }; }).call(this,require("timers").setImmediate,require("timers").clearImmediate) },{"process/browser.js":40,"timers":41}],42:[function(require,module,exports){ -arguments[4][3][0].apply(exports,arguments) -},{"dup":3}],43:[function(require,module,exports){ +arguments[4][5][0].apply(exports,arguments) +},{"dup":5}],43:[function(require,module,exports){ arguments[4][31][0].apply(exports,arguments) },{"dup":31}],44:[function(require,module,exports){ arguments[4][32][0].apply(exports,arguments) -},{"./support/isBuffer":43,"_process":40,"dup":32,"inherits":42}]},{},[26]); +},{"./support/isBuffer":43,"_process":40,"dup":32,"inherits":42}]},{},[1]); diff --git a/web-service/server/src/public/js/index.js b/web-service/public/js/index.js similarity index 96% rename from web-service/server/src/public/js/index.js rename to web-service/public/js/index.js index e99d5d412..4b774b125 100644 --- a/web-service/server/src/public/js/index.js +++ b/web-service/public/js/index.js @@ -1,4 +1,4 @@ -const Peer = require('../../peer') +const Peer = require('../../server/src/peer') const VideoPlayer = require('./lib/VideoPlayer') let current_data = {}; diff --git a/web-service/server/src/public/js/lib/VideoPlayer.js b/web-service/public/js/lib/VideoPlayer.js similarity index 100% rename from web-service/server/src/public/js/lib/VideoPlayer.js rename to web-service/public/js/lib/VideoPlayer.js diff --git a/web-service/server/src/public/js/lib/configs.js b/web-service/public/js/lib/configs.js similarity index 100% rename from web-service/server/src/public/js/lib/configs.js rename to web-service/public/js/lib/configs.js diff --git a/web-service/server/src/public/js/lib/libde265min.js b/web-service/public/js/lib/libde265min.js similarity index 100% rename from web-service/server/src/public/js/lib/libde265min.js rename to web-service/public/js/lib/libde265min.js diff --git a/web-service/server/src/index.js b/web-service/server/src/index.js index 29c842b6a..1f414cb1e 100644 --- a/web-service/server/src/index.js +++ b/web-service/server/src/index.js @@ -14,8 +14,7 @@ const bodyParser = require('body-parser') // ---- INDEXES ---------------------------------------------------------------- app.use(passport.initialize()); -app.use(express.static(__dirname + '/public')); - +app.use(express.static(__dirname + '/../../public')); app.use(bodyParser.json()) @@ -406,8 +405,7 @@ app.ws('/', (ws, req) => { }); p.bind("node_details", () => { - console.log(p.convertUUID()) - return [`{"title": "FTL Web-Service", "id": "${p.convertUUID()}", "kind": "master"}`]; + return [`{"title": "FTL Web-Service", "id": "${p.getUuid()}", "kind": "master"}`]; }); p.bind("list_streams", () => { diff --git a/web-service/server/src/peer.js b/web-service/server/src/peer.js index 161ae6533..1c2ff1505 100644 --- a/web-service/server/src/peer.js +++ b/web-service/server/src/peer.js @@ -273,17 +273,17 @@ Peer.prototype.on = function(evt, f) { } /** - * Returns a string of the UUID + * Returns a UUID in a string form */ -Peer.prototype.convertUUID = function() { +Peer.prototype.getUuid = function() { const digits = "0123456789abcdef"; - let returnVal = ""; + let uuid = ""; //If the char is "-" add it, else add the letter/digit represented in the variable digits for(let i=0; i<cpp_my_uuid.length; i++){ - returnVal += (cpp_my_uuid[i] == "-") ? "-" : digits[digits.indexOf(cpp_my_uuid[i])] + uuid += (cpp_my_uuid[i] == "-") ? "-" : digits[digits.indexOf(cpp_my_uuid[i])] } - return returnVal; + return uuid; } module.exports = Peer; -- GitLab