'use strict' const { test } = require('node:test') const http = require('node:http') const path = require('node:path') const request = require('supertest') const send = require('../lib/send').send const { shouldNotHaveBody, createServer, shouldNotHaveHeader } = require('./utils') const dateRegExp = /^\w{3}, \d+ \w+ \d+ \d+:\d+:\d+ \w+$/ const fixtures = path.join(__dirname, 'fixtures') test('send(file)', async function (t) { t.plan(22) await t.test('should stream the file contents', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('Content-Length', '4') .expect(200, 'tobi') }) await t.test('should stream a zero-length file', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/empty.txt') .expect('Content-Length', '0') .expect(200, '') }) await t.test('should decode the given path as a URI', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/some%20thing.txt') .expect(200, 'hey') }) await t.test('should serve files with dots in name', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/do..ts.txt') .expect(200, '...') }) await t.test('should treat a malformed URI as a bad request', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/some%99thing.txt') .expect(400, /Bad Request/) }) await t.test('should 400 on NULL bytes', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/some%00thing.txt') .expect(400, /Bad Request/) }) await t.test('should treat an ENAMETOOLONG as a 404', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) const path = Array(100).join('foobar') await request(app) .get('/' + path) .expect(404) }) await t.test('should support HEAD', async function (t) { t.plan(1) const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .head('/name.txt') .expect(200) .expect('Content-Length', '4') .expect(shouldNotHaveBody(t)) }) await t.test('should add an ETag header field', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('etag', /^W\/"[^"]+"$/) }) await t.test('should add a Date header field', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('date', dateRegExp) }) await t.test('should add a Last-Modified header field', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('last-modified', dateRegExp) }) await t.test('should add a Accept-Ranges header field', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('Accept-Ranges', 'bytes') }) await t.test('should 404 if the file does not exist', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/meow') .expect(404, /Not Found/) }) await t.test('should 404 if the filename is too long', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) const longFilename = new Array(512).fill('a').join('') await request(app) .get('/' + longFilename) .expect(404, /Not Found/) }) await t.test('should 404 if the requested resource is not a directory', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/nums.txt/invalid') .expect(404, /Not Found/) }) await t.test('should not override content-type', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, { ...headers, 'Content-Type': 'application/x-custom' }) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('Content-Type', 'application/x-custom') }) await t.test('should set Content-Type via mime map', async function (t) { const app = http.createServer(async function (req, res) { const { statusCode, headers, stream } = await send(req, req.url, { root: fixtures }) res.writeHead(statusCode, headers) stream.pipe(res) }) await request(app) .get('/name.txt') .expect('Content-Type', 'text/plain; charset=utf-8') .expect(200) await request(app) .get('/tobi.html') .expect('Content-Type', 'text/html; charset=utf-8') .expect(200) }) await t.test('send directory', async function (t) { t.plan(5) await t.test('should redirect directories to trailing slash', async function (t) { await request(createServer({ root: fixtures })) .get('/pets') .expect('Location', '/pets/') .expect(301) }) await t.test('should respond with an HTML redirect', async function (t) { await request(createServer({ root: fixtures })) .get('/pets') .expect('Location', '/pets/') .expect('Content-Type', /html/) .expect(301, />Redirecting to \/pets\/Redirecting to \/snow%20%E2%98%83\/Not Found