You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.6 KiB

11 years ago
11 years ago
11 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. #!/usr/bin/env node
  2. "use strict";
  3. var dope = require("console-dope"),
  4. connect = require("connect"),
  5. http = require("http"),
  6. Model = require("nature").Model,
  7. w = require("wodge"),
  8. path = require("path"),
  9. loadConfig = require("config-master"),
  10. morgan = require("morgan"),
  11. serveStatic = require("serve-static"),
  12. directory = require("serve-index"),
  13. compress = require("compression");
  14. var usage =
  15. "usage: \n\
  16. $ ws [--directory|-d <directory>] [--port|-p <port>] [--log-format|-f dev|default|short|tiny]\n\
  17. $ ws --compress|-c \n\
  18. $ ws --help|-h";
  19. function halt(message){
  20. dope.red.log("Error: %s", message);
  21. dope.log(usage);
  22. process.exit(1);
  23. }
  24. /* parse command-line args */
  25. var argv = new Model()
  26. .define({ name: "port", alias: "p", type: "number", defaultOption: true, value: 8000 })
  27. .define({ name: "log-format", alias: "f", type: "string" })
  28. .define({ name: "help", alias: "h", type: "boolean" })
  29. .define({ name: "directory", alias: "d", type: "string", value: process.cwd() })
  30. .define({ name: "config", type: "boolean" })
  31. .define({ name: "compress", alias: "c", type: "boolean" });
  32. /* Merge together options from "~/.local-web-server.json", "{cwd}/.local-web-server.json" and "{cwd}/package.json", in that order. */
  33. var storedConfig = loadConfig(
  34. path.join(w.getHomeDir(), ".local-web-server.json"),
  35. path.join(process.cwd(), ".local-web-server.json"),
  36. path.join(process.cwd(), "package.json:local-web-server")
  37. );
  38. try {
  39. argv.set(storedConfig);
  40. } catch (err){
  41. dope.red.log("Failed to set stored config, please check your config files");
  42. halt(err.message);
  43. }
  44. /* Finally, set the options from the command-line, overriding all defaults. */
  45. try {
  46. argv.set(process.argv);
  47. } catch (err){
  48. dope.red.log("Failed to set command line options");
  49. halt(err.message);
  50. }
  51. /* Die here if invalid args received */
  52. if (!argv.valid) halt(argv.validationMessages);
  53. if (argv.config){
  54. dope.log("Stored config: ");
  55. dope.log(storedConfig);
  56. process.exit(0);
  57. } else if (argv.help){
  58. dope.log(usage);
  59. } else {
  60. var total = {
  61. req: 0,
  62. bytes: 0,
  63. connections: 0
  64. };
  65. process.on("SIGINT", function(){
  66. dope.showCursor();
  67. dope.log();
  68. process.exit(0);
  69. });
  70. /* customised logger :date token, purely to satisfy Logstalgia. */
  71. morgan.token("date", function(){
  72. var a = new Date();
  73. return (a.getDate() + "/" + a.getUTCMonth() + "/" + a.getFullYear() + ":" + a.toTimeString())
  74. .replace("GMT", "").replace(" (BST)", "");
  75. });
  76. var app = connect();
  77. /* log using --log-format (if supplied), else output statics */
  78. if(argv["log-format"]){
  79. app.use(morgan(argv["log-format"]));
  80. } else {
  81. app.use(function(req, res, next){
  82. dope.column(1).write(++total.req);
  83. next();
  84. });
  85. }
  86. /* --compress enables compression */
  87. if (argv.compress) app.use(compress());
  88. /* static file server including directory browsing support */
  89. app.use(serveStatic(path.resolve(argv.directory)))
  90. .use(directory(path.resolve(argv.directory), { icons: true }));
  91. /* launch server */
  92. var server = http.createServer(app)
  93. .on("error", function(err){
  94. if (err.code === "EADDRINUSE"){
  95. halt("port " + argv.port + " is already is use");
  96. } else {
  97. halt(err.message);
  98. }
  99. })
  100. .listen(argv.port);
  101. /* write status to stderr so stdout can be piped to disk ($ ws > log.txt) */
  102. if (path.resolve(argv.directory) === process.cwd()){
  103. dope.error("serving at %underline{%s}", "http://localhost:" + argv.port);
  104. } else {
  105. dope.error("serving %underline{%s} at %underline{%s}", argv.directory, "http://localhost:" + argv.port);
  106. }
  107. /* in stats mode, monitor connections and bytes transferred */
  108. if (!argv["log-format"]){
  109. dope.hideCursor();
  110. dope.log("%underline{Requests} %underline{Data} %underline{Connections}");
  111. server.on("connection", function(socket){
  112. var oldWrite = socket.write;
  113. socket.write = function(data) {
  114. if (!Buffer.isBuffer(data)) {
  115. data = new Buffer(data);
  116. }
  117. oldWrite.call(this, data);
  118. total.bytes += data.length;
  119. dope.column(12).write(w.padRight(w.bytesToSize(total.bytes, 2), 12));
  120. };
  121. dope.column(24).write(++total.connections);
  122. socket.on("close", function(){
  123. dope.column(24).write(w.padRight(--total.connections));
  124. });
  125. });
  126. }
  127. }