Skip to content

Commit

Permalink
Set SO_REUSEADDR only non-Windows platforms (#3473)
Browse files Browse the repository at this point in the history
* Fix: Per discussion in #2958, set `SO_REUSEADDR` only non-Windows platforms.

Add: Tests confirming that only a single webserver instance may
bind to a given address.

* Clean: Lint.

* Clean: another guess at making the formatter happy.

* Clean: More cargo fmt fun. (Running cargo fmt locally doesn't help.)

---------

Co-authored-by: Bryan A. Jones <bjones1@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
  • Loading branch information
3 people authored Oct 1, 2024
1 parent d9d2282 commit 1c4e265
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 7 deletions.
1 change: 1 addition & 0 deletions actix-web/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Minimum supported Rust version (MSRV) is now 1.75.
- On Windows platforms, produce an error when invoking `HttpServer::bind` on a socket that's already in use. See [issue 2958](https://github.com/actix/actix-web/issues/2958).

## 4.9.0

Expand Down
5 changes: 4 additions & 1 deletion actix-web/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,10 @@ fn create_tcp_listener(addr: net::SocketAddr, backlog: u32) -> io::Result<net::T
use socket2::{Domain, Protocol, Socket, Type};
let domain = Domain::for_address(addr);
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
socket.set_reuse_address(true)?;
#[cfg(not(windows))]
{
socket.set_reuse_address(true)?;
}
socket.bind(&addr.into())?;
// clamp backlog to max u32 that fits in i32 range
let backlog = cmp::min(backlog, i32::MAX as u32) as i32;
Expand Down
30 changes: 24 additions & 6 deletions actix-web/tests/test_httpserver.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;

#[cfg(any(unix, feature = "openssl"))]
use {
actix_web::{web, App, HttpResponse, HttpServer},
std::{sync::mpsc, thread, time::Duration},
};
use std::{sync::mpsc, thread, time::Duration};

use actix_web::{web, App, HttpResponse, HttpServer};

#[cfg(unix)]
#[actix_rt::test]
async fn test_start() {
let addr = actix_test::unused_addr();
Expand Down Expand Up @@ -53,6 +50,27 @@ async fn test_start() {
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success());

// Attempt to start a second server using the same address.
let result = HttpServer::new(|| {
App::new().service(
web::resource("/").route(web::to(|| async { HttpResponse::Ok().body("test") })),
)
})
.workers(1)
.backlog(1)
.max_connections(10)
.max_connection_rate(10)
.keep_alive(Duration::from_secs(10))
.client_request_timeout(Duration::from_secs(5))
.client_disconnect_timeout(Duration::ZERO)
.server_hostname("localhost")
.system_exit()
.disable_signals()
.bind(format!("{}", addr));

// This should fail: the address is in use.
assert!(result.is_err());

srv.stop(false).await;
}

Expand Down

0 comments on commit 1c4e265

Please sign in to comment.