server.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*eslint-env node*/
  2. 'use strict';
  3. (function() {
  4. var express = require('express');
  5. var compression = require('compression');
  6. var fs = require('fs');
  7. var path = require('path')
  8. var url = require('url');
  9. var request = require('request');
  10. var gzipHeader = Buffer.from('1F8B08', 'hex');
  11. var yargs = require('yargs').options({
  12. 'port' : {
  13. 'default' : 8080,
  14. 'description' : 'Port to listen on.'
  15. },
  16. 'public' : {
  17. 'type' : 'boolean',
  18. 'description' : 'Run a public server that listens on all interfaces.'
  19. },
  20. 'upstream-proxy' : {
  21. 'description' : 'A standard proxy server that will be used to retrieve data. Specify a URL including port, e.g. "http://proxy:8000".'
  22. },
  23. 'bypass-upstream-proxy-hosts' : {
  24. 'description' : 'A comma separated list of hosts that will bypass the specified upstream_proxy, e.g. "lanhost1,lanhost2"'
  25. },
  26. 'help' : {
  27. 'alias' : 'h',
  28. 'type' : 'boolean',
  29. 'description' : 'Show this help.'
  30. }
  31. });
  32. var argv = yargs.argv;
  33. if (argv.help) {
  34. return yargs.showHelp();
  35. }
  36. // eventually this mime type configuration will need to change
  37. // https://github.com/visionmedia/send/commit/d2cb54658ce65948b0ed6e5fb5de69d022bef941
  38. // *NOTE* Any changes you make here must be mirrored in web.config.
  39. var mime = express.static.mime;
  40. mime.define({
  41. 'application/json' : ['czml', 'json', 'geojson', 'topojson'],
  42. 'application/wasm' : ['wasm'],
  43. 'image/crn' : ['crn'],
  44. 'image/ktx' : ['ktx'],
  45. 'model/gltf+json' : ['gltf'],
  46. 'model/gltf-binary' : ['bgltf', 'glb'],
  47. 'application/octet-stream' : ['b3dm', 'pnts', 'i3dm', 'cmpt', 'geom', 'vctr'],
  48. 'text/plain' : ['glsl']
  49. }, true);
  50. var app = express();
  51. app.use(compression());
  52. app.use(function(req, res, next) {
  53. res.header('Access-Control-Allow-Origin', '*');
  54. res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  55. next();
  56. });
  57. function checkGzipAndNext(req, res, next) {
  58. var reqUrl = url.parse(req.url, true);
  59. var filePath = reqUrl.pathname.substring(1);
  60. var readStream = fs.createReadStream(filePath, { start: 0, end: 2 });
  61. readStream.on('error', function(err) {
  62. next();
  63. });
  64. readStream.on('data', function(chunk) {
  65. if (chunk.equals(gzipHeader)) {
  66. res.header('Content-Encoding', 'gzip');
  67. }
  68. next();
  69. });
  70. }
  71. var knownTilesetFormats = [/\.b3dm/, /\.pnts/, /\.i3dm/, /\.cmpt/, /\.glb/, /\.geom/, /\.vctr/, /tileset.*\.json$/];
  72. app.get(knownTilesetFormats, checkGzipAndNext);
  73. app.use(express.static(__dirname));
  74. function getRemoteUrlFromParam(req) {
  75. var remoteUrl = req.params[0];
  76. if (remoteUrl) {
  77. // add http:// to the URL if no protocol is present
  78. if (!/^https?:\/\//.test(remoteUrl)) {
  79. remoteUrl = 'http://' + remoteUrl;
  80. }
  81. remoteUrl = url.parse(remoteUrl);
  82. // copy query string
  83. remoteUrl.search = url.parse(req.url).search;
  84. }
  85. return remoteUrl;
  86. }
  87. var dontProxyHeaderRegex = /^(?:Host|Proxy-Connection|Connection|Keep-Alive|Transfer-Encoding|TE|Trailer|Proxy-Authorization|Proxy-Authenticate|Upgrade)$/i;
  88. function filterHeaders(req, headers) {
  89. var result = {};
  90. // filter out headers that are listed in the regex above
  91. Object.keys(headers).forEach(function(name) {
  92. if (!dontProxyHeaderRegex.test(name)) {
  93. result[name] = headers[name];
  94. }
  95. });
  96. return result;
  97. }
  98. var upstreamProxy = argv['upstream-proxy'];
  99. var bypassUpstreamProxyHosts = {};
  100. if (argv['bypass-upstream-proxy-hosts']) {
  101. argv['bypass-upstream-proxy-hosts'].split(',').forEach(function(host) {
  102. bypassUpstreamProxyHosts[host.toLowerCase()] = true;
  103. });
  104. }
  105. app.get('/proxy/*', function(req, res, next) {
  106. // look for request like http://localhost:8080/proxy/http://example.com/file?query=1
  107. var remoteUrl = getRemoteUrlFromParam(req);
  108. if (!remoteUrl) {
  109. // look for request like http://localhost:8080/proxy/?http%3A%2F%2Fexample.com%2Ffile%3Fquery%3D1
  110. remoteUrl = Object.keys(req.query)[0];
  111. if (remoteUrl) {
  112. remoteUrl = url.parse(remoteUrl);
  113. }
  114. }
  115. if (!remoteUrl) {
  116. return res.status(400).send('No url specified.');
  117. }
  118. if (!remoteUrl.protocol) {
  119. remoteUrl.protocol = 'http:';
  120. }
  121. var proxy;
  122. if (upstreamProxy && !(remoteUrl.host in bypassUpstreamProxyHosts)) {
  123. proxy = upstreamProxy;
  124. }
  125. // encoding : null means "body" passed to the callback will be raw bytes
  126. request.get({
  127. url : url.format(remoteUrl),
  128. headers : filterHeaders(req, req.headers),
  129. encoding : null,
  130. proxy : proxy
  131. }, function(error, response, body) {
  132. var code = 500;
  133. if (response) {
  134. code = response.statusCode;
  135. res.header(filterHeaders(req, response.headers));
  136. }
  137. res.status(code).send(body);
  138. });
  139. });
  140. app.get('/wts/:layer/:z/:x/:y',function(req, res, next){
  141. var z = req.params['z']
  142. var x = req.params['x']
  143. var y = req.params['y']
  144. var l = req.params['layer']
  145. var file = path.join(__dirname,'static',l,z,x,y+'.pbf')
  146. fs.exists(file,function(exists){
  147. if(exists){
  148. res.header("Content-Type", "application/x-protobuf");
  149. res.send(fs.readFileSync(file))
  150. }else{
  151. res.end()
  152. }
  153. })
  154. })
  155. var server = app.listen(argv.port, argv.public ? undefined : 'localhost', function() {
  156. if (argv.public) {
  157. console.log('Cesium development server running publicly. Connect to http://localhost:%d/', server.address().port);
  158. } else {
  159. console.log('Cesium development server running locally. Connect to http://localhost:%d/', server.address().port);
  160. }
  161. });
  162. server.on('error', function (e) {
  163. if (e.code === 'EADDRINUSE') {
  164. console.log('Error: Port %d is already in use, select a different port.', argv.port);
  165. console.log('Example: node server.js --port %d', argv.port + 1);
  166. } else if (e.code === 'EACCES') {
  167. console.log('Error: This process does not have permission to listen on port %d.', argv.port);
  168. if (argv.port < 1024) {
  169. console.log('Try a port number higher than 1024.');
  170. }
  171. }
  172. console.log(e);
  173. process.exit(1);
  174. });
  175. server.on('close', function() {
  176. console.log('Cesium development server stopped.');
  177. });
  178. var isFirstSig = true;
  179. process.on('SIGINT', function() {
  180. if (isFirstSig) {
  181. console.log('Cesium development server shutting down.');
  182. server.close(function() {
  183. process.exit(0);
  184. });
  185. isFirstSig = false;
  186. } else {
  187. console.log('Cesium development server force kill.');
  188. process.exit(1);
  189. }
  190. });
  191. })();