+2007-03-16 Dan Winship <danw@novell.com>
+
+ * libsoup/soup-message.c (soup_message_get_response_encoding):
+ update the handling of CONNECT: it has no response body by
+ default, but does have a body if its headers say so.
+
+ * tests/proxy-test.c: test libsoup's behavior when talking to
+ proxies.
+
+ * tests/httpd.conf.in: Load mod_proxy and mod_ssl, and add
+ sections configuring them, for proxy-test
+
+ * configure.in: update the apache-module-dir-finding code to deal
+ with the fact that some modules (eg, mod_ssl) might only be in the
+ mpm-specific module dir, while others (eg, mod_php5) might only be
+ in the generic module dir.
+
2007-03-12 Dan Winship <danw@novell.com>
* tests/Makefile.am (INCLUDES): add $(LIBGNUTLS_CFLAGS) for
[ --with-apache-module-dir Apache modules dir (for tests)],
APACHE_MODULE_DIR="$withval",
[apache_prefix=`dirname \`dirname $APACHE_HTTPD\``
+ mpm=`$APACHE_HTTPD -V | sed -ne 's/^Server MPM: */-/p' | tr 'A-Z' 'a-z'`
# This only works with bash, but should fail harmlessly in sh
- for dir in $apache_prefix/lib{64,}/{apache,http}{2,}{/modules,}; do
+ for dir in $apache_prefix/lib{64,}/{apache,http}{2,}{$mpm,}{/modules,}; do
if test -f $dir/mod_auth_digest.so; then
APACHE_MODULE_DIR="$dir"
- break
+ fi
+ if test -f $dir/mod_ssl.so; then
+ APACHE_SSL_MODULE_DIR="$dir"
+ fi
+ if test -f $dir/mod_php5.so; then
+ APACHE_PHP_MODULE_DIR="$dir"
fi
done])
AC_MSG_RESULT($APACHE_MODULE_DIR)
AC_SUBST(APACHE_MODULE_DIR)
+ AC_SUBST(APACHE_SSL_MODULE_DIR)
+ AC_SUBST(APACHE_PHP_MODULE_DIR)
fi
if test "$APACHE_HTTPD" != "no" -a -n "$APACHE_MODULE_DIR"; then
if test "$have_apache" = 1; then
AC_MSG_CHECKING([for mod_php5])
- if test -f $APACHE_MODULE_DIR/mod_php5.so; then
+ if test -f $APACHE_PHP_MODULE_DIR/mod_php5.so; then
have_php=yes
IF_HAVE_PHP=""
else
{
SoupMethodId method = soup_method_get_id (msg->method);
- /* FIXME: should CONNECT really be here? Where does it say that? */
if (method == SOUP_METHOD_ID_HEAD ||
- method == SOUP_METHOD_ID_CONNECT ||
msg->status_code == SOUP_STATUS_NO_CONTENT ||
msg->status_code == SOUP_STATUS_NOT_MODIFIED ||
SOUP_STATUS_IS_INFORMATIONAL (msg->status_code))
*content_length = lval;
return SOUP_TRANSFER_CONTENT_LENGTH;
}
- } else
+ } else if (method == SOUP_METHOD_ID_CONNECT)
+ return SOUP_TRANSFER_NONE;
+ else
return SOUP_TRANSFER_EOF;
}
}
get_SOURCES = get.c
getbug_SOURCES = getbug.c
header_parsing_SOURCES = header-parsing.c
+proxy_test_SOURCES = proxy-test.c apache-wrapper.c apache-wrapper.h
revserver_SOURCES = revserver.c
simple_httpd_SOURCES = simple-httpd.c
simple_proxy_SOURCES = simple-proxy.c
xmlrpc_test_SOURCES = xmlrpc-test.c apache-wrapper.c apache-wrapper.h
if HAVE_APACHE
-APACHE_TESTS = auth-test
+APACHE_TESTS = auth-test proxy-test
endif
if HAVE_SSL
SSL_TESTS = ssl-test
PidFile @builddir@/httpd.pid
DocumentRoot @srcdir@
+# The tests shut down apache with "graceful-stop", because that makes
+# it close its listening socket right away. But it seems to sometimes
+# result in apache never fully exiting. This fixes that.
+GracefulShutdownTimeout 1
+
# Change this to "./error.log" if it's failing and you don't know why
ErrorLog /dev/null
-LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so
-LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so
-LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so
-LoadModule authn_file_module @APACHE_MODULE_DIR@/mod_authn_file.so
-LoadModule authz_user_module @APACHE_MODULE_DIR@/mod_authz_user.so
-LoadModule dir_module @APACHE_MODULE_DIR@/mod_dir.so
-LoadModule mime_module @APACHE_MODULE_DIR@/mod_mime.so
-@IF_HAVE_PHP@LoadModule php5_module @APACHE_MODULE_DIR@/mod_php5.so
+LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so
+LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so
+LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so
+LoadModule authn_file_module @APACHE_MODULE_DIR@/mod_authn_file.so
+LoadModule authz_host_module @APACHE_MODULE_DIR@/mod_authz_host.so
+LoadModule authz_user_module @APACHE_MODULE_DIR@/mod_authz_user.so
+LoadModule dir_module @APACHE_MODULE_DIR@/mod_dir.so
+LoadModule mime_module @APACHE_MODULE_DIR@/mod_mime.so
+@IF_HAVE_PHP@LoadModule php5_module @APACHE_PHP_MODULE_DIR@/mod_php5.so
+LoadModule proxy_module @APACHE_MODULE_DIR@/mod_proxy.so
+LoadModule proxy_http_module @APACHE_MODULE_DIR@/mod_proxy_http.so
+LoadModule proxy_connect_module @APACHE_MODULE_DIR@/mod_proxy_connect.so
+LoadModule ssl_module @APACHE_SSL_MODULE_DIR@/mod_ssl.so
DirectoryIndex httpd.pid
AddType application/x-httpd-php .php
+
+# Proxy #1: unauthenticated
+Listen 127.0.0.1:47526
+<VirtualHost 127.0.0.1:47526>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections
+ <Proxy http://127.0.0.1:47524*>
+ Order Allow,Deny
+ Allow from all
+ </Proxy>
+
+ # Allow CONNECT to local https port
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+ </Proxy>
+
+ # Deny non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+# Proxy #2: authenticated
+Listen 127.0.0.1:47527
+<VirtualHost 127.0.0.1:47527>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Allow CONNECT to local https port with authentication
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+# Proxy #3: unauthenticatable-to
+Listen 127.0.0.1:47528
+<VirtualHost 127.0.0.1:47528>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require user no-such-user
+ </Proxy>
+
+ # Allow CONNECT to local https port with authentication
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require user no-such-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+
+# SSL setup
+<IfModule mod_ssl.c>
+ Listen 127.0.0.1:47525
+
+ <VirtualHost 127.0.0.1:47525>
+ SSLEngine on
+
+ SSLCertificateFile test-cert.pem
+ SSLCertificateKeyFile test-key.pem
+
+ </VirtualHost>
+</IfModule>
+
+
+# Basic auth tests
Alias /Basic/realm1/realm2/realm1 @builddir@
Alias /Basic/realm1/realm2 @builddir@
Alias /Basic/realm1/subdir @builddir@
Alias /Basic/realm1 @builddir@
Alias /Basic/realm2 @builddir@
Alias /Basic/realm3 @builddir@
+Alias /Basic @builddir@
<Location /Basic/realm1>
AuthType Basic
Require user user3
</Location>
-
+# Digest auth tests
Alias /Digest/realm1/realm2/realm1 @builddir@
Alias /Digest/realm1/realm2 @builddir@
Alias /Digest/realm1/subdir @builddir@
Alias /Digest/realm1 @builddir@
Alias /Digest/realm2 @builddir@
Alias /Digest/realm3 @builddir@
+Alias /Digest @builddir@
<Location /Digest/realm1>
AuthType Digest
AuthDigestDomain /Digest/realm3
Require valid-user
</Location>
-
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libsoup/soup.h"
+#include "apache-wrapper.h"
+
+int errors = 0;
+
+typedef struct {
+ const char *explanation;
+ const char *url;
+ const guint final_status;
+} SoupProxyTest;
+
+SoupProxyTest tests[] = {
+ { "GET -> 200", "", SOUP_STATUS_OK },
+ { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND },
+ { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK },
+ { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED },
+ { "GET -> 403", "http://no-proxy.example.com/", SOUP_STATUS_FORBIDDEN },
+};
+int ntests = sizeof (tests) / sizeof (tests[0]);
+
+#define HTTP_SERVER "http://127.0.0.1:47524"
+#define HTTPS_SERVER "https://127.0.0.1:47525"
+
+enum {
+ SIMPLE_PROXY,
+ AUTH_PROXY,
+ UNAUTH_PROXY
+};
+static const char *proxies[] = {
+ "http://127.0.0.1:47526",
+ "http://127.0.0.1:47527",
+ "http://127.0.0.1:47528"
+};
+static const char *proxy_names[] = {
+ "simple proxy",
+ "authenticated proxy",
+ "unauthenticatable-to proxy"
+};
+
+static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password, gpointer data)
+{
+ *username = g_strdup ("user1");
+ *password = g_strdup ("realm1");
+}
+
+static void
+test_url (const char *url, int proxy, guint expected, gboolean sync)
+{
+ SoupSession *session;
+ SoupUri *proxy_uri;
+ SoupMessage *msg;
+
+ printf (" GET %s via %s\n", url, proxy_names[proxy]);
+ if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
+ expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
+
+ /* We create a new session for each request to ensure that
+ * connections/auth aren't cached between tests.
+ */
+ proxy_uri = soup_uri_new (proxies[proxy]);
+ session = g_object_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+
+ msg = soup_message_new (SOUP_METHOD_GET, url);
+ if (!msg) {
+ fprintf (stderr, "proxy-test: Could not parse URI\n");
+ exit (1);
+ }
+
+ soup_session_send_message (session, msg);
+
+ printf (" %d %s\n", msg->status_code, msg->reason_phrase);
+ if (msg->status_code != expected) {
+ printf (" EXPECTED %d!\n", expected);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ soup_session_abort (session);
+ g_object_unref (session);
+}
+
+static void
+run_test (int i, gboolean sync)
+{
+ char *http_url, *https_url;
+
+ printf ("Test %d: %s (%s)\n", i + 1, tests[i].explanation,
+ sync ? "sync" : "async");
+
+ if (!strncmp (tests[i].url, "http", 4)) {
+ http_url = g_strdup (tests[i].url);
+ https_url = g_strdup_printf ("https%s", tests[i].url + 4);
+ } else {
+ http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL);
+ https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
+ }
+ test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync);
+ test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync);
+ test_url (http_url, AUTH_PROXY, tests[i].final_status, sync);
+ test_url (https_url, AUTH_PROXY, tests[i].final_status, sync);
+ test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync);
+ test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync);
+
+ g_free (http_url);
+ g_free (https_url);
+
+ printf ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+
+ g_type_init ();
+ g_thread_init (NULL);
+
+ if (!apache_init ()) {
+ fprintf (stderr, "Could not start apache\n");
+ return 1;
+ }
+
+ for (i = 0; i < ntests; i++) {
+ run_test (i, FALSE);
+ run_test (i, TRUE);
+ }
+
+ apache_cleanup ();
+
+ printf ("proxy-test: %d errors\n", errors);
+ return errors;
+}