work on test

This commit is contained in:
Christian Grothoff
2024-12-23 16:50:30 +01:00
parent 22f6c839a6
commit 9f6dcfb19f
5 changed files with 345 additions and 1 deletions
+6
View File
@@ -26,6 +26,7 @@ $(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile
check_PROGRAMS = \
test_client_server \
test_authentication \
test_postprocessor
if MHD_ENABLE_HTTPS
@@ -56,6 +57,11 @@ test_client_server_SOURCES = \
test_client_server_LDADD = \
libmhdt.la
test_authentication_SOURCES = \
test_authentication.c
test_authentication_LDADD = \
libmhdt.la
test_tls_SOURCES = \
test_tls.c
test_tls_LDADD = \
+57
View File
@@ -375,6 +375,34 @@ MHDT_client_do_post (
struct MHDT_PhaseContext *pc);
/**
* Perform GET request and send some HTTP basic authentication header
* to authorize the request.
*
* @param cls a string with "$USERNAME:$PASSWORD"
* @param pc context for the client
* @return error message, NULL on success
*/
const char *
MHDT_client_send_basic_auth (
const void *cls,
struct MHDT_PhaseContext *pc);
/**
* Perform GET request and send some HTTP basic authentication header
* to authorize the request. Expect authentication to fail.
*
* @param cls a string with "$USERNAME:$PASSWORD"
* @param pc context for the client
* @return error message, NULL on success
*/
const char *
MHDT_client_fail_basic_auth (
const void *cls,
struct MHDT_PhaseContext *pc);
/**
* Returns the text from @a cls as the response to any
* request.
@@ -602,6 +630,35 @@ MHDT_server_reply_check_post (
uint_fast64_t upload_size);
/**
* Checks that the client request includes the given
* username and password in HTTP basic authetnication.
* If so, returns #MHD_HTTP_STATUS_NO_CONTENT, otherwise
* an #MHD_HTTP_STATUS_UNAUTHORIZED.
*
* @param cls expected upload data as a 0-terminated string.
* @param request the request object
* @param path the requested uri (without arguments after "?")
* @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
* #MHD_HTTP_METHOD_PUT, etc.)
* @param upload_size the size of the message upload content payload,
* #MHD_SIZE_UNKNOWN for chunked uploads (if the
* final chunk has not been processed yet)
* @return action how to proceed, NULL
* if the request must be aborted due to a serious
* error while handling the request (implies closure
* of underling data stream, for HTTP/1.1 it means
* socket closure).
*/
const struct MHD_Action *
MHDT_server_reply_check_basic_auth (
void *cls,
struct MHD_Request *MHD_RESTRICT request,
const struct MHD_String *MHD_RESTRICT path,
enum MHD_HTTP_Method method,
uint_fast64_t upload_size);
/**
* Initialize options for an MHD daemon for a test.
*
@@ -19,7 +19,7 @@
*/
/**
* @file libtest_convenience.c
* @file libtest_convenience_client_request.c
* @brief convenience functions implementing clients making requests for libtest users
* @author Christian Grothoff
*/
@@ -814,3 +814,114 @@ MHDT_client_do_post (
}
return NULL;
}
/**
* Send HTTP request with basic authentication.
*
* @param cred $USERNAME:$PASSWORD to use
* @param[in,out] phase context
* @param[out] http_status set to HTTP status
* @return error message, NULL on success
*/
static const char *
send_basic_auth (const char *cred,
struct MHDT_PhaseContext *pc,
unsigned int *http_status)
{
CURL *c;
const char *err;
long status;
char *pass = strchr (cred, ':');
char *user;
if (NULL == pass)
return "invalid credential given";
user = strndup (cred,
pass - user);
pass++;
c = curl_easy_init ();
if (NULL == c)
{
free (user);
return "Failed to initialize Curl handle";
}
err = set_url (c,
pc->base_url,
pc);
if (NULL != err)
{
free (user);
curl_easy_cleanup (c);
return err;
}
if ( (CURLE_OK !=
curl_easy_setopt (c,
CURLOPT_HTTPAUTH,
(long) CURLAUTH_BASIC)) ||
(CURLE_OK !=
curl_easy_setopt (c,
CURLOPT_USERNAME,
"user")) ||
(CURLE_OK !=
curl_easy_setopt (c,
CURLOPT_PASSWORD,
"password")) )
{
curl_easy_cleanup (c);
free (user);
return "Failed to set basic authentication header for curl request";
}
free (user);
PERFORM_REQUEST (c);
if (CURLE_OK !=
curl_easy_getinfo (c,
CURLINFO_RESPONSE_CODE,
&status))
{
return "Failed to get HTTP status";
}
*http_status = (unsigned int) status;
curl_easy_cleanup (c);
return NULL;
}
const char *
MHDT_client_send_basic_auth (
const void *cls,
struct MHDT_PhaseContext *pc)
{
const char *cred = cls;
const char *ret;
unsigned int status;
ret = send_basic_auth (cred,
pc,
&status);
if (NULL != ret)
return ret;
if (MHD_HTTP_STATUS_NO_CONTENT != status)
return "invalid HTTP response code";
return NULL;
}
const char *
MHDT_client_fail_basic_auth (
const void *cls,
struct MHDT_PhaseContext *pc)
{
const char *cred = cls;
const char *ret;
unsigned int status;
ret = send_basic_auth (cred,
pc,
&status);
if (NULL != ret)
return ret;
if (MHD_HTTP_STATUS_UNAUTHORIZED != status)
return "invalid HTTP response code";
return NULL;
}
@@ -723,3 +723,69 @@ MHDT_server_reply_check_post (
&post_stream_done,
pi);
}
const struct MHD_Action *
MHDT_server_reply_check_basic_auth (
void *cls,
struct MHD_Request *MHD_RESTRICT request,
const struct MHD_String *MHD_RESTRICT path,
enum MHD_HTTP_Method method,
uint_fast64_t upload_size)
{
const char *cred = cls;
union MHD_RequestInfoDynamicData dd;
enum MHD_StatusCode sc;
struct MHD_BasicAuthInfo *ba;
struct MHD_Connection *connection; // FIXME - suboptimal API
union MHD_RequestInfoFixedData rif;
#define FIXME 1
#if FIXME
sc = MHD_request_get_info_fixed (request,
MHD_REQUEST_INFO_FIXED_CONNECTION,
&rif);
if (MHD_SC_OK != sc)
return NULL;
connection = rif.v_connection;
#endif
/* should not be needed, except to make gcc happy */
memset (&dd,
0,
sizeof (dd));
sc = MHD_request_get_info_dynamic (request,
MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO,
&dd);
if (MHD_SC_OK != sc)
return MHD_action_basic_auth_required_response (
connection, // FIXME: I'd like to see 'request' here.
"test-realm",
MHD_YES,
MHD_response_from_empty (
MHD_HTTP_STATUS_FORBIDDEN));
ba = dd.v_bauth_info;
if (NULL == ba)
return MHD_action_basic_auth_required_response (
connection, // FIXME: I'd like to see 'request' here.
"test-realm",
MHD_YES,
MHD_response_from_empty (
MHD_HTTP_STATUS_FORBIDDEN));
if ( (0 != strncmp (ba->username.cstr,
cred,
ba->username.len)) ||
(':' != cred[ba->username.len]) ||
(NULL == ba->password.cstr) ||
(0 != strcmp (ba->password.cstr,
&cred[ba->username.len + 1])) )
return MHD_action_basic_auth_required_response (
connection, // FIXME: I'd like to see 'request' here.
"test-realm",
MHD_YES,
MHD_response_from_empty (
MHD_HTTP_STATUS_FORBIDDEN));
return MHD_action_from_response (
request,
MHD_response_from_empty (
MHD_HTTP_STATUS_NO_CONTENT));
}
@@ -0,0 +1,104 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2016, 2024 Christian Grothoff & Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file test_authentication.c
* @brief test for HTTP authentication
* @author Christian Grothoff
*/
#include "libtest.h"
int
main (int argc, char *argv[])
{
struct MHD_DaemonOptionAndValue thread1auto[] = {
MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO),
MHD_D_OPTION_WM_WORKER_THREADS (1),
MHD_D_OPTION_TERMINATE ()
};
struct ServerType
{
const char *label;
MHDT_ServerSetup server_setup;
void *server_setup_cls;
MHDT_ServerRunner server_runner;
void *server_runner_cls;
} configs[] = {
{
.label = "auto-selected mode, single threaded",
.server_setup = &MHDT_server_setup_minimal,
.server_setup_cls = thread1auto,
.server_runner = &MHDT_server_run_minimal,
},
{
.label = "END"
},
};
struct MHDT_Phase phases[] = {
{
.label = "simple basic authentication",
.server_cb = &MHDT_server_reply_check_basic_auth,
.server_cb_cls = (void *) "username:password",
.client_cb = &MHDT_client_send_basic_auth,
.client_cb_cls = (void *) "username:password",
.timeout_ms = 200,
},
{
.label = "failing basic authentication",
.server_cb = &MHDT_server_reply_check_basic_auth,
.server_cb_cls = (void *) "username:password",
.client_cb = &MHDT_client_fail_basic_auth,
.client_cb_cls = (void *) "username:word", /* incorrect on purpose */
.timeout_ms = 200,
},
// TODO: digest auth
{
.label = NULL,
},
};
unsigned int i;
(void) argc; /* Unused. Silence compiler warning. */
(void) argv; /* Unused. Silence compiler warning. */
for (i = 0; NULL != configs[i].server_setup; i++)
{
int ret;
fprintf (stderr,
"Running tests with server setup '%s'\n",
configs[i].label);
ret = MHDT_test (configs[i].server_setup,
configs[i].server_setup_cls,
configs[i].server_runner,
configs[i].server_runner_cls,
phases);
if (0 != ret)
{
fprintf (stderr,
"Test failed with server of type '%s' (%u)\n",
configs[i].label,
i);
return ret;
}
}
return 0;
}