From 2719f62feb97c321d87b62145fc9e92f76d3025e Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 11 Jan 2021 22:29:39 +0100
Subject: [PATCH 1/2] net/SocketError: relicense to BSD-2

---
 src/net/SocketError.cxx | 36 +++++++++++++++++++++++-------------
 src/net/SocketError.hxx | 40 +++++++++++++++++++++++++---------------
 2 files changed, 48 insertions(+), 28 deletions(-)

diff --git a/src/net/SocketError.cxx b/src/net/SocketError.cxx
index 342900efd..2412549dd 100644
--- a/src/net/SocketError.cxx
+++ b/src/net/SocketError.cxx
@@ -1,20 +1,30 @@
 /*
- * Copyright 2003-2021 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2015-2021 Max Kellermann <max.kellermann@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "SocketError.hxx"
diff --git a/src/net/SocketError.hxx b/src/net/SocketError.hxx
index 887542828..8e8db1ddf 100644
--- a/src/net/SocketError.hxx
+++ b/src/net/SocketError.hxx
@@ -1,24 +1,34 @@
 /*
- * Copyright 2003-2021 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2015-2021 Max Kellermann <max.kellermann@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef MPD_SOCKET_ERROR_HXX
-#define MPD_SOCKET_ERROR_HXX
+#ifndef SOCKET_ERROR_HXX
+#define SOCKET_ERROR_HXX
 
 #include "util/Compiler.h"
 #include "system/Error.hxx"

From 9e2d09dabc5abc8cc98c339c6fd34c769125096c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 21 Jan 2021 22:04:14 +0100
Subject: [PATCH 2/2] net/SocketError: add syscall specific check functions

Fixes Windows compatibility.
---
 NEWS                                     |  2 +
 src/event/BufferedSocket.cxx             |  2 +-
 src/event/FullyBufferedSocket.cxx        |  2 +-
 src/net/SocketError.hxx                  | 73 ++++++++++++++++++++++--
 src/output/plugins/httpd/HttpdClient.cxx |  6 +-
 5 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index c67ff4e81..8d0c6455a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
 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 d40d2c013..5fa31afd1 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 5f9dc544d..56390b679 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;
 
 		IdleMonitor::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 16593001c..c5e2d819e 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)) {