ソースを参照

Add API function for 30x redirect

bel2125 7 年 前
コミット
9397b14e2e

+ 2 - 3
docs/api/mg_send_http_error.md

@@ -14,7 +14,7 @@
 ### Return Value
 
 | Type | Description |
-| :--- | :--- |
+|`int`| An integer indicating success (>=0) or failure (<0) |
 
 
 ### Description
@@ -28,6 +28,5 @@ A body of the error message, to explain the error in more detail, can be specifi
 ### See Also
 
 * [`mg_send_http_ok();`](mg_send_http_ok.md)
-* [`mg_printf();`](mg_printf.md)
-* [`mg_write();`](mg_write.md)
+* [`mg_send_http_redirect();`](mg_send_http_redirect.md)
 

+ 4 - 3
docs/api/mg_send_http_ok.md

@@ -1,6 +1,6 @@
 # Civetweb API Reference
 
-### `mg_send_http_ok( conn, mime_type, content_len, ... );`
+### `mg_send_http_ok( conn, mime_type, content_len );`
 
 ### Parameters
 
@@ -13,7 +13,7 @@
 ### Return Value
 
 | Type | Description |
-| :--- | :--- |
+|`int`| An integer indicating success (>=0) or failure (<0) |
 
 
 ### Description
@@ -26,7 +26,8 @@ The `content_len` specifies the size of the response body in bytes. If the size
 
 ### See Also
 
-* [`mg_send_http_error();`](mg_send_http_ok.md)
+* [`mg_send_http_error();`](mg_send_http_error.md)
+* [`mg_send_http_redirect();`](mg_send_http_redirect.md)
 * [`mg_write();`](mg_write.md)
 * [`mg_send_chunk();`](mg_send_chunk.md)
 

+ 41 - 0
docs/api/mg_send_http_redirect.md

@@ -0,0 +1,41 @@
+# Civetweb API Reference
+
+### `mg_send_http_redirect( conn, target_url, redirect_code );`
+
+### Parameters
+
+| Parameter | Type | Description |
+| :--- | :--- | :--- |
+|**`conn`**|`struct mg_connection *`|The connection over which the data must be sent|
+|**`target_url`**|`const char *`|The new target location|
+|**`redirect_code`**|`int`|HTTP redirect response code|
+
+### Return Value
+
+| Type | Description |
+|`int`| An integer indicating success (>=0) or failure (<0) |
+
+
+### Description
+
+The function `mg_send_http_redirect()` can be used to send a "HTTP 30x ..." redirect response.
+
+The new location sent to the client (browser) is specified by `target_url`.  The location could be a relative or an absolute URL.
+
+The parameter `redirect_code` defines, what kind of redirect is sent:
+
+|`redirect_code`|type|HTTP method|
+|301|permanent redirect||
+|302|temporary redirect||
+|303|temporary redirect|always use GET|
+|307|temporary redirect|use same method|
+|308|permanent redirect|use same method|
+
+Status codes 301 and 302 were defined in HTTP/1.0, status codes 303, 307 and 308 were defined in HTTP/1.1.  For HTTP/1.1 redirects, it is explicitly defined if a redirected POST request should remain a POST request (307, 308) or should be a GET request (303) to the new target location.  Using 302 is not recommended for other requests than GET, since some browsers used to implement it as 303, others as 307.
+
+
+### See Also
+
+* [`mg_send_http_error();`](mg_send_http_error.md)
+* [`mg_send_http_ok();`](mg_send_http_ok.md)
+

+ 23 - 6
include/civetweb.h

@@ -880,14 +880,15 @@ CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
 
 
 /* Send HTTP error reply. */
-CIVETWEB_API void mg_send_http_error(struct mg_connection *conn,
-                                     int status_code,
-                                     PRINTF_FORMAT_STRING(const char *fmt),
-                                     ...) PRINTF_ARGS(3, 4);
+CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
+                                    int status_code,
+                                    PRINTF_FORMAT_STRING(const char *fmt),
+                                    ...) PRINTF_ARGS(3, 4);
+
 
 /* Send "HTTP 200 OK" response header.
- * Browsers will send a user name and password in their next request, showing
- * an authentication dialog if the password is not stored.
+ * After calling this function, use mg_write or mg_send_chunk to send the
+ * response body.
  * Parameters:
  *   conn: Current connection handle.
  *   mime_type: Set Content-Type for the following content.
@@ -900,6 +901,22 @@ CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn,
                                  const char *mime_type,
                                  long long content_length);
 
+
+/* Send "HTTP 30x" redirect response.
+ * The response has content-size zero: do not send any body data after calling
+ * this function.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   target_url: New location.
+ *   redirect_code: HTTP redirect type. Could be 301, 302, 303, 307, 308.
+ * Return:
+ *   < 0   Error (-1 send error, -2 parameter error)
+ */
+CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn,
+                                       const char *target_url,
+                                       int redirect_code);
+
+
 /* Send HTTP digest access authentication request.
  * Browsers will send a user name and password in their next request, showing
  * an authentication dialog if the password is not stored.

+ 115 - 30
src/civetweb.c

@@ -4462,7 +4462,7 @@ mg_get_response_code_text(const struct mg_connection *conn, int response_code)
 }
 
 
-static void
+static int
 mg_send_http_error_impl(struct mg_connection *conn,
                         int status,
                         const char *fmt,
@@ -4482,7 +4482,7 @@ mg_send_http_error_impl(struct mg_connection *conn,
 	const char *status_text = mg_get_response_code_text(conn, status);
 
 	if ((conn == NULL) || (fmt == NULL)) {
-		return;
+		return -2;
 	}
 
 	/* Set status (for log) */
@@ -4601,7 +4601,7 @@ mg_send_http_error_impl(struct mg_connection *conn,
 				conn->in_error_handler = 1;
 				handle_file_based_request(conn, path_buf, &error_page_file);
 				conn->in_error_handler = 0;
-				return;
+                return 0;
 			}
 		}
 
@@ -4635,16 +4635,21 @@ mg_send_http_error_impl(struct mg_connection *conn,
 			DEBUG_TRACE("Error %i", status);
 		}
 	}
+	return 0;
 }
 
 
-void
+int
 mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
 {
 	va_list ap;
+	int ret;
+
 	va_start(ap, fmt);
-	mg_send_http_error_impl(conn, status, fmt, ap);
+	ret = mg_send_http_error_impl(conn, status, fmt, ap);
 	va_end(ap);
+
+	return ret;
 }
 
 
@@ -4656,6 +4661,11 @@ mg_send_http_ok(struct mg_connection *conn,
 	char date[64];
 	time_t curtime = time(NULL);
 
+	if ((mime_type == NULL) || (*mime_type == 0)) {
+		/* Parameter error */
+		return -2;
+	}
+
 	gmt_time_string(date, sizeof(date), &curtime);
 
 	mg_printf(conn,
@@ -4673,14 +4683,70 @@ mg_send_http_ok(struct mg_connection *conn,
 		mg_printf(conn, "Transfer-Encoding: chunked\r\n\r\n");
 	} else {
 		mg_printf(conn,
-		          "Content-Length: %" INT64_FMT "\r\n\r\n",
-		          content_length);
+                  "Content-Length: %" UINT64_FMT "\r\n\r\n",
+                  (uint64_t)content_length);
 	}
 
 	return 0;
 }
 
 
+int
+mg_send_http_redirect(struct mg_connection *conn,
+                      const char *target_url,
+                      int redirect_code)
+{
+	/* Send a 30x redirect response.
+	 *
+	 * Redirect types (status codes):
+	 *
+	 * Status | Perm/Temp | Method              | Version
+	 *   301  | permanent | POST->GET undefined | HTTP/1.0
+	 *   302  | temporary | POST->GET undefined | HTTP/1.0
+	 *   303  | temporary | always use GET      | HTTP/1.1
+	 *   307  | temporary | always keep method  | HTTP/1.1
+	 *   308  | permanent | always keep method  | HTTP/1.1
+	 */
+	const char *resp;
+    int ret;
+
+	/* In case redirect_code=0, use 307. */
+	if (redirect_code == 0) {
+		redirect_code = 307;
+	}
+
+	/* In case redirect_code is none of the above, return error. */
+	if ((redirect_code != 301) && (redirect_code != 302)
+	    && (redirect_code != 303) && (redirect_code != 307)
+	    && (redirect_code != 308)) {
+		/* Parameter error */
+		return -2;
+	}
+
+	/* Get proper text for response code */
+	resp = mg_get_response_code_text(conn, redirect_code);
+
+	/* If target_url is not defined, redirect to "/". */
+	if ((target_url == NULL) || (*target_url == 0)) {
+		target_url = "/";
+	}
+
+	/* Do not send any additional header. For all other options,
+	 * including caching, there are suitable defaults. */
+	ret = mg_printf(conn,
+	                "HTTP/1.1 %i %s\r\n"
+	                "Location: %s\r\n"
+	                "Content-Length: 0\r\n"
+	                "Connection: %s\r\n\r\n",
+	                redirect_code,
+	                resp,
+	                target_url,
+	                suggest_connection_header(conn));
+
+	return (ret > 0) ? ret : -1;
+}
+
+
 #if defined(_WIN32) && !defined(__SYMBIAN32__)
 /* Create substitutes for POSIX functions in Win32. */
 
@@ -10888,7 +10954,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 		}
 	} else if (get_header(ri.http_headers, ri.num_headers, "Location")
 	           != NULL) {
-		conn->status_code = 302;
+		conn->status_code = 307;
 	} else {
 		conn->status_code = 200;
 	}
@@ -12767,30 +12833,54 @@ alloc_get_host(struct mg_connection *conn)
 static void
 redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 {
+	char target_url[MG_BUF_LEN];
+	int truncated = 0;
+
 	conn->must_close = 1;
 
 	/* Send host, port, uri and (if it exists) ?query_string */
 	if (conn->host) {
-		mg_printf(conn,
-		          "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n",
-		          conn->host,
+
+		/* Use "308 Permanent Redirect" */
+		int redirect_code = 308;
+
+		/* Create target URL */
+		mg_snprintf(
+		    conn,
+		    &truncated,
+		    target_url,
+		    sizeof(target_url),
+		    "Location: https://%s:%d%s%s%s",
+
+		    conn->host,
 #if defined(USE_IPV6)
-		          (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
-		           == AF_INET6)
-		              ? (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
-		                               .lsa.sin6.sin6_port)
-		              :
+		    (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
+		     == AF_INET6)
+		        ? (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
+		                         .lsa.sin6.sin6_port)
+		        :
 #endif
-		              (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
-		                             .lsa.sin.sin_port),
-		          conn->request_info.local_uri,
-		          (conn->request_info.query_string == NULL) ? "" : "?",
-		          (conn->request_info.query_string == NULL)
-		              ? ""
-		              : conn->request_info.query_string);
+		        (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
+		                       .lsa.sin.sin_port),
+		    conn->request_info.local_uri,
+		    (conn->request_info.query_string == NULL) ? "" : "?",
+		    (conn->request_info.query_string == NULL)
+		        ? ""
+		        : conn->request_info.query_string);
+
+		/* Check overflow in location buffer (will not occur if MG_BUF_LEN
+		 * is used as buffer size) */
+		if (truncated) {
+			mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
+			return;
+		}
+
+		/* Use redirect helper function */
+		mg_send_http_redirect(conn, target_url, redirect_code);
 	}
 }
 
+
 static void
 handler_info_acquire(struct mg_handler_info *handler_info)
 {
@@ -12799,6 +12889,7 @@ handler_info_acquire(struct mg_handler_info *handler_info)
 	pthread_mutex_unlock(&handler_info->refcount_mutex);
 }
 
+
 static void
 handler_info_release(struct mg_handler_info *handler_info)
 {
@@ -12808,6 +12899,7 @@ handler_info_release(struct mg_handler_info *handler_info)
 	pthread_mutex_unlock(&handler_info->refcount_mutex);
 }
 
+
 static void
 handler_info_wait_unused(struct mg_handler_info *handler_info)
 {
@@ -13764,13 +13856,6 @@ handle_request(struct mg_connection *conn)
 	/* 15. read a normal file with GET or HEAD */
 	handle_file_based_request(conn, path, &file);
 #endif /* !defined(NO_FILES) */
-
-#if 0
-            /* Perform redirect and auth checks before calling begin_request()
-             * handler.
-             * Otherwise, begin_request() would need to perform auth checks and
-             * redirects. */
-#endif
 }