diff --git a/NEWS b/NEWS
index 6f5e676e5..36ddce6fe 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ ver 0.23 (not yet released)
   - new command "getvol"
 
 ver 0.22.5 (not yet released)
+* output
+  - httpd: error handling on Windows improved
 
 ver 0.22.4 (2021/01/21)
 * protocol
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
index 268f055f0..53877da5f 100644
--- a/src/event/BufferedSocket.cxx
+++ b/src/event/BufferedSocket.cxx
@@ -36,7 +36,7 @@ BufferedSocket::DirectRead(void *data, size_t length) noexcept
 	}
 
 	const auto code = GetSocketError();
-	if (IsSocketErrorAgain(code))
+	if (IsSocketErrorReceiveWouldBlock(code))
 		return 0;
 
 	if (IsSocketErrorClosed(code))
diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx
index 93e2efb07..b8bfdf5f9 100644
--- a/src/event/FullyBufferedSocket.cxx
+++ b/src/event/FullyBufferedSocket.cxx
@@ -31,7 +31,7 @@ FullyBufferedSocket::DirectWrite(const void *data, size_t length) noexcept
 	const auto nbytes = GetSocket().Write((const char *)data, length);
 	if (gcc_unlikely(nbytes < 0)) {
 		const auto code = GetSocketError();
-		if (IsSocketErrorAgain(code))
+		if (IsSocketErrorSendWouldBlock(code))
 			return 0;
 
 		idle_event.Cancel();
diff --git a/src/net/SocketError.hxx b/src/net/SocketError.hxx
index 8e8db1ddf..44989c52f 100644
--- a/src/net/SocketError.hxx
+++ b/src/net/SocketError.hxx
@@ -52,14 +52,79 @@ GetSocketError() noexcept
 #endif
 }
 
-gcc_const
-static inline bool
-IsSocketErrorAgain(socket_error_t code) noexcept
+constexpr bool
+IsSocketErrorInProgress(socket_error_t code) noexcept
 {
 #ifdef _WIN32
 	return code == WSAEINPROGRESS;
 #else
-	return code == EAGAIN;
+	return code == EINPROGRESS;
+#endif
+}
+
+constexpr bool
+IsSocketErrorWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+	return code == WSAEWOULDBLOCK;
+#else
+	return code == EWOULDBLOCK;
+#endif
+}
+
+constexpr bool
+IsSocketErrorConnectWouldBlock(socket_error_t code) noexcept
+{
+#if defined(_WIN32) || defined(__linux__)
+	/* on Windows, WSAEINPROGRESS is for blocking sockets and
+	   WSAEWOULDBLOCK for non-blocking sockets */
+	/* on Linux, EAGAIN==EWOULDBLOCK is for local sockets and
+	   EINPROGRESS is for all other sockets */
+	return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+	/* on all other operating systems, there's just EINPROGRESS */
+	return IsSocketErrorInProgress(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorSendWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+	/* on Windows, WSAEINPROGRESS is for blocking sockets and
+	   WSAEWOULDBLOCK for non-blocking sockets */
+	return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+	/* on all other operating systems, there's just EAGAIN==EWOULDBLOCK */
+	return IsSocketErrorWouldBlock(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorReceiveWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+	/* on Windows, WSAEINPROGRESS is for blocking sockets and
+	   WSAEWOULDBLOCK for non-blocking sockets */
+	return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+	/* on all other operating systems, there's just
+	   EAGAIN==EWOULDBLOCK */
+	return IsSocketErrorWouldBlock(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorAcceptWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+	/* on Windows, WSAEINPROGRESS is for blocking sockets and
+	   WSAEWOULDBLOCK for non-blocking sockets */
+	return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+	/* on all other operating systems, there's just
+	   EAGAIN==EWOULDBLOCK */
+	return IsSocketErrorWouldBlock(code);
 #endif
 }
 
diff --git a/src/output/plugins/httpd/HttpdClient.cxx b/src/output/plugins/httpd/HttpdClient.cxx
index 6345f8f9f..e16389db0 100644
--- a/src/output/plugins/httpd/HttpdClient.cxx
+++ b/src/output/plugins/httpd/HttpdClient.cxx
@@ -278,7 +278,7 @@ HttpdClient::TryWrite() noexcept
 						      metadata_current_position);
 			if (nbytes < 0) {
 				auto e = GetSocketError();
-				if (IsSocketErrorAgain(e))
+				if (IsSocketErrorSendWouldBlock(e))
 					return true;
 
 				if (!IsSocketErrorClosed(e)) {
@@ -305,7 +305,7 @@ HttpdClient::TryWrite() noexcept
 			ssize_t nbytes = GetSocket().Write(&empty_data, 1);
 			if (nbytes < 0) {
 				auto e = GetSocketError();
-				if (IsSocketErrorAgain(e))
+				if (IsSocketErrorSendWouldBlock(e))
 					return true;
 
 				if (!IsSocketErrorClosed(e)) {
@@ -328,7 +328,7 @@ HttpdClient::TryWrite() noexcept
 				      bytes_to_write);
 		if (nbytes < 0) {
 			auto e = GetSocketError();
-			if (IsSocketErrorAgain(e))
+			if (IsSocketErrorSendWouldBlock(e))
 				return true;
 
 			if (!IsSocketErrorClosed(e)) {