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.

141 lines
4.3 KiB

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