Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to remove "x-frame-options" header from files returned by VirtualDirectory Middleware from angel3_static #72

Open
insinfo opened this issue Jul 12, 2022 · 7 comments
Assignees
Labels
enhancement New feature or request

Comments

@insinfo
Copy link

insinfo commented Jul 12, 2022

How to remove "x-frame-options" header from files returned by VirtualDirectory Middleware from angel3_static package: ^4.1.0

image

@dukefirehawk
Copy link
Collaborator

Based on initial scanning through the code, "x-frame-options" is not explicitly set anywhere. Will have to run through debug and analysis session to see where this comes from.

@thosakwe
Copy link
Contributor

It comes from HttpServer.defaultResponseHeaders. It's not set directly by Angel: https://api.dart.dev/stable/2.17.7/dart-io/HttpServer/defaultResponseHeaders.html

IIRC you can do something like HttpServer.defaultResponseHeaders.remove("x-frame-options");.

@dukefirehawk dukefirehawk added the enhancement New feature or request label Aug 29, 2022
@dukefirehawk dukefirehawk self-assigned this Sep 16, 2022
@insinfo
Copy link
Author

insinfo commented Oct 2, 2023

@thosakwe
Can I do this in the angel instance without modifying the angel package code?

@thosakwe
Copy link
Contributor

@thosakwe Can I do this in the angel instance without modifying the angel package code?

Hi Isaque, Http.defaultResponseHeaders is a global variable, if I remember correctly. It's not dependent on Angel, so you should be able to remove x-frame-options without modifying any Angel package code.

It's been a long time since I've touched Dart/Angel code, but I think you can just do something like this in the entry point of your program:

main() async {
  // Change global settings on startup
  HttpServer.defaultResponseHeaders.remove("x-frame-options");
  
  var app = Angel();
  // ... Initialize app logic...
}

Best of luck, and thanks everyone for keeping the spirit of Angel alive. It means a lot to me.

@insinfo
Copy link
Author

insinfo commented Oct 20, 2023

@thosakwe
Thank you very much for the feedback,
I saw that get defaultResponseHeaders is an instance property, it's not static, so I had to modify the angel3_hot.dart and angel3_production.dart files

angel3_hot.dart

// TODO add this
  /// Boots a shared server instance. Use this if launching multiple isolates.
  Future<HttpServer> Function(dynamic, int) _startSharedHttpServer() {    
    return (address, int port) async {
      final server =
          await HttpServer.bind(address ?? '127.0.0.1', port, shared: true);
      server.defaultResponseHeaders.remove('X-Frame-Options', 'SAMEORIGIN');    
      return Future.value(server);
    };
  }

Future<AngelHttp> _generateServer() async {
    var app = await generator();
    await Future.forEach(app.startupHooks, app.configure);
    app.optimizeForProduction();
// TODO change to _startSharedHttpServer
    final angelServer = await AngelHttp.custom(app, _startSharedHttpServer());
    return Future.value(angelServer);
  }

 /// Starts listening to requests and filesystem events.
  Future<HttpServer> startServer([address, int? port]) async {
    var isHot = true;
    _server = await _generateServer();

    if (_paths.isNotEmpty != true) {
      _logWarning(
          'You have instantiated a HotReloader without providing any filesystem paths to watch.');
    }

    bool sw(String s) {
      return Platform.executableArguments.any((ss) => ss.startsWith(s));
    }

    if (!sw('--observe') && !sw('--enable-vm-service')) {
      _logWarning(
          'You have instantiated a HotReloader without passing `--enable-vm-service` or `--observe` to the Dart VM. Hot reloading will be disabled.');
      isHot = false;
    } else {
      var info = await dev.Service.getInfo();
      var uri = info.serverUri!;
      uri = uri.replace(path: p.join(uri.path, 'ws'));
      if (uri.scheme == 'https') {
        uri = uri.replace(scheme: 'wss');
      } else {
        uri = uri.replace(scheme: 'ws');
      }
      _client = await vm.vmServiceConnectUri(uri.toString());
      _vmachine ??= await _client.getVM();
      _mainIsolate ??= _vmachine?.isolates?.first;

      if (_vmachine != null) {
        for (var isolate in _vmachine!.isolates ?? <vm.IsolateRef>[]) {
          if (isolate.id != null) {
            await _client.setIsolatePauseMode(isolate.id!,
                exceptionPauseMode: 'None');
          }
        }
      }

      await _listenToFilesystem();
    }

    _onChange.stream
        //.transform( _Debounce( Duration(seconds: 1)))
        .listen(_handleWatchEvent);

    while (_requestQueue.isNotEmpty) {
      await _handle(_requestQueue.removeFirst());
    }
    var server = _io = await HttpServer.bind(address ?? '127.0.0.1', port ?? 0);

    // TODO add this
    server.defaultResponseHeaders.remove('X-Frame-Options', 'SAMEORIGIN');   


    server.listen(handleRequest);

    // Print a Flutter-like prompt...
    if (enableHotkeys) {
      var serverUri =
          Uri(scheme: 'http', host: server.address.address, port: server.port);

      Uri? observatoryUri;
      if (isHot) {
        observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri!);
      }

      print(styleBold.wrap(
          '\n🔥  To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".'));
      stdout.write('Your server is listening at: ');
      print(wrapWith('$serverUri', [cyan, styleUnderlined]));

      if (isHot) {
        stdout.write(
            'An Observatory debugger and profiler on ${Platform.operatingSystem} is available at: ');

        print(wrapWith('$observatoryUri', [cyan, styleUnderlined]));
      } else {
        stdout.write(
            'The observatory debugger and profiler are not available.\n');
      }
      print(
          'For a more detailed help message, press "h". To quit, press "q".\n');

      if (_paths.isNotEmpty) {
        print(darkGray.wrap(
            'Changes to the following path(s) will also trigger a hot reload:'));

        for (var p in _paths) {
          print(darkGray.wrap('  * $p'));
        }

        stdout.writeln();
      }

      // Listen for hotkeys
      try {
        stdin.lineMode = stdin.echoMode = false;
      } catch (_) {}

      late StreamSubscription<int> sub;

      try {
        sub = stdin.expand((l) => l).listen((ch) async {
          if (ch == $r) {
            _handleWatchEvent(
                WatchEvent(ChangeType.MODIFY, '[manual-reload]'), isHot);
          }
          if (ch == $R) {
            _logInfo('Manually restarting server...\n');
            await _killServer();
            await _server!.close();
            var addr = _io.address.address;
            var port = _io.port;
            await _io.close(force: true);
            await startServer(addr, port);
          } else if (ch == $q) {
            stdin.echoMode = stdin.lineMode = true;
            await close();
            await sub.cancel();
            exit(0);
          } else if (ch == $h) {
            print(
                'Press "r" to hot reload the Dart VM, and restart the active server.');
            print(
                'Press "R" to restart the server, WITHOUT a hot reload of the VM.');
            print('Press "q" to quit the server.');
            print('Press "h" to display this help information.');
            stdout.writeln();
          }
        });
      } catch (_) {}
    }

    return server;
  }

angel3_production.dart

// TODO add this
  /// Boots a shared server instance. Use this if launching multiple isolates.
  static Future<HttpServer> Function(dynamic, int) startSharedHttpServer() {
    return (address, int port) async {
      final server =
          await HttpServer.bind(address ?? '127.0.0.1', port, shared: true);
      server.defaultResponseHeaders.remove('X-Frame-Options', 'SAMEORIGIN');
      return Future.value(server);
    };
  }

  static Future<HttpServer> Function(dynamic, int) startSharedSecureHttpServer(
      SecurityContext securityContext) {
    return (address, int port) async {
      final server = await HttpServer.bindSecure(
          address ?? '127.0.0.1', port, securityContext,
          shared: true);
      server.defaultResponseHeaders.remove('X-Frame-Options', 'SAMEORIGIN');
      return Future.value(server);
    };
  }

 static void isolateMain(RunnerArgsWithId argsWithId) {
    var args = argsWithId.args;
    hierarchicalLoggingEnabled = false;

    var zone = Zone.current.fork(specification: ZoneSpecification(
      print: (self, parent, zone, msg) {
        args.loggingSendPort.send(LogRecord(Level.INFO, msg, args.loggerName));
      },
    ));

    zone.run(() async {
      var client =
          pub_sub.IsolateClient('client${argsWithId.id}', args.pubSubSendPort);

      var app = Angel(reflector: args.reflector)
        ..container.registerSingleton<pub_sub.Client>(client)
        ..container.registerSingleton(InstanceInfo(id: argsWithId.id));

      app.shutdownHooks.add((_) => client.close());

      await app.configure(args.configureServer);

      app.logger = Logger(args.loggerName)
        ..onRecord.listen((rec) => Runner.handleLogRecord(rec, args.options));

      AngelHttp http;
      late SecurityContext securityContext;
      Uri serverUrl;

      if (args.options.ssl || args.options.http2) {
        securityContext = SecurityContext();
        if (args.options.certificateFile != null) {
          securityContext.useCertificateChain(args.options.certificateFile!,
              password: args.options.certificatePassword);
        }

        if (args.options.keyFile != null) {
          securityContext.usePrivateKey(args.options.keyFile!,
              password: args.options.keyPassword);
        }
      }

      if (args.options.ssl) {
        // TODO change to startSharedSecureHttpServer
        http = AngelHttp.custom(
            app, startSharedSecureHttpServer(securityContext),
            useZone: args.options.useZone);
      } else {
        // TODO change to startSharedHttpServer
        http = AngelHttp.custom(app, startSharedHttpServer(),
            useZone: args.options.useZone);
      }

      Driver driver;

      if (args.options.http2) {
        securityContext.setAlpnProtocols(['h2'], true);
        var http2 = AngelHttp2.custom(app, securityContext, startSharedHttp2,
            useZone: args.options.useZone);
        http2.onHttp1.listen(http.handleRequest);
        driver = http2;
      } else {
        driver = http;
      }

      await driver.startServer(args.options.hostname, args.options.port);
      serverUrl = driver.uri;
      if (args.options.ssl || args.options.http2) {
        serverUrl = serverUrl.replace(scheme: 'https');
      }
      print('Instance #${argsWithId.id} listening at $serverUrl');
    });
  }

@dukefirehawk
Copy link
Collaborator

angel3_production:8.1.0 now can support passing addition parameters to add or remove headers. See example in the updated README.md. For angel3_hot, defaultResponseHeaders method is already accessible. See example in the updated README.md.

@insinfo
Copy link
Author

insinfo commented Nov 13, 2023

@duquefirehawk
Thank you very much for your commitment and dedication to this project

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants