SimtinHTTP is a simple and tiny HTTP server written in Rust.
It focuses on Windows, but being pure Rust, it should compile easily on Linux and other operating systems as well
(with some limitations, see Rust platform support).
Functionally speaking, you can view it very roughly as python3 -m http.server
without Python — and within
less than 1 MiB (or 400 KiB with UPX compression).
It started as a project to serve files from a local folder over Tor, as a replacement to Onionshare, which is great but is too heavy, does a bit too many things to be really simple to set up, and for some reason won’t provide a portable setup (yes, that ticket is from 2017 🤷). SimtinHTTP is also appropriate to serve files on a home network (for instance, I use it to read videos on my HY300Pro projector), and, provided that you know what you are doing, it can also be used to serve files publicly over the internet, with some additional configuration efforts (in a nutshell, configuring port forwarding or your firewall if applicable).
As a result, its focus is to be:
{"serverBindings": [{"basePath": "folder to serve"}]}
cargo build --release
Since the primary purpose of SimtinHTTP is to serve files over Tor, it should also not require JavaScript on the client side, as Tor users generally prefer to keep JavaScript off.
1. Create a file named config.json
with this minimal content:
{ "serverBindings": [ { "basePath": "folder to serve" } ] }
2. Place config.json
in the same folder as simtin-http.exe
3. Run simtin-http.exe
4. Open your browser and go to http://127.0.0.1:8080/
Create a file named config.json
and following this structure:
{ "serverBindings": [ { "bindAddress": "127.0.0.1:8080", "basePath": "C:/root/folder1/" }, { "bindAddress": "127.0.0.1:8081", "basePath": "C:/root/folder2/" } ], "ipWhitelist" [], "cache": { "size": 1000, "ttl": 60 }, "debug": false, "directoryIndex": "just a placeholder", "enableDirectoryListing": true, "log": { "level": "info", "timestamp": "ms", "output": "stdout" }, "shortSizeUnits": false, "showLastModified": true, "showServerSignature": 2 }
The parameters are detailed in the table below:
Parameter | Type | Default | Description |
---|---|---|---|
serverBindings | Array | no default (mandatory) |
Array of server bindings. Each binding is an object with the properties basePath and bindAddress. See below for details about each of them. Example: [{"basePath": "C:/root/folder1/", "bindAddress": "127.0.0.1:8080"}]
|
ipWhitelist | Array | [] |
Array of whitelisted IPs. If empty or missing, all IPs are allowed. Otherwise, only the listed IPs are allowed,
and connections from other IPs are dropped without sending a response. Example: ["127.0.0.1", "127.0.0.2"]
|
serverBindings[].basePath | String | no default (mandatory) |
Folder to serve. Example: C:/root/folder/ EVERYTHING in that folder (and, obviously, subfolders) will be made accessible via the server, so choose wisely. And yes, even “hidden” files, files with a name that starts with a dot, “.htaccess”, etc. Every. Thing. Relative paths should work, but you’ll probably want to use an absolute path in order to avoid bad surprises. |
serverBindings[].bindAddress | String | 127.0.0.1:8080 |
Address and port to listen on (format: IP:Port ). The default will only serve requests from localhost, which is enough for a Tor onion.Set it to 0.0.0.0:8080 if you want to serve from all interfaces, or something like 192.168.1.1:8080 (whatever your LAN IP is)
to serve on your home network. Don’t forget to customize the port as needed.While it is not mandatory, if you want to serve multiple base folders you will have to configure each of them with a unique bindAddress, so the default value will only take you so far. |
cache.size | usize | 1000 | Maximum number of entries in the cache. Note that the cache will only cache (fully HTML-rendered) directory listings. So, while not offering direct control over the size in bytes, each entry is expected to be reasonably small, and this will depend on your typical directory contents. |
cache.ttl | 64-bit integer | 60 | TTL (time to live) for cache entries, in seconds. If your directory contents barely ever change, you may want to increase it. If they change often, you may want to decrease it, or even get a build without the cache feature. |
debug | boolean | false |
Set to true to enable some debugging features.You’ll probably want to keep it false . In its current state, true won’t cause a big mess either,
but you never know how it will change.
|
directoryIndex | String[] | None |
An ordered array of names of files to display when a directory is requested. SimtinHTTP will pick the first one that exists.
If empty or not set (or invalid), or if none of the listed index files exists, SimtinHTTP will display a list of the files in the directory
if enableDirectoryListing is set to true , or a not found error if false .Example: ["index.html", "index.htm"]
|
enableDirectoryListing | boolean | false |
If set to true , SimtinHTTP will display a list of the files in the directory when a directory is requested.The default it false , for the sake of consistency with the rest of the other default settings, which were chosen
with a focus on security and minimizing information leaks.This is, however, a breaking change compared to the previous behavior, as well as a bit of a departure from the original design. It also adds a little extra step to the first-time setup. |
log.level | String | info |
Verbosity level of the logger. Corresponds to the log::Level enum from the log crate. Possible values, from most to least verbose: trace , debug , info , warn , error , off .
trace is currently not used.
|
log.timestamp | String | ms |
Formatting precision of timestamps. Possible values: s , m (ms, milliseconds), µ (µs, microseconds), n (ns, nanoseconds).
|
log.ouput | String | stdout |
Where to output the logs. Possible values: stdout , stderr , file some/file/name.txt (don’t forget the “file ” prefix).
|
shortSizeUnits | boolean | false |
If set to true , units will be abbreviated even more, like K instead of kiB ,
B instead of bytes , etc.
|
showLastModified | boolean | false |
Whether to show the last modified date of your files. You’ll probably want to set it to true because it’s quite nicer this way.
But as security and minimizing information leaks is a priority for the project, it’s set to false by default.
|
showServerSignature | 8-bit integer | 0 |
Whether to show the server signature: 0 shows nothing, 1 shows the name, 2 shows the name and version. It’s typically a good practice to at least hide the version. However, since we’re not that mainstream (yet?), 2 should be safe enough. Again, it’s set to 0 by default due to our security focus, but setting it to 1 would be kind to spread the word 👀 Note that 0 will attempt to make the server less recognizable, by not serving the user manual, nor the logo/favicon or the default styles. Which will have the side effect of making the directory listing look a bit ugly. Also note that this setting will probably evolve in the future, but 0 and 1 should remain roughly the same. |
Place the config.json
file in the same directory as the simtin-http.exe
file, and run the server with:
simtin-http.exe
You should then be able to access the server at http://127.0.0.1:8080/ (obviously, change the port as needed,
and the path to something that exists).
If you set bindAddress
to 0.0.0.0:8080
and you’re not behind some kind of firewall or router, you might well be already able
to access the server at http://[your-external-ip]:8080/
from anywhere. Which you might want to ask yourself if this is what you really want.
As of version 0.7.2, you can also name the configuration file as you want, and pass
its name as an argument: simtin-http.exe "my/cfg.file"
This section is written with Windows in mind. However, the mentioned Tor Expert Bundle is available for Linux and macOS, and the instructions for those platforms should be roughly the same.
Start by downloading the latest version of Tor from https://www.torproject.org/download/tor/. Note that you need Tor (the “Tor Expert Bundle”), not the usual “Tor Browser”. But you should also get Tor Browser as well, if you don’t already have it, in order to later test your setup.
From this bundle, extract tor.exe
, geoip
and geoip6
. Place them wherever you want, but I recommend placing them
all together in a dedicated folder. Let’s call it tordir
.
In tordir
, create a file named torrc
and following this structure:
DataDirectory [tordir]\data DisableNetwork 0 GeoIPFile [tordir]\geoip GeoIPv6File [tordir]\geoip6 Log notice stdout ExitRelay 0 HiddenServiceDir [tordir]\hsdir HiddenServicePort 80 127.0.0.1:8080 SocksPolicy reject * SocksPort 0
Obviously, replace [tordir]
with the actual, full path to where you placed geoip
and geoip6
.
The data
and hsdir
folders may need to be created manually. You may also choose to place them
elsewhere, or name them something else.
You can find what appears to be a fairly complete list of parameters at https://manpages.debian.org/testing/tor/torrc.5.en.html. Briefly, notably because finding documentation isn’t as easy as I wished it was:
DataDirectory
is the directory where Tor stores its working data.DisableNetwork
is to disable Tor’s network access conveniently. 0 is the default and means enabled, so you can just omit it.
But it’s convenient to have it there, as you can then easily set it to 1 to disable.
GeoIPFile
and GeoIPv6File
are GeoIP and GeoIPv6 databases. They are not strictly required, and the above-mentioned manual indeed
says they are “for use with by-country statistics”, but if you don’t configure them, you’ll get a couple of annoying warnings.
Log
is the logging level and its output place. In the example above, we log notices and above to stdout. For instance,
to log warnings and above to a file, you’d use Log warn file [tordir]\log.txt
.
ExitRelay 0
is to make sure that your Tor instance isn’t used as an exit relay. This setting is a bit tricky, as its default of auto
will have a different behavior (either enabled or disabled) depending on how other parameters are set. So it’s best to explicitly set it to 0
,
to avoid any bad surprises, unless you really, really know
what
you’re
doing.
HiddenServiceDir
is where your hidden service (“Onion”) configuration is going to be stored. More specifically, it contains a
hostname
file, with the name of your hidden service (“[random stuff].onion”), which is automatically generated the first time
you run it, the related public and private keys, and a subfolder named authorized_clients
, for access control,
which is beyond the scope of this manual.
If you want to preserve your hidden service address, notably when moving it to another computer, you should keep a backup of this folder.
SocksPolicy
and SocksPort
are to disable using your Tor instance as a SOCKS proxy.
Obviously, you can choose instead to enable it, but this is mostly redundant if you also have Tor Browser on the side, as you probably do
(and probably should, if only for testing your setup).SocksPolicy accept 127.0.0.1, reject *
and SocksPort [random port number]
would make sense.
Just make sure you pick a port that is not already in use.
See also https://community.torproject.org/onion-services/setup/ for some additional information on configuring a Tor Onion service.
Assuming you did place torrc
in the same folder as tor.exe
, you can start tor with:
tor.exe -f torrc
After a short moment, you should be able to access your hidden service, in Tor Browser, at http://[random stuff].onion
,
where [random stuff].onion
is the name found in the hostname
file that was automatically created in HiddenServiceDir
.
Note that tor seems to not be very happy when you restart it “too often”, so you’ll want to double-check your config file before running it the first time,
as well as before restarting it to update the config file. Many times, my onion would become unreachable for a long time after a restart.
A quick fix is to delete the HiddenServiceDir in order to generate a brand new onion address, but obviously it can be a bit of a problem
if you don’t want the address to change.
There are ways to configure Tor to accept commands such as reloading the config on the fly (without restarting), but that’s beyond the scope of this manual.
Long story short, look into ControlPort
and related settings.
This section is under construction / a very brief draft, but it can only do so much, as this is something that depends on your router, your local network setup, and you ability to configure those.
A tool that worked for me is Portmapper. It uses Java, so it’s annoying because it uses Java, but it’s also convenient because it should work no matter your OS. The most important trick about it is that it includes 4 different UPnP libraries (actually I think there are just 3 plus a fake one), so if the default one doesn’t work for you, you should definitely try the others until one works: for me, the good one was the third.
Todo: add some pointers. But generally speaking, that will be in your router settings.
enableDirectoryListing
option with a default value of false
SimtinHTTP uses the following libraries:
Library | Description | License |
---|---|---|
bytes | Types and traits for working with bytes | MIT |
chrono | Date and time library for Rust | MIT or Apache-2.0 |
env_logger | A logging implementation for `log` which is configured via an environment variable. | MIT or Apache-2.0 |
fifo-cache | A minimalist FIFO cache with TTL (Time To Live) support | MIT |
futures | An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. | MIT or Apache-2.0 |
futures-util | Common utilities and extension traits for the futures-rs library. | MIT or Apache-2.0 |
http-body-util | Combinators and adapters for HTTP request or response bodies. | MIT |
hyper | A protective and efficient HTTP library for all | MIT |
hyper-util | A collection of utilities to do common things with hyper | MIT |
Iconoir | An open-source library with 1500+ unique SVG icons, designed on a 24x24 pixels grid | MIT |
jiff | A date-time library that encourages you to jump into the pit of success. This library is heavily inspired by the Temporal project. | Unlicense or MIT |
log | A lightweight logging facade for Rust | MIT or Apache-2.0 |
serde | A generic serialization/deserialization framework | MIT or Apache-2.0 |
serde_json | A JSON serialization file format | MIT or Apache-2.0 |
tokio | An event-driven, non-blocking I/O platform for writing asynchronous I/O backed applications | MIT |
tokio-util | Additional utilities for working with Tokio | MIT |
urlencoding | A Rust library for doing URL percentage encoding. | MIT |
And, only for building:
Library | Description | License |
---|---|---|
list-features | Extracts the list of enabled feature flags during compilation. | MIT |
* SimtinHTTP isn’t a statin, but it is a-static 😏