Repository: locustio/locust

Issue:
--html doesnt work in web mode
<!-- 
If you have a general question about how to use Locust, please check Stack Overflow first https://stackoverflow.com/questions/tagged/locust

You can also ask new questions on SO, https://stackoverflow.com/questions/ask just remember to tag your question with "locust". Do not immediately post your issue here after posting to SO, wait for an answer there instead.

Use this form only for reporting actual bugs in Locust. Remember, the developers of Locust are unpaid volunteers, so make sure you have tried everything you can think of before filing a bug :) 

Always make sure you are running an up to date Locust version (pip3 install -U locust)
-->

### Describe the bug
<!-- A clear and concise description of what the bug is -->

There is no any setting description about the html option in doc. Only said it "Store HTML report file". No any value example.
I tried "true", "html_prefix_like_csv" and it doesn't work. Can any one tell me what value should I set and where would be the report saved, and is there any way to customized the report html format?

### Expected behavior
<!-- Tell us what you think should happen -->

### Actual behavior
<!-- Tell us what happens instead. Include screenshots if this an issue with the GUI. -->

### Steps to reproduce
<!-- Please provide a minimal reproducible code example (https://stackoverflow.com/help/minimal-reproducible-example) --> 

### Environment

- OS: MacOS
- Python version: 3.9.0
- Locust version: 2.5.0
- Locust command line that you ran: locust
- Locust file contents (anonymized if necessary): i set html = report.html in locust.config


Discussion:
Also face the same problem at version=2.5.0, just configuration the html = xxxx.html in the locust.conf file can not working, there is no xxx.html output ,but if wen running on the command line add the args '--headless --html=xxxx.html -t 10s ' will see the html been output.
Probably my fault, probably somewhere in main.py. Dont have time to fix it though, maybe you have time yourselves?
Automatic html report seems to work only in headless mode. I suppose it isn't a bug. 
You need add 'headless' to your locust.conf or as a command line argument. 
I think this is a bug, it should work in ”headful” as well. 

Resolving patch:
diff --git a/locust/argument_parser.py b/locust/argument_parser.py
--- a/locust/argument_parser.py
+++ b/locust/argument_parser.py
@@ -397,7 +397,7 @@ def setup_parser_arguments(parser):
     stats_group.add_argument(
         "--html",
         dest="html_file",
-        help="Store HTML report file",
+        help="Store HTML report to file path specified",
         env_var="LOCUST_HTML",
     )
 
diff --git a/locust/main.py b/locust/main.py
--- a/locust/main.py
+++ b/locust/main.py
@@ -457,6 +457,12 @@ def sig_term_handler():
         logger.info("Got SIGTERM signal")
         shutdown()
 
+    def save_html_report():
+        html_report = get_html_report(environment, show_download_link=False)
+        logger.info("writing html report to file: %s", options.html_file)
+        with open(options.html_file, "w", encoding="utf-8") as file:
+            file.write(html_report)
+
     gevent.signal_handler(signal.SIGTERM, sig_term_handler)
 
     try:
@@ -466,11 +472,10 @@ def sig_term_handler():
 
         main_greenlet.join()
         if options.html_file:
-            html_report = get_html_report(environment, show_download_link=False)
-            with open(options.html_file, "w", encoding="utf-8") as file:
-                file.write(html_report)
+            save_html_report()
     except KeyboardInterrupt:
-        pass
+        if options.html_file:
+            save_html_report()
     except Exception:
         raise
     shutdown()
diff --git a/locust/web.py b/locust/web.py
--- a/locust/web.py
+++ b/locust/web.py
@@ -120,7 +120,7 @@ def __init__(
                 self.auth.init_app(self.app)
             else:
                 raise AuthCredentialsError(
-                    "Invalid auth_credentials. It should be a string in the following format: 'user.pass'"
+                    "Invalid auth_credentials. It should be a string in the following format: 'user:pass'"
                 )
         if environment.runner:
             self.update_template_args()
@@ -174,6 +174,8 @@ def stop():
                 self._swarm_greenlet.kill(block=True)
                 self._swarm_greenlet = None
             environment.runner.stop()
+            if self.greenlet is not None:
+                self.greenlet.kill(block=True)
             return jsonify({"success": True, "message": "Test stopped"})
 
         @app.route("/stats/reset")


Test patch: