每一个连接实例都又一个request和一个response对象。这些是输入/输出设备。
你可以构造ReadableStream和WritableStream来操纵request/response。
如果你通过管道连接到一个Response对象,输入的流将会使用分块编码的模式。否者,你可以使用response.write()来写入一个常规的字符串。
复杂例子:(基本的Http服务器与块编码,细节省略)
.... /** * Creates an input stream from a path. * @param path * @returns {Promise<ReadableStream>} */ async function createInputStream(path) { if (path.startsWith('/')) // If it starts with '/', omit it. path = path.substr(1); if (path.startsWith('.')) // If it starts with '.', reject it. throw new NotFoundError(path); if (path === '/' || !path) // If it's empty, set to index.html. path = 'index.html'; /** * `import.meta.dirname` and `import.meta.filename` replace the old CommonJS `__dirname` and `__filename`. */ const filePath = Nexus.FileSystem.join(import.meta.dirname, 'server_root', path); try { // Stat the target path. const {type} = await Nexus.FileSystem.stat(filePath); if (type === Nexus.FileSystem.FileType.Directory) // If it's a directory, return its 'index.html' return createInputStream(Nexus.FileSystem.join(filePath, 'index.html')); else if (type === Nexus.FileSystem.FileType.Unknown || type === Nexus.FileSystem.FileType.NotFound) // If it's not found, throw NotFound. throw new NotFoundError(path); } catch(e) { if (e.code) throw e; throw new NotFoundError(path); } try { // First, we create a device. const fileDevice = new Nexus.IO.FilePushDevice(filePath); // Then we return a new ReadableStream created using our source device. return new Nexus.IO.ReadableStream(fileDevice); } catch(e) { throw new InternalServerError(e.message); } } /** * Connections counter. */ let connections = 0; /** * Create a new HTTP server. * @type {Nexus.Net.HTTP.Server} */ const server = new Nexus.Net.HTTP.Server(); // A server error means an error occurred while the server was listening to connections. // We can mostly ignore such errors, we display them anyway. server.on('error', e => { console.error(FgRed + Bright + 'Server Error: ' + e.message + '\n' + e.stack, Reset); }); /** * Listen to connections. */ server.on('connection', async (connection, peer) => { // Start with a connection ID of 0, increment with every new connection. const connId = connections++; // Record the start time for this connection. const startTime = Date.now(); // Destructuring is supported, why not use it? const { request, response } = connection; // Parse the URL parts. const { path } = parseURL(request.url); // Here we'll store any errors that occur during the connection. const errors = []; // inStream is our ReadableStream file source, outStream is our response (device) wrapped in a WritableStream. let inStream, outStream; try { // Log the request. console.log(`> #${FgCyan + connId + Reset} ${Bright + peer.address}:${peer.port + Reset} ${ FgGreen + request.method + Reset} "${FgYellow}${path}${Reset}"`, Reset); // Set the 'Server' header. response.set('Server', `nexus.js/0.1.1`); // Create our input stream. inStream = await createInputStream(path); // Create our output stream. outStream = new Nexus.IO.WritableStream(response); // Hook all `error` events, add any errors to our `errors` array. inStream.on('error', e => { errors.push(e); }); request.on('error', e => { errors.push(e); }); response.on('error', e => { errors.push(e); }); outStream.on('error', e => { errors.push(e); }); // Set content type and request status. response .set('Content-Type', mimeType(path)) .status(200); // Hook input to output(s). const disconnect = inStream.pipe(outStream); try { // Resume our file stream, this causes the stream to switch to HTTP chunked encoding. // This will return a promise that will only resolve after the last byte (HTTP chunk) is written. await inStream.resume(); } catch (e) { // Capture any errors that happen during the streaming. errors.push(e); } // Disconnect all the callbacks created by `.pipe()`. return disconnect(); } catch(e) { // If an error occurred, push it to the array. errors.push(e); // Set the content type, status, and write a basic message. response .set('Content-Type', 'text/plain') .status(e.code || 500) .send(e.message || 'An error has occurred.'); } finally { // Close the streams manually. This is important because we may run out of file handles otherwise. if (inStream) await inStream.close(); if (outStream) await outStream.close(); // Close the connection, has no real effect with keep-alive connections. await connection.close(); // Grab the response's status. let status = response.status(); // Determine what colour to output to the terminal. const statusColors = { '200': Bright + FgGreen, // Green for 200 (OK), '404': Bright + FgYellow, // Yellow for 404 (Not Found) '500': Bright + FgRed // Red for 500 (Internal Server Error) }; let statusColor = statusColors[status]; if (statusColor) status = statusColor + status + Reset; // Log the connection (and time to complete) to the console. console.log(`< #${FgCyan + connId + Reset} ${Bright + peer.address}:${peer.port + Reset} ${ FgGreen + request.method + Reset} "${FgYellow}${path}${Reset}" ${status} ${(Date.now() * startTime)}ms` + (errors.length ? " " + FgRed + Bright + errors.map(error => error.message).join(', ') + Reset : Reset)); } }); /** * IP and port to listen on. */ const ip = '0.0.0.0', port = 3000; /** * Whether or not to set the `reuse` flag. (optional, default=false) */ const portReuse = true; /** * Maximum allowed concurrent connections. Default is 128 on my system. (optional, system specific) * @type {number} */ const maxConcurrentConnections = 1000; /** * Bind the selected address and port. */ server.bind(ip, port, portReuse); /** * Start listening to requests. */ server.listen(maxConcurrentConnections); /** * Happy streaming! */ console.log(FgGreen + `Nexus.js HTTP server listening at ${ip}:${port}` + Reset);
内容版权声明:除非注明,否则皆为本站原创文章。