update the handling of CONNECT: it has no response body by default, but
authorDan Winship <danw@src.gnome.org>
Fri, 16 Mar 2007 21:02:17 +0000 (21:02 +0000)
committerDan Winship <danw@src.gnome.org>
Fri, 16 Mar 2007 21:02:17 +0000 (21:02 +0000)
* 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.

svn path=/trunk/; revision=920

ChangeLog
configure.in
libsoup/soup-message.c
tests/Makefile.am
tests/httpd.conf.in
tests/proxy-test.c [new file with mode: 0644]

index 94ce46a..736ab62 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+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
index 4d0f00a..c4fe298 100644 (file)
@@ -211,15 +211,23 @@ if test "$APACHE_HTTPD" != "no"; then
                [  --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
@@ -232,7 +240,7 @@ AM_CONDITIONAL(HAVE_APACHE, test $have_apache = 1)
 
 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
index 907b5ee..160ada5 100644 (file)
@@ -984,9 +984,7 @@ soup_message_get_response_encoding (SoupMessage *msg, guint *content_length)
 {
        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))
@@ -1022,7 +1020,9 @@ soup_message_get_response_encoding (SoupMessage *msg, guint *content_length)
                                        *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;
        }
 }
index 9d51671..28fa5bd 100644 (file)
@@ -28,6 +28,7 @@ dns_SOURCES = dns.c
 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
@@ -36,7 +37,7 @@ uri_parsing_SOURCES = uri-parsing.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
index 80899ac..c5a8b1f 100644 (file)
@@ -6,21 +6,161 @@ Listen 127.0.0.1:47524
 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@
@@ -28,6 +168,7 @@ Alias /Basic/realm1/not @builddir@
 Alias /Basic/realm1 @builddir@
 Alias /Basic/realm2 @builddir@
 Alias /Basic/realm3 @builddir@
+Alias /Basic @builddir@
 
 <Location /Basic/realm1>
   AuthType Basic
@@ -71,7 +212,7 @@ Alias /Basic/realm3 @builddir@
   Require user user3
 </Location>
 
-
+# Digest auth tests
 Alias /Digest/realm1/realm2/realm1 @builddir@
 Alias /Digest/realm1/realm2 @builddir@
 Alias /Digest/realm1/subdir @builddir@
@@ -79,6 +220,7 @@ Alias /Digest/realm1/not @builddir@
 Alias /Digest/realm1 @builddir@
 Alias /Digest/realm2 @builddir@
 Alias /Digest/realm3 @builddir@
+Alias /Digest @builddir@
 
 <Location /Digest/realm1>
   AuthType Digest
@@ -127,4 +269,3 @@ Alias /Digest/realm3 @builddir@
   AuthDigestDomain /Digest/realm3
   Require valid-user
 </Location>
-
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
new file mode 100644 (file)
index 0000000..a48db6d
--- /dev/null
@@ -0,0 +1,148 @@
+#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;
+}