UNPKG

mach

Version:
222 lines (192 loc) 7.07 kB
"use strict"; var fs = require("fs"); var d = require("describe-property"); var objectAssign = require("object-assign"); var getMimeType = require("../utils/getMimeType"); var filterProperties = require("../utils/filterProperties"); var stringifyCookie = require("../utils/stringifyCookie"); var saveToDisk = require("../utils/saveToDisk"); module.exports = function (mach) { mach.bind = require("../utils/bindApp"); mach.createConnection = require("../utils/createConnection"); mach.serve = require("../utils/serveApp"); Object.defineProperties(mach.Connection.prototype, { /** * True if the request uses XMLHttpRequest, false otherwise. */ isXHR: d.gs(function () { return this.request.headers["X-Requested-With"] === "XMLHttpRequest"; }), /** * A high-level method that returns a promise for an object that is the * union of parameters contained in the request body and query string. * * The paramTypes argument may be used to filter parameters. It functions * like a whitelist of acceptable parameters and increases the security of * your app by not returning any parameters that you do not specify. * * // This function parses a list of comma-separated values in * // a request parameter into an array. * function parseList(value) { * return value.split(','); * } * * function app(conn) { * return conn.getParams({ * name: String, * age: Number, * hobbies: parseList * }).then(function (params) { * // params.name will be a string, params.age a number, and * // params.hobbies an array if they were provided in the * // request. params won't contain any other properties. * }); * } * * Of course, paramTypes may be omitted entirely to get a hash of all parameters. * * The maxLength argument is passed directly to the request's parseContent method. * * var maxUploadLimit = Math.pow(2, 20); // 1 mb * * function app(conn) { * return conn.getParams(maxUploadLimit).then(function (params) { * // params is the union of query and request content params * }); * } * * Note: Content parameters take precedence over query parameters with the same name. */ getParams: d(function (paramTypes, maxLength) { if (typeof paramTypes !== "object") { maxLength = paramTypes; paramTypes = null; } var request = this.request; var queryParams = objectAssign({}, this.query); return request.parseContent(maxLength).then(function (contentParams) { // Content params take precedence over query params. var params = objectAssign(queryParams, contentParams); return paramTypes ? filterProperties(params, paramTypes) : params; }); }), /** * Redirects the client to the given location. If status is not * given, it defaults to 302 Found. */ redirect: d(function (status, location) { if (typeof status !== "number") { location = status; status = 302; } this.status = status; this.response.headers.Location = location; }), /** * Redirects the client back to the URL they just came from, or * to the given location if it isn't known. */ back: d(function (location) { this.redirect(this.request.headers.Referer || location || "/"); }), /** * A quick way to write the status and/or content to the response. * * Examples: * * conn.send(404); * conn.send(404, 'Not Found'); * conn.send('Hello world'); * conn.send(fs.createReadStream('welcome.txt')); */ send: d(function (status, content) { if (typeof status === "number") { this.status = status; } else { content = status; } if (content != null) this.response.content = content; }), /** * Sends the given text in a text/plain response. */ text: d(function (status, text) { this.response.contentType = "text/plain"; this.send(status, text); }), /** * Sends the given HTML in a text/html response. */ html: d(function (status, html) { this.response.contentType = "text/html"; this.send(status, html); }), /** * Sends the given JSON in an application/json response. */ json: d(function (status, json) { this.response.contentType = "application/json"; if (typeof status === "number") { this.status = status; } else { json = status; } if (json != null) this.response.content = typeof json === "string" ? json : JSON.stringify(json); }), /** * Sends a file to the client with the given options. The following * options are available: * * - content/path The raw file content as a string, Buffer, stream, or * path to a file on disk * - type The Content-Type of the file. Defaults to a guess based * on the file extension when a file path is given * - length/size The Content-Length of the file, if it's known. Defaults * to the size of the file when a file path is given * * Examples: * * response.file('path/to/file.txt'); * response.file(200, 'path/to/file.txt'); */ file: d(function (status, options) { if (typeof status === "number") { this.status = status; } else { options = status; } var response = this.response; if (typeof options === "string") options = { path: options }; if (options.content) { response.content = options.content; } else if (typeof options.path === "string") { response.content = fs.createReadStream(options.path); } else { throw new Error("Missing file content/path"); } if (options.type || options.path) response.headers["Content-Type"] = options.type || getMimeType(options.path); if (options.length || options.size) { response.headers["Content-Length"] = options.length || options.size; } else if (typeof options.path === "string") { response.headers["Content-Length"] = fs.statSync(options.path).size; } }) }); mach.extend(require("./multipart")); var _handlePart = mach.Message.prototype.handlePart; Object.defineProperties(mach.Message.prototype, { /** * Sets a cookie with the given name and options. */ setCookie: d(function (name, options) { this.addHeader("Set-Cookie", stringifyCookie(name, options)); }), /** * Override the multipart extension's Message#handlePart to enable * streaming file uploads to disk when parsing multipart messages. */ handlePart: d(function (part) { return part.filename ? saveToDisk(part, "MachUpload-") : _handlePart.apply(this, arguments); }) }); };