test-ros-server.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. const ROS = require('realm-object-server');
  2. const fs = require('fs');
  3. const http = require('http');
  4. const httpProxy = require('http-proxy');
  5. const os = require('os');
  6. const path = require('path');
  7. // Bypass the mandatory email prompt.
  8. process.env.ROS_TOS_EMAIL_ADDRESS = 'ci@realm.io';
  9. process.env.DOCKER_DATA_PATH = '/tmp';
  10. // Don't bother calling fsync() because we're throwing away all the files
  11. // between runs anyway
  12. process.env.REALM_DISABLE_SYNC_TO_DISK = 'true';
  13. // Workaround for <https://github.com/realm/realm-object-server-private/issues/950>.
  14. process.env.ROS_SUPERAGENT_RETRY_DELAY = '0';
  15. // Enable timestamps in the logs
  16. process.env.ROS_LOG_TIMESTAMP = '1';
  17. if (!process.env.SYNC_WORKER_FEATURE_TOKEN) {
  18. try {
  19. require(os.homedir() + '/.ros-feature-token.js');
  20. }
  21. catch (e) {
  22. console.error('ROS feature token not found. Running Object Server tests requires setting the SYNC_WORKER_FEATURE_TOKEN environment variable.');
  23. process.exit(1);
  24. }
  25. }
  26. // A "email handler" which actually just writes the tokens to files that the
  27. // tests can read
  28. class PasswordEmailHandler {
  29. constructor(dataRoot) {
  30. this.dataRoot = dataRoot;
  31. fs.mkdirSync(this.dataRoot);
  32. }
  33. resetPassword(email, token, userAgent, remoteIp) {
  34. fs.writeFileSync(path.join(this.dataRoot, email), token);
  35. return new Promise(r => setTimeout(r, 0));
  36. }
  37. confirmEmail(email, token) {
  38. fs.writeFileSync(path.join(this.dataRoot, email), token);
  39. return new Promise(r => setTimeout(r, 0));
  40. }
  41. }
  42. class Proxy {
  43. constructor(listenPort, targetPort) {
  44. this.proxy = httpProxy.createProxyServer({target: `http://127.0.0.1:${targetPort}`, ws: true});
  45. this.proxy.on('error', e => {
  46. console.log('proxy error', e);
  47. });
  48. this.server = http.createServer((req, res) => {
  49. this.web(req, res);
  50. });
  51. this.server.on('upgrade', (req, socket, head) => {
  52. this.ws(req, socket, head);
  53. });
  54. this.server.listen(listenPort);
  55. }
  56. stop() {
  57. this.server.close();
  58. this.proxy.close();
  59. }
  60. web(req, res) {
  61. this.proxy.web(req, res);
  62. }
  63. ws(req, socket, head) {
  64. this.proxy.ws(req, socket, head);
  65. }
  66. }
  67. // A simple proxy server that runs in front of ROS and validates custom headers
  68. class HeaderValidationProxy extends Proxy {
  69. web(req, res) {
  70. if (this.validate(req)) {
  71. this.proxy.web(req, res);
  72. }
  73. else {
  74. res.writeHead(400);
  75. res.end('Missing X-Allow-Connection header');
  76. }
  77. }
  78. ws(req, socket, head) {
  79. if (this.validate(req)) {
  80. this.proxy.ws(req, socket, head);
  81. }
  82. else {
  83. socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
  84. }
  85. }
  86. validate(req) {
  87. return !!req.headers['x-allow-connection'];
  88. }
  89. }
  90. // A proxy which sits in front of ROS and takes a long time to establish connections
  91. class SlowConnectingWebProxy extends Proxy {
  92. web(req, res) {
  93. setTimeout(() => this.proxy.web(req, res), 2000);
  94. }
  95. }
  96. class SlowConnectingWsProxy extends Proxy {
  97. ws(req, socket, head) {
  98. setTimeout(() => this.proxy.ws(req, socket, head), 2000);
  99. }
  100. }
  101. const server = new ROS.BasicServer();
  102. server.start({
  103. // The desired logging threshold. Can be one of: all, trace, debug, detail, info, warn, error, fatal, off)
  104. logLevel: 'off',
  105. // For all the full list of configuration parameters see:
  106. // https://realm.io/docs/realm-object-server/latest/api/ros/interfaces/serverconfig.html
  107. address: '0.0.0.0',
  108. port: 9080,
  109. httpsPort: 9443,
  110. https: true,
  111. httpsKeyPath: __dirname + '/certificates/localhost-cert-key.pem',
  112. httpsCertChainPath: __dirname + '/certificates/localhost-cert.pem',
  113. httpsForInternalComponents: false,
  114. dataPath: process.argv[2],
  115. authProviders: [
  116. new ROS.auth.DebugAuthProvider(),
  117. new ROS.auth.PasswordAuthProvider({
  118. autoCreateAdminUser: true,
  119. emailHandler: new PasswordEmailHandler(path.join(process.argv[2], 'email')),
  120. }),
  121. ],
  122. autoKeyGen: true,
  123. // Disable the legacy Realm-based permissions service
  124. permissionServiceConfigOverride: (config) => {
  125. config.enableManagementRealmReflection = false;
  126. config.enablePermissionRealmReflection = false;
  127. },
  128. }).then(() => {
  129. console.log('started');
  130. fs.closeSync(1);
  131. }).catch(err => {
  132. console.error(`Error starting Realm Object Server: ${err.message}`)
  133. });
  134. new HeaderValidationProxy(9081, 9080);
  135. new SlowConnectingWebProxy(9082, 9080);
  136. new SlowConnectingWsProxy(9083, 9080);