response.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. var logging = require("./logging");
  2. var config = require("./config");
  3. var crc = require("crc").crc32;
  4. var human_status = {
  5. "-2": "user error",
  6. "-1": "server error",
  7. 0: "none",
  8. 1: "cached",
  9. 2: "downloaded",
  10. 3: "checked",
  11. };
  12. // handles HTTP responses
  13. // +request+ a http.IncomingMessage
  14. // +response+ a http.ServerResponse
  15. // +result+ an object with:
  16. // * status: see human_status, required
  17. // * redirect: redirect URL
  18. // * body: file or message, required unless redirect is present or status is < 0
  19. // * type: a valid Content-Type for the body, defaults to "text/plain"
  20. // * hash: image hash, required when body is an image
  21. // * err: a possible Error
  22. module.exports = function(request, response, result) {
  23. response.on("close", function() {
  24. logging.warn(request.id, "Connection closed");
  25. });
  26. response.on("finish", function() {
  27. logging.log(request.id, response.statusCode, "(" + human_status[result.status] + ")");
  28. });
  29. response.on("error", function(err) {
  30. logging.error(request.id, err);
  31. });
  32. // These headers are the same for every response
  33. var headers = {
  34. "Content-Type": result.type || "text/plain",
  35. "Cache-Control": "max-age=" + config.browser_cache_time + ", public",
  36. "Response-Time": Date.now() - request.start,
  37. "X-Storage-Type": human_status[result.status],
  38. "X-Request-ID": request.id,
  39. "Access-Control-Allow-Origin": "*"
  40. };
  41. if (result.err) {
  42. logging.error(result.err);
  43. }
  44. if (result.body) {
  45. // use Mojang's image hash if available
  46. // use crc32 as a hash function otherwise
  47. var etag = result.body && result.hash && result.hash.substr(0, 10) || crc(result.body);
  48. headers.Etag = "\"" + etag + "\"";
  49. // handle etag caching
  50. var incoming_etag = request.headers["if-none-match"];
  51. if (incoming_etag && incoming_etag === headers.Etag) {
  52. logging.debug("Etag matches");
  53. response.writeHead(304, headers);
  54. response.end();
  55. return;
  56. }
  57. }
  58. if (result.redirect) {
  59. headers.Location = result.redirect;
  60. response.writeHead(307, headers);
  61. response.end();
  62. return;
  63. }
  64. if (result.status === -2) {
  65. response.writeHead(422, headers);
  66. response.end(result.body);
  67. } else if (result.status === -1) {
  68. response.writeHead(500, headers);
  69. response.end(result.body);
  70. } else {
  71. response.writeHead(200, headers);
  72. response.end(result.body);
  73. }
  74. };