diff options
| author | 8cy <[email protected]> | 2020-04-03 02:37:42 -0700 |
|---|---|---|
| committer | 8cy <[email protected]> | 2020-04-03 02:37:42 -0700 |
| commit | 60867fb030bae582082340ead7dbc7efdc2f5398 (patch) | |
| tree | 4c6a7356351be2e4914e15c4703172597c45656e /node_modules/snekfetch/src/node/transports | |
| parent | commenting (diff) | |
| download | s5nical-60867fb030bae582082340ead7dbc7efdc2f5398.tar.xz s5nical-60867fb030bae582082340ead7dbc7efdc2f5398.zip | |
2020/04/03, 02:34, v1.2.0
Diffstat (limited to 'node_modules/snekfetch/src/node/transports')
3 files changed, 239 insertions, 0 deletions
diff --git a/node_modules/snekfetch/src/node/transports/ResponseStream.js b/node_modules/snekfetch/src/node/transports/ResponseStream.js new file mode 100644 index 0000000..1d40101 --- /dev/null +++ b/node_modules/snekfetch/src/node/transports/ResponseStream.js @@ -0,0 +1,24 @@ +const Stream = require('stream'); + +class ResponseStream extends Stream.Readable { + constructor() { + super(); + this.statusCode = 200; + this.status = 'OK'; + } + + error(code, message) { + this.statusCode = code; + this.status = message; + return this; + } + + on(event, handler) { + if (['end', 'open'].includes(event)) + handler(); + } + + _read() {} // eslint-disable-line no-empty-function +} + +module.exports = ResponseStream; diff --git a/node_modules/snekfetch/src/node/transports/file.js b/node_modules/snekfetch/src/node/transports/file.js new file mode 100644 index 0000000..a30c5fe --- /dev/null +++ b/node_modules/snekfetch/src/node/transports/file.js @@ -0,0 +1,113 @@ +const fs = require('fs'); +const path = require('path'); +const mime = require('../mime'); +const EventEmitter = require('events'); +const ResponseStream = require('./ResponseStream'); + +const methods = { + GET: (filename, req) => { + req.end = () => { + const stream = should404(filename) ? + new ResponseStream().error(404, `ENOENT: no such file or directory, open '${filename}'`) : + fs.createReadStream(filename); + req.res = stream; + stream.headers = { + 'content-length': 0, + 'content-type': mime.lookup(path.extname(filename)), + }; + stream.on('open', () => { + req.emit('response', stream); + }); + if (stream instanceof ResponseStream) + return; + stream.statusCode = 200; + stream.on('end', () => { + stream.headers['content-length'] = stream.bytesRead; + }); + /* istanbul ignore next */ + stream.on('error', (err) => { + stream.statusCode = 400; + stream.status = err.message; + }); + }; + }, + POST: (filename, req) => { + const chunks = []; + /* istanbul ignore next */ + req.write = (data) => { + chunks.push(data); + }; + req.end = (data) => { + chunks.push(data); + const stream = fs.createWriteStream(filename); + const standin = new ResponseStream(); + req.res = standin; + standin.headers = { + 'content-length': 0, + 'content-type': mime.lookup(path.extname(filename)), + }; + stream.on('finish', () => { + req.emit('response', standin); + }); + stream.on('open', () => { + (function write() { + const chunk = chunks.shift(); + if (!chunk) + return; + /* istanbul ignore next */ + if (!stream.write(chunk)) + stream.once('drain', write); + else + write(); + }()); + stream.end(); + }); + }; + }, + DELETE: (filename, req) => { + req.end = () => { + const stream = new ResponseStream(); + req.res = stream; + stream.headers = { + 'content-length': 0, + 'content-type': mime.lookup(path.extname(filename)), + }; + fs.unlink(filename, (err) => { + req.emit('response', err ? stream.error(400, err.message) : stream); + }); + }; + }, +}; + +class Req extends EventEmitter { + constructor() { + super(); + this._headers = {}; + } + + setHeader() {} // eslint-disable-line no-empty-function + getHeader() {} // eslint-disable-line no-empty-function +} + +function request(options) { + const method = methods[options.method]; + if (!method) + throw new Error(`Invalid request method for file: "${options.method}"`); + const filename = options.href.replace('file://', ''); + + const req = new Req(); + method(filename, req, options); + return req; +} + +function should404(p) { + try { + return fs.lstatSync(p).isDirectory(); + } catch (err) { + return true; + } +} + +module.exports = { + request, +}; diff --git a/node_modules/snekfetch/src/node/transports/http2.js b/node_modules/snekfetch/src/node/transports/http2.js new file mode 100644 index 0000000..326b32e --- /dev/null +++ b/node_modules/snekfetch/src/node/transports/http2.js @@ -0,0 +1,102 @@ +const http = require('http'); +const Stream = require('stream'); +const EventEmitter = require('events'); +const http2 = (() => { + try { + const h2 = require('http2'); + if (!h2.constants) + throw new Error('DAMN_YOU_NPM_HTTP2'); + return h2; + } catch (err) { + return { + constants: {}, + connect: () => { + throw new Error('Please run node with --expose-http2 to use the http2 request transport'); + }, + }; + } +})(); + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_STATUS, +} = http2.constants; + +class Http2Request extends EventEmitter { + constructor(options) { + super(); + this.options = options; + this._headers = { + [HTTP2_HEADER_PATH]: options.pathname, + [HTTP2_HEADER_METHOD]: options.method.toUpperCase(), + }; + } + + setHeader(name, value) { + this._headers[name.toLowerCase()] = value; + } + + getHeader(name) { + return this._headers[name]; + } + + getHeaders() { + return this._headers; + } + + get path() { + return this._headers[HTTP2_HEADER_PATH]; + } + + set path(path) { + this._headers[HTTP2_HEADER_PATH] = path; + } + + end(data) { + const options = this.options; + const client = http2.connect(`${options.protocol}//${options.hostname}`); + const req = client.request(this._headers); + const stream = new Stream.PassThrough(); + + client.once('error', (e) => this.emit('error', e)); + client.once('frameError', (e) => this.emit('error', e)); + + req.once('error', (e) => this.emit('error', e)); + + req.once('response', (headers) => { + stream.headers = headers; + stream.statusCode = headers[HTTP2_HEADER_STATUS]; + stream.status = http.STATUS_CODES[stream.statusCode]; + + this.emit('response', stream); + this.response = stream; + + req.on('data', (chunk) => { + if (!stream.push(chunk)) + req.pause(); + }); + + req.once('end', () => { + stream.push(null); + client.destroy(); + }); + + stream.once('error', (err) => { + stream.statusCode = 400; + stream.status = err.message; + }); + }); + + req.end(data); + + return req; + } +} + + +function request(options) { + return new Http2Request(options); +} + +module.exports = { request }; |