Browse Source

obs-webrtc: Improve WHIP compliance

Location header is now required. Support relative and absolute URLs
aggresss 2 years ago
parent
commit
c81f531edb
2 changed files with 59 additions and 26 deletions
  1. 53 23
      plugins/obs-webrtc/whip-output.cpp
  2. 6 3
      plugins/obs-webrtc/whip-utils.h

+ 53 - 23
plugins/obs-webrtc/whip-output.cpp

@@ -290,7 +290,7 @@ bool WHIPOutput::Connect()
 	}
 	}
 
 
 	std::string read_buffer;
 	std::string read_buffer;
-	std::string location_header;
+	std::vector<std::string> location_headers;
 
 
 	char offer_sdp[4096] = {0};
 	char offer_sdp[4096] = {0};
 	rtcGetLocalDescription(peer_connection, offer_sdp, sizeof(offer_sdp));
 	rtcGetLocalDescription(peer_connection, offer_sdp, sizeof(offer_sdp));
@@ -307,7 +307,7 @@ bool WHIPOutput::Connect()
 	curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)&read_buffer);
 	curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)&read_buffer);
 	curl_easy_setopt(c, CURLOPT_HEADERFUNCTION,
 	curl_easy_setopt(c, CURLOPT_HEADERFUNCTION,
 			 curl_header_location_function);
 			 curl_header_location_function);
-	curl_easy_setopt(c, CURLOPT_HEADERDATA, (void *)&location_header);
+	curl_easy_setopt(c, CURLOPT_HEADERDATA, (void *)&location_headers);
 	curl_easy_setopt(c, CURLOPT_HTTPHEADER, headers);
 	curl_easy_setopt(c, CURLOPT_HTTPHEADER, headers);
 	curl_easy_setopt(c, CURLOPT_URL, endpoint_url.c_str());
 	curl_easy_setopt(c, CURLOPT_URL, endpoint_url.c_str());
 	curl_easy_setopt(c, CURLOPT_POST, 1L);
 	curl_easy_setopt(c, CURLOPT_POST, 1L);
@@ -323,7 +323,7 @@ bool WHIPOutput::Connect()
 
 
 	CURLcode res = curl_easy_perform(c);
 	CURLcode res = curl_easy_perform(c);
 	if (res != CURLE_OK) {
 	if (res != CURLE_OK) {
-		do_log(LOG_WARNING,
+		do_log(LOG_ERROR,
 		       "Connect failed: CURL returned result not CURLE_OK");
 		       "Connect failed: CURL returned result not CURLE_OK");
 		cleanup();
 		cleanup();
 		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
 		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
@@ -333,7 +333,7 @@ bool WHIPOutput::Connect()
 	long response_code;
 	long response_code;
 	curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
 	curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
 	if (response_code != 201) {
 	if (response_code != 201) {
-		do_log(LOG_WARNING,
+		do_log(LOG_ERROR,
 		       "Connect failed: HTTP endpoint returned response code %ld",
 		       "Connect failed: HTTP endpoint returned response code %ld",
 		       response_code);
 		       response_code);
 		cleanup();
 		cleanup();
@@ -342,35 +342,65 @@ bool WHIPOutput::Connect()
 	}
 	}
 
 
 	if (read_buffer.empty()) {
 	if (read_buffer.empty()) {
-		do_log(LOG_WARNING,
+		do_log(LOG_ERROR,
 		       "Connect failed: No data returned from HTTP endpoint request");
 		       "Connect failed: No data returned from HTTP endpoint request");
 		cleanup();
 		cleanup();
 		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
 		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
 		return false;
 		return false;
 	}
 	}
 
 
-	if (location_header.empty()) {
-		do_log(LOG_WARNING,
+	long redirect_count = 0;
+	curl_easy_getinfo(c, CURLINFO_REDIRECT_COUNT, &redirect_count);
+
+	if (location_headers.size() < static_cast<size_t>(redirect_count) + 1) {
+		do_log(LOG_ERROR,
 		       "WHIP server did not provide a resource URL via the Location header");
 		       "WHIP server did not provide a resource URL via the Location header");
-	} else {
-		CURLU *h = curl_url();
-		curl_url_set(h, CURLUPART_URL, endpoint_url.c_str(), 0);
-		curl_url_set(h, CURLUPART_URL, location_header.c_str(), 0);
-		char *url = nullptr;
-		CURLUcode rc = curl_url_get(h, CURLUPART_URL, &url,
-					    CURLU_NO_DEFAULT_PORT);
-		if (!rc) {
-			resource_url = url;
-			curl_free(url);
-			do_log(LOG_DEBUG, "WHIP Resource URL is: %s",
-			       resource_url.c_str());
-		} else {
-			do_log(LOG_WARNING,
-			       "Unable to process resource URL response");
+		cleanup();
+		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
+		return false;
+	}
+
+	CURLU *url_builder = curl_url();
+	auto last_location_header = location_headers.back();
+
+	// If Location header doesn't start with `http` it is a relative URL.
+	// Construct a absolute URL using the host of the effective URL
+	if (last_location_header.find("http") != 0) {
+		char *effective_url = nullptr;
+		curl_easy_getinfo(c, CURLINFO_EFFECTIVE_URL, &effective_url);
+		if (effective_url == nullptr) {
+			do_log(LOG_ERROR, "Failed to build Resource URL");
+			cleanup();
+			obs_output_signal_stop(output,
+					       OBS_OUTPUT_CONNECT_FAILED);
+			return false;
 		}
 		}
-		curl_url_cleanup(h);
+
+		curl_url_set(url_builder, CURLUPART_URL, effective_url, 0);
+		curl_url_set(url_builder, CURLUPART_PATH,
+			     last_location_header.c_str(), 0);
+		curl_url_set(url_builder, CURLUPART_QUERY, "", 0);
+	} else {
+		curl_url_set(url_builder, CURLUPART_URL,
+			     last_location_header.c_str(), 0);
 	}
 	}
 
 
+	char *url = nullptr;
+	CURLUcode rc = curl_url_get(url_builder, CURLUPART_URL, &url,
+				    CURLU_NO_DEFAULT_PORT);
+	if (rc) {
+		do_log(LOG_ERROR,
+		       "WHIP server provided a invalid resource URL via the Location header");
+		cleanup();
+		obs_output_signal_stop(output, OBS_OUTPUT_CONNECT_FAILED);
+		return false;
+	}
+
+	resource_url = url;
+	curl_free(url);
+	do_log(LOG_DEBUG, "WHIP Resource URL is: %s", resource_url.c_str());
+	curl_url_cleanup(url_builder);
+
 #ifdef DEBUG_SDP
 #ifdef DEBUG_SDP
 	do_log(LOG_DEBUG, "Answer SDP:\n%s", read_buffer.c_str());
 	do_log(LOG_DEBUG, "Answer SDP:\n%s", read_buffer.c_str());
 #endif
 #endif

+ 6 - 3
plugins/obs-webrtc/whip-utils.h

@@ -45,7 +45,7 @@ static size_t curl_writefunction(char *data, size_t size, size_t nmemb,
 static size_t curl_header_location_function(char *data, size_t size,
 static size_t curl_header_location_function(char *data, size_t size,
 					    size_t nmemb, void *priv_data)
 					    size_t nmemb, void *priv_data)
 {
 {
-	auto header_buffer = static_cast<std::string *>(priv_data);
+	auto header_buffer = static_cast<std::vector<std::string> *>(priv_data);
 
 
 	size_t real_size = size * nmemb;
 	size_t real_size = size * nmemb;
 
 
@@ -54,8 +54,11 @@ static size_t curl_header_location_function(char *data, size_t size,
 
 
 	if (!astrcmpi_n(data, "location: ", LOCATION_HEADER_LENGTH)) {
 	if (!astrcmpi_n(data, "location: ", LOCATION_HEADER_LENGTH)) {
 		char *val = data + LOCATION_HEADER_LENGTH;
 		char *val = data + LOCATION_HEADER_LENGTH;
-		header_buffer->append(val, real_size - LOCATION_HEADER_LENGTH);
-		*header_buffer = trim_string(*header_buffer);
+		auto header_temp =
+			std::string(val, real_size - LOCATION_HEADER_LENGTH);
+
+		header_temp = trim_string(header_temp);
+		header_buffer->push_back(header_temp);
 	}
 	}
 
 
 	return real_size;
 	return real_size;