Skip to content

Commit

Permalink
Added new configuration option 'unix_socket'.
Browse files Browse the repository at this point in the history
This specifies a path to a UNIX socket file to listen on instead of the TCP port.

If specified, SQLPage will accept HTTP connections only on this socket and not on any TCP port. This option is mutually exclusive with the `listen_on` and `port` options.

Useful when running SQLpage behind a proxy server like Nginx, as the overhead for communication using Unix Domain Sockets is less than when using the TCP stack.
  • Loading branch information
vlasky committed May 18, 2024
1 parent c298d7c commit c24d101
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 29 deletions.
1 change: 1 addition & 0 deletions configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Here are the available configuration options and their default values:
| `listen_on` | 0.0.0.0:8080 | Interface and port on which the web server should listen |
| `database_url` | sqlite://sqlpage.db?mode=rwc | Database connection URL, in the form `dbname://user:password@host:port/dbname`. Special characters in user and password should be [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding). |
| `port` | 8080 | Like listen_on, but specifies only the port. |
| `unix_socket` | | Path to a UNIX socket to listen on instead of the TCP port. If specified, SQLPage will accept HTTP connections only on this socket and not on any TCP port. This option is mutually exclusive with `listen_on` and `port`.
| `max_database_pool_connections` | PostgreSQL: 50<BR> MySql: 75<BR> SQLite: 16<BR> MSSQL: 100 | How many simultaneous database connections to open at most |
| `database_connection_idle_timeout_seconds` | SQLite: None<BR> All other: 30 minutes | Automatically close database connections after this period of inactivity |
| `database_connection_max_lifetime_seconds` | SQLite: None<BR> All other: 60 minutes | Always close database connections after this amount of time |
Expand Down
1 change: 1 addition & 0 deletions src/app_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct AppConfig {
#[serde(default, deserialize_with = "deserialize_socket_addr")]
pub listen_on: Option<SocketAddr>,
pub port: Option<u16>,
pub unix_socket: Option<PathBuf>,

/// Number of times to retry connecting to the database after a failure when the server starts
/// up. Retries will happen every 5 seconds. The default is 6 retries, which means the server
Expand Down
31 changes: 18 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,28 @@ async fn start() -> anyhow::Result<()> {
}

async fn log_welcome_message(config: &AppConfig) {
// Don't show 0.0.0.0 as the host, show the actual IP address
let http_addr = config.listen_on().to_string().replace(
"0.0.0.0",
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST)
.to_string()
.as_str(),
);

log::info!(
"Server started successfully.
SQLPage is now running on {}
You can write your website's code in .sql files in {}.",
let address_message = if let Some(unix_socket) = &config.unix_socket {
format!("unix:{}", unix_socket.display())
} else {
// Don't show 0.0.0.0 as the host, show the actual IP address
let http_addr = config.listen_on().to_string().replace(
"0.0.0.0",
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST)
.to_string()
.as_str(),
);
if let Some(domain) = &config.https_domain {
format!("https://{}", domain)
} else {
format!("http://{}", http_addr)
},
}
};

log::info!(
"Server started successfully.
SQLPage is now listening on {}
You can write your website's code in .sql files in {}.",
address_message,
config.web_root.display()
);
}
Expand Down
60 changes: 44 additions & 16 deletions src/webserver/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,22 +549,28 @@ pub async fn run_server(config: &AppConfig, state: AppState) -> anyhow::Result<(
return Ok(());
}
let mut server = HttpServer::new(factory);
if let Some(domain) = &config.https_domain {
let mut listen_on_https = listen_on;
listen_on_https.set_port(443);
log::info!("Will start HTTPS server on {listen_on_https}");
let config = make_auto_rustls_config(domain, config);
server = server
.bind_rustls_0_22(listen_on_https, config)
.map_err(|e| bind_error(e, listen_on_https))?;
} else if listen_on.port() == 443 {
bail!("Please specify a value for https_domain in the configuration file. This is required when using HTTPS (port 443)");
}
if listen_on.port() != 443 {
log::info!("Will start HTTP server on {listen_on}");
server = server
.bind(listen_on)
.map_err(|e| bind_error(e, listen_on))?;
if let Some(unix_socket) = &config.unix_socket {
log::info!("Will start HTTP server on UNIX socket: {:?}", unix_socket);
server = server.bind_uds(unix_socket)
.map_err(|e| bind_uds_error(e, unix_socket))?;
} else {
if let Some(domain) = &config.https_domain {
let mut listen_on_https = listen_on;
listen_on_https.set_port(443);
log::info!("Will start HTTPS server on {listen_on_https}");
let config = make_auto_rustls_config(domain, config);
server = server
.bind_rustls_0_22(listen_on_https, config)
.map_err(|e| bind_error(e, listen_on_https))?;
} else if listen_on.port() == 443 {
bail!("Please specify a value for https_domain in the configuration file. This is required when using HTTPS (port 443)");
}
if listen_on.port() != 443 {
log::info!("Will start HTTP server on {listen_on}");
server = server
.bind(listen_on)
.map_err(|e| bind_error(e, listen_on))?;
}
}
server
.run()
Expand Down Expand Up @@ -600,3 +606,25 @@ fn bind_error(e: std::io::Error, listen_on: std::net::SocketAddr) -> anyhow::Err
};
anyhow::anyhow!(e).context(ctx)
}

fn bind_uds_error(e: std::io::Error, unix_socket: &PathBuf) -> anyhow::Error {
let ctx = match e.kind() {
std::io::ErrorKind::AddrInUse => format!(
"Another program is already using the UNIX socket {:?}. \
You can either stop that program or change the socket path in the configuration file.",
unix_socket,
),
std::io::ErrorKind::PermissionDenied => format!(
"You do not have permission to bind to the UNIX socket {:?}. \
You can change the socket path in the configuration file or check the permissions.",
unix_socket,
),
std::io::ErrorKind::AddrNotAvailable => format!(
"The UNIX socket path {:?} is not available. \
You can change the socket path in the configuration file.",
unix_socket,
),
_ => format!("Unable to bind to UNIX socket {:?}", unix_socket),
};
anyhow::anyhow!(e).context(ctx)
}

0 comments on commit c24d101

Please sign in to comment.