diff options
author | Seonah Moon <seonah1.moon@samsung.com> | 2019-12-10 13:58:05 +0900 |
---|---|---|
committer | Seonah Moon <seonah1.moon@samsung.com> | 2019-12-10 13:58:46 +0900 |
commit | a4c63790246af7f0e0715ec58045ee0d7d0bd4bd (patch) | |
tree | 5d14b6de79b14cfe9d0aa2c13a3c0c9433c9af42 /tests | |
parent | 7e83c2dd89bc4fcbbc823eaa53a335013dbd4343 (diff) | |
parent | 58894334cd3f0b89c865304e8e9d6eea3cd20a55 (diff) | |
download | libsoup-a4c63790246af7f0e0715ec58045ee0d7d0bd4bd.tar.gz libsoup-a4c63790246af7f0e0715ec58045ee0d7d0bd4bd.tar.bz2 libsoup-a4c63790246af7f0e0715ec58045ee0d7d0bd4bd.zip |
Upgrade to 2.62.2submit/tizen/20191218.041916accepted/tizen/unified/20191224.131909
Change-Id: I8b24412aceb62f5217597e6ddf4106bac947c809
Diffstat (limited to 'tests')
46 files changed, 6111 insertions, 1211 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index a8b9d019..81a72cbc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -41,6 +41,9 @@ test_programs = \ timeout-test \ tld-test \ uri-parsing \ + websocket-test \ + xmlrpc-old-server-test \ + xmlrpc-old-test \ xmlrpc-server-test \ xmlrpc-test @@ -64,22 +67,6 @@ libtest_la_SOURCES = \ LDADD = libtest.la if HAVE_APACHE -if HAVE_APACHE_2_2 -httpd_conf_in = httpd.conf.22.in -else -httpd_conf_in = httpd.conf.24.in -endif -httpd.conf: $(httpd_conf_in) - $(AM_V_GEN) sed -e 's,[@]srcdir@,$(srcdir),' \ - -e 's,[@]builddir@,$(builddir),' \ - -e 's,[@]APACHE_MODULE_DIR@,$(APACHE_MODULE_DIR),' \ - -e 's,[@]APACHE_PHP_MODULE_DIR@,$(APACHE_PHP_MODULE_DIR),' \ - -e 's,[@]APACHE_PHP_MODULE@,$(APACHE_PHP_MODULE),' \ - -e 's,[@]IF_HAVE_PHP@,$(IF_HAVE_PHP),' \ - -e 's,[@]APACHE_SSL_MODULE_DIR@,$(APACHE_SSL_MODULE_DIR),' \ - $< > $@ || rm -f $@ - -BUILT_SOURCES += httpd.conf test_data += \ htdigest \ htpasswd \ @@ -94,8 +81,7 @@ soup-tests.gresource: soup-tests.gresource.xml $(RESOURCES) EXTRA_DIST += \ htdigest \ htpasswd \ - httpd.conf.22.in \ - httpd.conf.24.in \ + httpd.conf.in \ index.txt \ libsoup.supp \ soup-tests.gresource.xml \ @@ -116,21 +102,9 @@ check-local: check-TESTS .PHONY: start-httpd kill-httpd start-httpd: -if HAVE_APACHE_2_2 - @$(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; -endif -if HAVE_APACHE_2_4 @$(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; -endif kill-httpd: -if HAVE_APACHE_2_2 - @if [ -f httpd.pid ]; then \ - $(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ - fi -endif -if HAVE_APACHE_2_4 @if [ -f httpd.pid ]; then \ $(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ fi -endif diff --git a/tests/Makefile.in b/tests/Makefile.in index 5e432785..91f53720 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.13.4 from Makefile.am. +# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -83,9 +93,6 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(top_srcdir)/glib-tap.mk $(srcdir)/Makefile.in \ - $(srcdir)/Makefile.am $(top_srcdir)/build-aux/depcomp \ - $(top_srcdir)/build-aux/test-driver TESTS = $(am__EXEEXT_1) installed_test_PROGRAMS = $(am__EXEEXT_6) noinst_PROGRAMS = $(am__EXEEXT_7) @@ -115,25 +122,26 @@ check_PROGRAMS = $(am__EXEEXT_5) @ENABLE_INSTALLED_TESTS_TRUE@ $(dist_installed_test_data) @ENABLE_INSTALLED_TESTS_TRUE@am__append_12 = $(test_ltlibraries) $(installed_test_ltlibraries) @ENABLE_INSTALLED_TESTS_TRUE@am__append_13 = $(installed_test_meta_DATA) -@HAVE_APACHE_TRUE@am__append_14 = httpd.conf -@HAVE_APACHE_TRUE@am__append_15 = \ +@HAVE_APACHE_TRUE@am__append_14 = \ @HAVE_APACHE_TRUE@ htdigest \ @HAVE_APACHE_TRUE@ htpasswd \ @HAVE_APACHE_TRUE@ httpd.conf subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/glibtests.m4 \ - $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ - $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_code_coverage.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/gtk-doc.m4 \ + $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/vapigen.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = httpd.conf CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -189,7 +197,9 @@ am__EXEEXT_1 = auth-test$(EXEEXT) cache-test$(EXEEXT) \ server-test$(EXEEXT) sniffing-test$(EXEEXT) \ socket-test$(EXEEXT) ssl-test$(EXEEXT) streaming-test$(EXEEXT) \ timeout-test$(EXEEXT) tld-test$(EXEEXT) uri-parsing$(EXEEXT) \ - xmlrpc-server-test$(EXEEXT) xmlrpc-test$(EXEEXT) + websocket-test$(EXEEXT) xmlrpc-old-server-test$(EXEEXT) \ + xmlrpc-old-test$(EXEEXT) xmlrpc-server-test$(EXEEXT) \ + xmlrpc-test$(EXEEXT) am__EXEEXT_2 = $(am__EXEEXT_1) am__EXEEXT_3 = ntlm-test-helper$(EXEEXT) $(am__EXEEXT_2) am__EXEEXT_4 = $(am__EXEEXT_1) $(am__EXEEXT_3) @@ -330,6 +340,18 @@ uri_parsing_SOURCES = uri-parsing.c uri_parsing_OBJECTS = uri-parsing.$(OBJEXT) uri_parsing_LDADD = $(LDADD) uri_parsing_DEPENDENCIES = libtest.la +websocket_test_SOURCES = websocket-test.c +websocket_test_OBJECTS = websocket-test.$(OBJEXT) +websocket_test_LDADD = $(LDADD) +websocket_test_DEPENDENCIES = libtest.la +xmlrpc_old_server_test_SOURCES = xmlrpc-old-server-test.c +xmlrpc_old_server_test_OBJECTS = xmlrpc-old-server-test.$(OBJEXT) +xmlrpc_old_server_test_LDADD = $(LDADD) +xmlrpc_old_server_test_DEPENDENCIES = libtest.la +xmlrpc_old_test_SOURCES = xmlrpc-old-test.c +xmlrpc_old_test_OBJECTS = xmlrpc-old-test.$(OBJEXT) +xmlrpc_old_test_LDADD = $(LDADD) +xmlrpc_old_test_DEPENDENCIES = libtest.la xmlrpc_server_test_SOURCES = xmlrpc-server-test.c xmlrpc_server_test_OBJECTS = xmlrpc-server-test.$(OBJEXT) xmlrpc_server_test_LDADD = $(LDADD) @@ -382,6 +404,7 @@ SOURCES = $(libtest_la_SOURCES) auth-test.c cache-test.c \ resource-test.c server-auth-test.c server-test.c \ session-test.c sniffing-test.c socket-test.c ssl-test.c \ streaming-test.c timeout-test.c tld-test.c uri-parsing.c \ + websocket-test.c xmlrpc-old-server-test.c xmlrpc-old-test.c \ xmlrpc-server-test.c xmlrpc-test.c DIST_SOURCES = $(libtest_la_SOURCES) auth-test.c cache-test.c \ chunk-io-test.c chunk-test.c coding-test.c connection-test.c \ @@ -392,6 +415,7 @@ DIST_SOURCES = $(libtest_la_SOURCES) auth-test.c cache-test.c \ resource-test.c server-auth-test.c server-test.c \ session-test.c sniffing-test.c socket-test.c ssl-test.c \ streaming-test.c timeout-test.c tld-test.c uri-parsing.c \ + websocket-test.c xmlrpc-old-server-test.c xmlrpc-old-test.c \ xmlrpc-server-test.c xmlrpc-test.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -595,6 +619,9 @@ TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/httpd.conf.in \ + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver $(top_srcdir)/glib-tap.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ @@ -602,7 +629,6 @@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APACHE_HTTPD = @APACHE_HTTPD@ APACHE_MODULE_DIR = @APACHE_MODULE_DIR@ -APACHE_PHP_MODULE = @APACHE_PHP_MODULE@ APACHE_PHP_MODULE_DIR = @APACHE_PHP_MODULE_DIR@ APACHE_SSL_MODULE_DIR = @APACHE_SSL_MODULE_DIR@ AR = @AR@ @@ -614,6 +640,11 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@ +CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CURL = @CURL@ @@ -629,6 +660,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GCOV = @GCOV@ +GENHTML = @GENHTML@ GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ @@ -647,6 +680,7 @@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HAVE_GNOME = @HAVE_GNOME@ HTML_DIR = @HTML_DIR@ +IF_HAVE_MOD_UNIXD = @IF_HAVE_MOD_UNIXD@ IF_HAVE_PHP = @IF_HAVE_PHP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -669,6 +703,10 @@ INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ @@ -680,6 +718,7 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -711,6 +750,7 @@ SOUP_AGE = @SOUP_AGE@ SOUP_API_VERSION = @SOUP_API_VERSION@ SOUP_CURRENT = @SOUP_CURRENT@ SOUP_DEBUG_FLAGS = @SOUP_DEBUG_FLAGS@ +SOUP_HIDDEN_VISIBILITY_CFLAGS = @SOUP_HIDDEN_VISIBILITY_CFLAGS@ SOUP_MAJOR_VERSION = @SOUP_MAJOR_VERSION@ SOUP_MICRO_VERSION = @SOUP_MICRO_VERSION@ SOUP_MINOR_VERSION = @SOUP_MINOR_VERSION@ @@ -719,6 +759,11 @@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ USE_NLS = @USE_NLS@ +VALAC = @VALAC@ +VAPIDIR = @VAPIDIR@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XML_CFLAGS = @XML_CFLAGS@ @@ -772,6 +817,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -789,15 +835,15 @@ LOG_COMPILER = $(top_srcdir)/tap-test NULL = # initialize variables for unconditional += appending -BUILT_SOURCES = $(am__append_14) +BUILT_SOURCES = BUILT_EXTRA_DIST = CLEANFILES = *.log *.trs $(am__append_13) DISTCLEANFILES = soup-tests.gresource httpd.conf MAINTAINERCLEANFILES = EXTRA_DIST = $(all_dist_test_scripts) $(all_dist_test_data) htdigest \ - htpasswd httpd.conf.22.in httpd.conf.24.in index.txt \ - libsoup.supp soup-tests.gresource.xml test-cert.pem \ - test-key.pem xmlrpc-server.php $(RESOURCES) + htpasswd httpd.conf.in index.txt libsoup.supp \ + soup-tests.gresource.xml test-cert.pem test-key.pem \ + xmlrpc-server.php $(RESOURCES) installed_test_LTLIBRARIES = $(am__append_12) installed_test_SCRIPTS = $(am__append_10) nobase_installed_test_DATA = $(am__append_11) @@ -868,6 +914,9 @@ test_programs = \ timeout-test \ tld-test \ uri-parsing \ + websocket-test \ + xmlrpc-old-server-test \ + xmlrpc-old-test \ xmlrpc-server-test \ xmlrpc-test @@ -876,14 +925,12 @@ test_extra_programs = \ $(TESTS) test_data = index.txt soup-tests.gresource test-cert.pem test-key.pem \ - xmlrpc-server.php $(am__append_15) + xmlrpc-server.php $(am__append_14) libtest_la_SOURCES = \ test-utils.c \ test-utils.h LDADD = libtest.la -@HAVE_APACHE_2_2_FALSE@@HAVE_APACHE_TRUE@httpd_conf_in = httpd.conf.24.in -@HAVE_APACHE_2_2_TRUE@@HAVE_APACHE_TRUE@httpd_conf_in = httpd.conf.22.in RESOURCES = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/soup-tests.gresource.xml) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am @@ -902,7 +949,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/glib-tap.mk $(am__co echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign tests/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -911,7 +957,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/glib-tap.mk: +$(top_srcdir)/glib-tap.mk $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -921,6 +967,8 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +httpd.conf: $(top_builddir)/config.status $(srcdir)/httpd.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ clean-checkLTLIBRARIES: -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES) @@ -1181,6 +1229,18 @@ uri-parsing$(EXEEXT): $(uri_parsing_OBJECTS) $(uri_parsing_DEPENDENCIES) $(EXTRA @rm -f uri-parsing$(EXEEXT) $(AM_V_CCLD)$(LINK) $(uri_parsing_OBJECTS) $(uri_parsing_LDADD) $(LIBS) +websocket-test$(EXEEXT): $(websocket_test_OBJECTS) $(websocket_test_DEPENDENCIES) $(EXTRA_websocket_test_DEPENDENCIES) + @rm -f websocket-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(websocket_test_OBJECTS) $(websocket_test_LDADD) $(LIBS) + +xmlrpc-old-server-test$(EXEEXT): $(xmlrpc_old_server_test_OBJECTS) $(xmlrpc_old_server_test_DEPENDENCIES) $(EXTRA_xmlrpc_old_server_test_DEPENDENCIES) + @rm -f xmlrpc-old-server-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(xmlrpc_old_server_test_OBJECTS) $(xmlrpc_old_server_test_LDADD) $(LIBS) + +xmlrpc-old-test$(EXEEXT): $(xmlrpc_old_test_OBJECTS) $(xmlrpc_old_test_DEPENDENCIES) $(EXTRA_xmlrpc_old_test_DEPENDENCIES) + @rm -f xmlrpc-old-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(xmlrpc_old_test_OBJECTS) $(xmlrpc_old_test_LDADD) $(LIBS) + xmlrpc-server-test$(EXEEXT): $(xmlrpc_server_test_OBJECTS) $(xmlrpc_server_test_DEPENDENCIES) $(EXTRA_xmlrpc_server_test_DEPENDENCIES) @rm -f xmlrpc-server-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(xmlrpc_server_test_OBJECTS) $(xmlrpc_server_test_LDADD) $(LIBS) @@ -1264,6 +1324,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timeout-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tld-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri-parsing.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/websocket-test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrpc-old-server-test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrpc-old-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrpc-server-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlrpc-test.Po@am__quote@ @@ -1272,14 +1335,14 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -1421,7 +1484,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS) if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ - else \ + elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ @@ -1756,6 +1819,27 @@ uri-parsing.log: uri-parsing$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +websocket-test.log: websocket-test$(EXEEXT) + @p='websocket-test$(EXEEXT)'; \ + b='websocket-test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +xmlrpc-old-server-test.log: xmlrpc-old-server-test$(EXEEXT) + @p='xmlrpc-old-server-test$(EXEEXT)'; \ + b='xmlrpc-old-server-test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +xmlrpc-old-test.log: xmlrpc-old-test$(EXEEXT) + @p='xmlrpc-old-test$(EXEEXT)'; \ + b='xmlrpc-old-test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) xmlrpc-server-test.log: xmlrpc-server-test$(EXEEXT) @p='xmlrpc-server-test$(EXEEXT)'; \ b='xmlrpc-server-test'; \ @@ -1971,21 +2055,14 @@ uninstall-am: uninstall-installed_testLTLIBRARIES \ uninstall-installed_test_metaDATA \ uninstall-nobase_installed_testDATA +.PRECIOUS: Makefile + @ENABLE_INSTALLED_TESTS_TRUE@%.test: %$(EXEEXT) Makefile @ENABLE_INSTALLED_TESTS_TRUE@ $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ @ENABLE_INSTALLED_TESTS_TRUE@ echo 'Type=session' >> $@.tmp; \ @ENABLE_INSTALLED_TESTS_TRUE@ echo 'Exec=$(installed_testdir)/$<' >> $@.tmp; \ @ENABLE_INSTALLED_TESTS_TRUE@ mv $@.tmp $@) -@HAVE_APACHE_TRUE@httpd.conf: $(httpd_conf_in) -@HAVE_APACHE_TRUE@ $(AM_V_GEN) sed -e 's,[@]srcdir@,$(srcdir),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]builddir@,$(builddir),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]APACHE_MODULE_DIR@,$(APACHE_MODULE_DIR),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]APACHE_PHP_MODULE_DIR@,$(APACHE_PHP_MODULE_DIR),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]APACHE_PHP_MODULE@,$(APACHE_PHP_MODULE),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]IF_HAVE_PHP@,$(IF_HAVE_PHP),' \ -@HAVE_APACHE_TRUE@ -e 's,[@]APACHE_SSL_MODULE_DIR@,$(APACHE_SSL_MODULE_DIR),' \ -@HAVE_APACHE_TRUE@ $< > $@ || rm -f $@ soup-tests.gresource: soup-tests.gresource.xml $(RESOURCES) $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $< @@ -1998,16 +2075,12 @@ check-local: check-TESTS .PHONY: start-httpd kill-httpd start-httpd: -@HAVE_APACHE_2_2_TRUE@ @$(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; -@HAVE_APACHE_2_4_TRUE@ @$(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; + @$(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; kill-httpd: -@HAVE_APACHE_2_2_TRUE@ @if [ -f httpd.pid ]; then \ -@HAVE_APACHE_2_2_TRUE@ $(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ -@HAVE_APACHE_2_2_TRUE@ fi -@HAVE_APACHE_2_4_TRUE@ @if [ -f httpd.pid ]; then \ -@HAVE_APACHE_2_4_TRUE@ $(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ -@HAVE_APACHE_2_4_TRUE@ fi + @if [ -f httpd.pid ]; then \ + $(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ + fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/tests/auth-test.c b/tests/auth-test.c index 8ed5cead..1144decf 100644 --- a/tests/auth-test.c +++ b/tests/auth-test.c @@ -414,13 +414,18 @@ digest_nonce_unauthorized (SoupMessage *msg, gpointer data) static void do_digest_nonce_test (SoupSession *session, - const char *nth, const char *uri, + const char *nth, const char *uri, gboolean use_auth_cache, gboolean expect_401, gboolean expect_signal) { SoupMessage *msg; gboolean got_401; msg = soup_message_new (SOUP_METHOD_GET, uri); + if (!use_auth_cache) { + SoupMessageFlags flags = soup_message_get_flags (msg); + + soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE); + } if (expect_signal) { g_signal_connect (session, "authenticate", G_CALLBACK (digest_nonce_authenticate), @@ -437,6 +442,12 @@ do_digest_nonce_test (SoupSession *session, got_401 ? "got" : "did not get"); soup_test_assert_message_status (msg, SOUP_STATUS_OK); + if (expect_signal) { + g_signal_handlers_disconnect_by_func (session, + G_CALLBACK (digest_nonce_authenticate), + NULL); + } + g_object_unref (msg); } @@ -451,15 +462,15 @@ do_digest_expiration_test (void) session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); uri = g_strconcat (base_uri, "Digest/realm1/", NULL); - do_digest_nonce_test (session, "First", uri, TRUE, TRUE); + do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE); g_free (uri); sleep (2); uri = g_strconcat (base_uri, "Digest/realm1/expire/", NULL); - do_digest_nonce_test (session, "Second", uri, TRUE, FALSE); + do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, FALSE); sleep (1); - do_digest_nonce_test (session, "Third", uri, FALSE, FALSE); + do_digest_nonce_test (session, "Third", uri, TRUE, FALSE, FALSE); sleep (1); - do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE); + do_digest_nonce_test (session, "Fourth", uri, TRUE, FALSE, FALSE); g_free (uri); soup_test_session_abort_unref (session); @@ -864,12 +875,10 @@ do_select_auth_test (void) * side of this scenario correctly, because we test it against * curl in server-auth-test. */ - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); basic_auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", @@ -1012,12 +1021,12 @@ do_auth_close_test (void) SoupURI *uri; AuthCloseData acd; - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - uri = soup_uri_new ("http://127.0.0.1/close"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (uri, "/close"); basic_auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", @@ -1123,14 +1132,13 @@ do_disappearing_auth_test (void) SoupSession *session; int counter; - g_test_bug ("https://bugzilla.redhat.com/show_bug.cgi?id=916224"); + g_test_bug_base ("https://bugzilla.redhat.com/"); + g_test_bug ("916224"); server = soup_test_server_new (FALSE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", @@ -1187,6 +1195,17 @@ static SoupAuthTest relogin_tests[] = { { NULL } }; +/* https://bugzilla.gnome.org/show_bug.cgi?id=755617 */ +static SoupAuthTest basic_root_pspace_test[] = { + { "Auth provided via URL, should succeed", + "BasicRoot", "1", TRUE, "01", SOUP_STATUS_OK }, + + { "Parent dir should automatically reuse auth", + "/", "1", FALSE, "1", SOUP_STATUS_OK }, + + { NULL } +}; + static void do_batch_tests (gconstpointer data) { @@ -1257,6 +1276,220 @@ do_batch_tests (gconstpointer data) soup_test_session_abort_unref (session); } +static void +do_clear_credentials_test (void) +{ + SoupSession *session; + SoupAuthManager *manager; + char *uri; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + + uri = g_strconcat (base_uri, "Digest/realm1/", NULL); + do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE); + + manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER)); + soup_auth_manager_clear_cached_credentials (manager); + + do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, TRUE); + g_free (uri); + + soup_test_session_abort_unref (session); +} + +static void +do_message_do_not_use_auth_cache_test (void) +{ + SoupSession *session; + SoupAuthManager *manager; + SoupMessage *msg; + SoupMessageFlags flags; + SoupURI *soup_uri; + char *uri; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + + uri = g_strconcat (base_uri, "Digest/realm1/", NULL); + + /* First check that cached credentials are not used */ + do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE); + do_digest_nonce_test (session, "Second", uri, TRUE, FALSE, FALSE); + do_digest_nonce_test (session, "Third", uri, FALSE, TRUE, TRUE); + + /* Passing credentials in the URI should always authenticate + * no matter whether the cache is used or not + */ + soup_uri = soup_uri_new (uri); + soup_uri_set_user (soup_uri, "user1"); + soup_uri_set_password (soup_uri, "realm1"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, soup_uri); + flags = soup_message_get_flags (msg); + soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + soup_uri_free (soup_uri); + + manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER)); + + soup_auth_manager_clear_cached_credentials (manager); + + /* Now check that credentials are not stored */ + do_digest_nonce_test (session, "First", uri, FALSE, TRUE, TRUE); + do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, TRUE); + do_digest_nonce_test (session, "Third", uri, TRUE, FALSE, FALSE); + + /* Credentials were stored for uri, but if we set SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE flag, + * and we don't have the authenticate signal, it should respond with 401 + */ + msg = soup_message_new (SOUP_METHOD_GET, uri); + flags = soup_message_get_flags (msg); + soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + g_object_unref (msg); + g_free (uri); + + soup_test_session_abort_unref (session); +} + +static void +async_no_auth_cache_authenticate (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, SoupAuth **auth_out) +{ + debug_printf (1, " async_no_auth_cache_authenticate\n"); + + soup_session_pause_message (session, msg); + *auth_out = g_object_ref (auth); + g_main_loop_quit (loop); +} + +static void +async_no_auth_cache_finished (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + debug_printf (1, " async_no_auth_cache_finished\n"); + + g_main_loop_quit (loop); +} + +static void +do_async_message_do_not_use_auth_cache_test (void) +{ + SoupSession *session; + SoupMessage *msg; + char *uri; + SoupAuth *auth = NULL; + SoupMessageFlags flags; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + loop = g_main_loop_new (NULL, TRUE); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + uri = g_strconcat (base_uri, "Basic/realm1/", NULL); + + msg = soup_message_new ("GET", uri); + g_free (uri); + g_signal_connect (session, "authenticate", + G_CALLBACK (async_no_auth_cache_authenticate), &auth); + flags = soup_message_get_flags (msg); + soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE); + g_object_ref (msg); + soup_session_queue_message (session, msg, async_no_auth_cache_finished, NULL); + g_main_loop_run (loop); + + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + + soup_test_assert (auth, "msg didn't get authenticate signal"); + soup_auth_authenticate (auth, "user1", "realm1"); + g_object_unref (auth); + + soup_session_unpause_message (session, msg); + g_main_loop_run (loop); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + soup_test_session_abort_unref (session); + g_object_unref (msg); + g_main_loop_unref (loop); +} + +static void +has_authorization_header_authenticate (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, gpointer data) +{ + SoupAuth **saved_auth = data; + + soup_auth_authenticate (auth, "user1", "realm1"); + *saved_auth = g_object_ref (auth); +} + +static void +has_authorization_header_authenticate_assert (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, gpointer data) +{ + soup_test_assert (FALSE, "authenticate emitted unexpectedly"); +} + +static void +do_message_has_authorization_header_test (void) +{ + SoupSession *session; + SoupMessage *msg; + SoupAuthManager *manager; + SoupAuth *auth = NULL; + char *token; + guint auth_id; + char *uri; + + g_test_bug ("775882"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + uri = g_strconcat (base_uri, "Digest/realm1/", NULL); + + msg = soup_message_new ("GET", uri); + auth_id = g_signal_connect (session, "authenticate", + G_CALLBACK (has_authorization_header_authenticate), &auth); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + soup_test_assert (SOUP_IS_AUTH (auth), "Expected a SoupAuth"); + token = soup_auth_get_authorization (auth, msg); + g_object_unref (auth); + g_object_unref (msg); + g_signal_handler_disconnect (session, auth_id); + + manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER)); + soup_auth_manager_clear_cached_credentials (manager); + + msg = soup_message_new ("GET", uri); + soup_message_headers_replace (msg->request_headers, "Authorization", token); + auth_id = g_signal_connect (session, "authenticate", + G_CALLBACK (has_authorization_header_authenticate_assert), + NULL); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + /* Check that we can also provide our own Authorization header when not using credentials cache. */ + soup_auth_manager_clear_cached_credentials (manager); + msg = soup_message_new ("GET", uri); + soup_message_headers_replace (msg->request_headers, "Authorization", token); + soup_message_set_flags (msg, soup_message_get_flags (msg) | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + g_free (token); + g_signal_handler_disconnect (session, auth_id); + + g_free (uri); + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -1269,6 +1502,7 @@ main (int argc, char **argv) g_test_add_data_func ("/auth/main-tests", main_tests, do_batch_tests); g_test_add_data_func ("/auth/relogin-tests", relogin_tests, do_batch_tests); + g_test_add_data_func ("/auth/basic-root-pspec-test", basic_root_pspace_test, do_batch_tests); g_test_add_func ("/auth/pipelined-auth", do_pipelined_auth_test); g_test_add_func ("/auth/digest-expiration", do_digest_expiration_test); g_test_add_func ("/auth/async-auth/good-password", do_async_auth_good_password_test); @@ -1278,6 +1512,10 @@ main (int argc, char **argv) g_test_add_func ("/auth/auth-close", do_auth_close_test); g_test_add_func ("/auth/infinite-auth", do_infinite_auth_test); g_test_add_func ("/auth/disappearing-auth", do_disappearing_auth_test); + g_test_add_func ("/auth/clear-credentials", do_clear_credentials_test); + g_test_add_func ("/auth/message-do-not-use-auth-cache", do_message_do_not_use_auth_cache_test); + g_test_add_func ("/auth/async-message-do-not-use-auth-cache", do_async_message_do_not_use_auth_cache_test); + g_test_add_func ("/auth/authorization-header-request", do_message_has_authorization_header_test); ret = g_test_run (); diff --git a/tests/cache-test.c b/tests/cache-test.c index 3478f377..7d5897c0 100644 --- a/tests/cache-test.c +++ b/tests/cache-test.c @@ -122,6 +122,7 @@ static char *do_request (SoupSession *session, static gboolean last_request_hit_network; static gboolean last_request_validated; +static gboolean last_request_unqueued; static guint cancelled_requests; static void @@ -152,6 +153,7 @@ do_request (SoupSession *session, GError *error = NULL; last_request_validated = last_request_hit_network = FALSE; + last_request_unqueued = FALSE; uri = soup_uri_new_with_base (base_uri, path); req = soup_session_request_http_uri (session, method, uri, NULL); @@ -181,6 +183,12 @@ do_request (SoupSession *session, g_object_unref (msg); + if (last_request_validated) + last_request_unqueued = FALSE; + else + soup_test_assert (!last_request_unqueued, + "Request unqueued before finishing"); + last_request_hit_network = is_network_stream (stream); g_input_stream_read_all (stream, buf, sizeof (buf), &nread, @@ -219,7 +227,7 @@ do_request_with_cancel (SoupSession *session, GError *error = NULL; GCancellable *cancellable; - last_request_validated = last_request_hit_network = FALSE; + last_request_validated = last_request_hit_network = last_request_unqueued = FALSE; cancelled_requests = 0; uri = soup_uri_new_with_base (base_uri, path); @@ -232,7 +240,8 @@ do_request_with_cancel (SoupSession *session, g_object_unref (stream); g_object_unref (req); return; - } + } else + g_clear_error (&error); g_clear_object (&cancellable); g_clear_object (&stream); @@ -242,8 +251,7 @@ do_request_with_cancel (SoupSession *session, } static void -request_started (SoupSession *session, SoupMessage *msg, - SoupSocket *socket) +message_starting (SoupMessage *msg, gpointer data) { if (soup_message_headers_get_one (msg->request_headers, "If-Modified-Since") || @@ -256,11 +264,21 @@ request_started (SoupSession *session, SoupMessage *msg, } static void +request_queued (SoupSession *session, SoupMessage *msg, + gpointer data) +{ + g_signal_connect (msg, "starting", + G_CALLBACK (message_starting), + data); +} + +static void request_unqueued (SoupSession *session, SoupMessage *msg, gpointer data) { if (msg->status_code == SOUP_STATUS_CANCELLED) cancelled_requests++; + last_request_unqueued = TRUE; } static void @@ -279,8 +297,11 @@ do_basics_test (gconstpointer data) SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, SOUP_SESSION_ADD_FEATURE, cache, NULL); - g_signal_connect (session, "request-started", - G_CALLBACK (request_started), NULL); + + g_signal_connect (session, "request-queued", + G_CALLBACK (request_queued), NULL); + g_signal_connect (session, "request-unqueued", + G_CALLBACK (request_unqueued), NULL); debug_printf (2, " Initial requests\n"); body1 = do_request (session, base_uri, "GET", "/1", NULL, @@ -288,9 +309,11 @@ do_basics_test (gconstpointer data) NULL); body2 = do_request (session, base_uri, "GET", "/2", NULL, "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Cache-Control", "must-revalidate", NULL); body3 = do_request (session, base_uri, "GET", "/3", NULL, "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT", "Test-Set-Cache-Control", "must-revalidate", NULL); body4 = do_request (session, base_uri, "GET", "/4", NULL, @@ -308,6 +331,8 @@ do_basics_test (gconstpointer data) NULL); soup_test_assert (!last_request_hit_network, "Request for /1 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /1 not unqueued"); g_assert_cmpstr (body1, ==, cmp); g_free (cmp); @@ -316,8 +341,13 @@ do_basics_test (gconstpointer data) debug_printf (1, " Heuristically-fresh cached resource\n"); cmp = do_request (session, base_uri, "GET", "/2", NULL, NULL); + /* Not validated even if it has must-revalidate, because it hasn't expired */ + soup_test_assert (!last_request_validated, + "Request for /2 was validated"); soup_test_assert (!last_request_hit_network, "Request for /2 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /2 not unqueued"); g_assert_cmpstr (body2, ==, cmp); g_free (cmp); @@ -328,26 +358,33 @@ do_basics_test (gconstpointer data) NULL); soup_test_assert (last_request_hit_network, "Request for /1?attr=value filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /1?attr=value not unqueued"); g_free (cmp); debug_printf (2, " Second request\n"); cmp = do_request (session, base_uri, "GET", "/1", NULL, NULL); soup_test_assert (!last_request_hit_network, "Second request for /1 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Request for /1 not unqueued"); g_assert_cmpstr (body1, ==, cmp); g_free (cmp); - /* Last-Modified + must-revalidate causes a conditional request */ + /* Expired + must-revalidate causes a conditional request */ debug_printf (1, " Unchanged must-revalidate resource w/ Last-Modified\n"); cmp = do_request (session, base_uri, "GET", "/3", NULL, "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT", "Test-Set-Cache-Control", "must-revalidate", NULL); soup_test_assert (last_request_validated, "Request for /3 not validated"); soup_test_assert (!last_request_hit_network, "Request for /3 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /3 not unqueued"); g_assert_cmpstr (body3, ==, cmp); g_free (cmp); @@ -356,12 +393,15 @@ do_basics_test (gconstpointer data) debug_printf (1, " Changed must-revalidate resource w/ Last-Modified\n"); cmp = do_request (session, base_uri, "GET", "/3", NULL, "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT", + "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT", "Test-Set-Cache-Control", "must-revalidate", NULL); soup_test_assert (last_request_validated, "Request for /3 not validated"); soup_test_assert (last_request_hit_network, "Request for /3 filled from cache"); + soup_test_assert (last_request_unqueued, + "Request for /3 not unqueued"); g_assert_cmpstr (body3, !=, cmp); g_free (cmp); @@ -374,6 +414,8 @@ do_basics_test (gconstpointer data) "Second request for /3 not validated"); soup_test_assert (!last_request_hit_network, "Second request for /3 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /3 not unqueued"); g_assert_cmpstr (body3, !=, cmp); g_free (cmp); @@ -386,6 +428,8 @@ do_basics_test (gconstpointer data) "Request for /4 not validated"); soup_test_assert (!last_request_hit_network, "Request for /4 not filled from cache"); + soup_test_assert (last_request_unqueued, + "Cached resource /4 not unqueued"); g_assert_cmpstr (body4, ==, cmp); g_free (cmp); @@ -397,6 +441,8 @@ do_basics_test (gconstpointer data) NULL); soup_test_assert (last_request_hit_network, "Request for /5 filled from cache"); + soup_test_assert (last_request_unqueued, + "Request for /5 not unqueued"); g_assert_cmpstr (body5, ==, cmp); g_free (cmp); @@ -455,6 +501,7 @@ do_cancel_test (gconstpointer data) NULL); body2 = do_request (session, base_uri, "GET", "/2", NULL, "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Expires", "Fri, 01 Jan 2011 00:00:00 GMT", "Test-Set-Cache-Control", "must-revalidate", NULL); @@ -463,11 +510,15 @@ do_cancel_test (gconstpointer data) flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; do_request_with_cancel (session, base_uri, "GET", "/1", flags); g_assert_cmpint (cancelled_requests, ==, 1); + soup_test_assert (last_request_unqueued, + "Cancelled request /1 not unqueued"); debug_printf (1, " Cancel fresh resource with g_cancellable_cancel()\n"); flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; do_request_with_cancel (session, base_uri, "GET", "/1", flags); g_assert_cmpint (cancelled_requests, ==, 1); + soup_test_assert (last_request_unqueued, + "Cancelled request /1 not unqueued"); soup_test_session_abort_unref (session); @@ -483,11 +534,15 @@ do_cancel_test (gconstpointer data) flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; do_request_with_cancel (session, base_uri, "GET", "/2", flags); g_assert_cmpint (cancelled_requests, ==, 2); + soup_test_assert (last_request_unqueued, + "Cancelled request /2 not unqueued"); debug_printf (1, " Cancel a revalidating resource with g_cancellable_cancel()\n"); flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; do_request_with_cancel (session, base_uri, "GET", "/2", flags); g_assert_cmpint (cancelled_requests, ==, 2); + soup_test_assert (last_request_unqueued, + "Cancelled request /2 not unqueued"); soup_test_session_abort_unref (session); @@ -588,8 +643,8 @@ do_headers_test (gconstpointer data) SOUP_SESSION_ADD_FEATURE, cache, NULL); - g_signal_connect (session, "request-started", - G_CALLBACK (request_started), NULL); + g_signal_connect (session, "request-queued", + G_CALLBACK (request_queued), NULL); debug_printf (2, " Initial requests\n"); body1 = do_request (session, base_uri, "GET", "/1", NULL, @@ -630,6 +685,85 @@ do_headers_test (gconstpointer data) g_free (body1); } +static guint +count_cached_resources_in_dir (const char *cache_dir) +{ + GDir *dir; + const char *name; + guint retval = 0; + + dir = g_dir_open (cache_dir, 0, NULL); + while ((name = g_dir_read_name (dir))) { + if (g_str_has_prefix (name, "soup.")) + continue; + + retval++; + } + g_dir_close (dir); + + return retval; +} + +static void +do_leaks_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupCache *cache; + char *cache_dir; + char *body; + + cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL); + debug_printf (2, " Caching to %s\n", cache_dir); + cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + SOUP_SESSION_ADD_FEATURE, cache, + NULL); + + debug_printf (2, " Initial requests\n"); + body = do_request (session, base_uri, "GET", "/1", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + g_free (body); + body = do_request (session, base_uri, "GET", "/2", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + g_free (body); + body = do_request (session, base_uri, "GET", "/3", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + g_free (body); + + debug_printf (2, " Dumping the cache\n"); + soup_cache_dump (cache); + + g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 3); + + body = do_request (session, base_uri, "GET", "/4", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + g_free (body); + body = do_request (session, base_uri, "GET", "/5", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + g_free (body); + + /* Destroy the cache without dumping the last two resources */ + soup_test_session_abort_unref (session); + g_object_unref (cache); + + cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER); + + debug_printf (2, " Loading the cache\n"); + g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 5); + soup_cache_load (cache); + g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 3); + + g_object_unref (cache); + g_free (cache_dir); +} + int main (int argc, char **argv) { @@ -641,13 +775,13 @@ main (int argc, char **argv) server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/cache/basics", base_uri, do_basics_test); g_test_add_data_func ("/cache/cancellation", base_uri, do_cancel_test); g_test_add_data_func ("/cache/refcounting", base_uri, do_refcounting_test); g_test_add_data_func ("/cache/headers", base_uri, do_headers_test); + g_test_add_data_func ("/cache/leaks", base_uri, do_leaks_test); ret = g_test_run (); diff --git a/tests/chunk-io-test.c b/tests/chunk-io-test.c index 1e53eef1..4746ea6c 100644 --- a/tests/chunk-io-test.c +++ b/tests/chunk-io-test.c @@ -10,7 +10,6 @@ force_io_streams_init (void) { SoupServer *server; SoupSession *session; - guint port; SoupURI *base_uri; SoupMessage *msg; @@ -20,10 +19,7 @@ force_io_streams_init (void) */ server = soup_test_server_new (TRUE); - port = soup_server_get_port (server); - - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); msg = soup_message_new_from_uri ("POST", base_uri); diff --git a/tests/chunk-test.c b/tests/chunk-test.c index 864f8620..c658cbd4 100644 --- a/tests/chunk-test.c +++ b/tests/chunk-test.c @@ -314,7 +314,8 @@ do_temporary_test (void) char *client_md5; const char *server_md5; - g_test_bug ("https://bugs.webkit.org/show_bug.cgi?id=18343"); + g_test_bug_base ("https://bugs.webkit.org/"); + g_test_bug ("18343"); msg = soup_message_new_from_uri ("PUT", base_uri); soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY, @@ -427,21 +428,17 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); loop = g_main_loop_new (NULL, TRUE); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); - + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_test_add_data_func ("/chunks/request/unstreamed", GINT_TO_POINTER (0), do_request_test); diff --git a/tests/coding-test.c b/tests/coding-test.c index 0445f632..de2949be 100644 --- a/tests/coding-test.c +++ b/tests/coding-test.c @@ -486,10 +486,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add ("/coding/message/plain", CodingTestData, GINT_TO_POINTER (CODING_TEST_NO_DECODER), diff --git a/tests/connection-test.c b/tests/connection-test.c index 8f8c74be..ec54daea 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -51,16 +51,21 @@ timeout_request_started (SoupServer *server, SoupMessage *msg, SoupClientContext *client, gpointer user_data) { SoupSocket *sock; - GMainContext *context = soup_server_get_async_context (server); + GMainContext *context = g_main_context_get_thread_default (); guint readable; + g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (client); + G_GNUC_END_IGNORE_DEPRECATIONS; readable = g_signal_connect (sock, "readable", G_CALLBACK (timeout_socket), NULL); + + g_mutex_unlock (&server_mutex); while (soup_socket_is_connected (sock)) g_main_context_iteration (context, TRUE); g_signal_handler_disconnect (sock, readable); - g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); } static void @@ -78,17 +83,18 @@ setup_timeout_persistent (SoupServer *server, SoupSocket *sock) * fail (since the client is waiting for us to * return a response). This will cause it to * emit "readable" later. - * 2. Connect to the server's request-started signal. - * 3. Run an inner main loop from that signal handler - * until the socket emits "readable". (If we don't - * do this then it's possible the client's next - * request would be ready before we returned to - * the main loop, and so the signal would never be - * emitted.) + * 2. Wait for the server to finish this request and + * start reading the next one (and lock server_mutex + * to interlock with the client and ensure that it + * doesn't start writing its next request until + * that point). + * 3. Block until "readable" is emitted, meaning the + * client has written its request. * 4. Close the socket. */ soup_socket_read (sock, buf, 1, &nread, NULL, NULL); + g_mutex_lock (&server_mutex); g_signal_connect (server, "request-started", G_CALLBACK (timeout_request_started), NULL); } @@ -130,7 +136,9 @@ server_callback (SoupServer *server, SoupMessage *msg, * the declared Content-Length. Instead, we * forcibly close the socket at that point. */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; g_signal_connect (msg, "wrote-chunk", G_CALLBACK (close_socket), sock); } else if (no_close) { @@ -148,7 +156,9 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/timeout-persistent")) { SoupSocket *sock; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; setup_timeout_persistent (server, sock); } @@ -250,6 +260,12 @@ do_timeout_test_for_session (SoupSession *session) } g_object_unref (msg); + /* The server will grab server_mutex before returning the response, + * and release it when it's ready for us to send the second request. + */ + g_mutex_lock (&server_mutex); + g_mutex_unlock (&server_mutex); + debug_printf (1, " Second message\n"); msg = soup_message_new_from_uri ("GET", base_uri); soup_session_send_message (session, msg); @@ -314,6 +330,12 @@ do_timeout_req_test_for_session (SoupSession *session) } g_object_unref (req); + /* The server will grab server_mutex before returning the response, + * and release it when it's ready for us to send the second request. + */ + g_mutex_lock (&server_mutex); + g_mutex_unlock (&server_mutex); + debug_printf (1, " Second request\n"); req = soup_session_request_uri (session, base_uri, NULL); @@ -382,7 +404,7 @@ static GMainLoop *max_conns_loop; static int msgs_done; static guint quit_loop_timeout; #define MAX_CONNS 2 -#define TEST_CONNS (MAX_CONNS * 2) +#define TEST_CONNS (MAX_CONNS * 2) + 1 static gboolean idle_start_server (gpointer data) @@ -403,7 +425,7 @@ static void max_conns_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) { - if (++msgs_done == MAX_CONNS) { + if (++msgs_done >= MAX_CONNS) { if (quit_loop_timeout) g_source_remove (quit_loop_timeout); quit_loop_timeout = g_timeout_add (100, quit_loop, NULL); @@ -420,7 +442,8 @@ max_conns_message_complete (SoupSession *session, SoupMessage *msg, gpointer use static void do_max_conns_test_for_session (SoupSession *session) { - SoupMessage *msgs[TEST_CONNS]; + SoupMessage *msgs[TEST_CONNS + 1]; + SoupMessageFlags flags; int i; max_conns_loop = g_main_loop_new (NULL, TRUE); @@ -430,7 +453,7 @@ do_max_conns_test_for_session (SoupSession *session) g_signal_connect (session, "request-started", G_CALLBACK (max_conns_request_started), NULL); msgs_done = 0; - for (i = 0; i < TEST_CONNS; i++) { + for (i = 0; i < TEST_CONNS - 1; i++) { msgs[i] = soup_message_new_from_uri ("GET", base_uri); g_object_ref (msgs[i]); soup_session_queue_message (session, msgs[i], @@ -439,6 +462,21 @@ do_max_conns_test_for_session (SoupSession *session) g_main_loop_run (max_conns_loop); g_assert_cmpint (msgs_done, ==, MAX_CONNS); + + if (quit_loop_timeout) + g_source_remove (quit_loop_timeout); + quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL); + + /* Message with SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS should start */ + msgs[i] = soup_message_new_from_uri ("GET", base_uri); + flags = soup_message_get_flags (msgs[i]); + soup_message_set_flags (msgs[i], flags | SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS); + g_object_ref (msgs[i]); + soup_session_queue_message (session, msgs[i], + max_conns_message_complete, NULL); + + g_main_loop_run (max_conns_loop); + g_assert_cmpint (msgs_done, ==, MAX_CONNS + 1); g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL); msgs_done = 0; @@ -762,11 +800,46 @@ network_event (SoupMessage *msg, GSocketClientEvent event, { const char **events = user_data; - debug_printf (2, " %s\n", event_name_from_abbrev (**events)); + debug_printf (2, " %s\n", event_names[event]); soup_test_assert (**events == event_abbrevs[event], - "Unexpected event: %s (expected %s)\n", + "Unexpected event: %s (expected %s)", event_names[event], event_name_from_abbrev (**events)); + + if (**events == event_abbrevs[event]) { + if (event == G_SOCKET_CLIENT_RESOLVING || + event == G_SOCKET_CLIENT_RESOLVED) { + soup_test_assert (connection == NULL, + "Unexpectedly got connection (%s) with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event < G_SOCKET_CLIENT_TLS_HANDSHAKING) { + soup_test_assert (G_IS_SOCKET_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING || + event == G_SOCKET_CLIENT_TLS_HANDSHAKED) { + soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event == G_SOCKET_CLIENT_COMPLETE) { + /* See if the previous expected event was TLS_HANDSHAKED */ + if ((*events)[-1] == 'T') { + soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else { + soup_test_assert (G_IS_SOCKET_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } + } + } + *events = *events + 1; } @@ -841,6 +914,186 @@ do_connection_event_test (void) soup_test_session_abort_unref (session); } +typedef struct { + GMainLoop *loop; + GIOStream *stream; + GError *error; + const char *events; +} ConnectTestData; + +static void +connect_progress (SoupSession *session, GSocketClientEvent event, GIOStream *connection, ConnectTestData *data) +{ + soup_test_assert (*data->events == event_abbrevs[event], + "Unexpected event: %s (expected %s)", + event_names[event], + event_name_from_abbrev (*data->events)); + data->events = data->events + 1; +} + +static void +connect_finished (SoupSession *session, GAsyncResult *result, ConnectTestData *data) +{ + data->stream = soup_session_connect_finish (session, result, &data->error); + g_main_loop_quit (data->loop); +} + +static void +do_one_connection_connect_test (SoupSession *session, SoupURI *uri, const char *response, const char *events) +{ + ConnectTestData data = { NULL, NULL, NULL, events }; + static const char *request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + gsize bytes = 0; + char buffer[128]; + + data.loop = g_main_loop_new (NULL, FALSE); + soup_session_connect_async (session, uri, NULL, + (SoupSessionConnectProgressCallback)connect_progress, + (GAsyncReadyCallback)connect_finished, + &data); + g_main_loop_run (data.loop); + + g_assert (G_IS_IO_STREAM (data.stream)); + g_assert_no_error (data.error); + g_assert (g_output_stream_write_all (g_io_stream_get_output_stream (data.stream), + request, strlen (request), &bytes, NULL, NULL)); + g_assert (g_input_stream_read_all (g_io_stream_get_input_stream (data.stream), + buffer, sizeof (buffer), &bytes, NULL, NULL)); + buffer[strlen (response)] = '\0'; + g_assert_cmpstr (buffer, ==, response); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + g_object_unref (data.stream); + g_main_loop_unref (data.loop); +} + +static void +do_one_connection_connect_fail_test (SoupSession *session, SoupURI *uri, GQuark domain, gint code, const char *events) +{ + ConnectTestData data = { NULL, NULL, NULL, events }; + + data.loop = g_main_loop_new (NULL, FALSE); + soup_session_connect_async (session, uri, NULL, + (SoupSessionConnectProgressCallback)connect_progress, + (GAsyncReadyCallback)connect_finished, + &data); + g_main_loop_run (data.loop); + + g_assert (!data.stream); + g_assert_error (data.error, domain, code); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } +} + +static void +do_connection_connect_test (void) +{ + SoupSession *session; + SoupURI *http_uri; + SoupURI *https_uri = NULL; + SoupURI *ws_uri; + SoupURI *wss_uri = NULL; + SoupURI *file_uri; + SoupURI *wrong_http_uri; + SoupURI *proxy_uri; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + + debug_printf (1, " http\n"); + http_uri = soup_uri_new (HTTP_SERVER); + do_one_connection_connect_test (session, http_uri, + "HTTP/1.1 200 OK", "rRcCx"); + + if (tls_available) { + debug_printf (1, " https\n"); + https_uri = soup_uri_new (HTTPS_SERVER); + do_one_connection_connect_test (session, https_uri, + "HTTP/1.1 200 OK", "rRcCtTx"); + } else + debug_printf (1, " https -- SKIPPING\n"); + + debug_printf (1, " ws\n"); + ws_uri = soup_uri_new (HTTP_SERVER); + ws_uri->scheme = SOUP_URI_SCHEME_WS; + do_one_connection_connect_test (session, ws_uri, + "HTTP/1.1 200 OK", "rRcCx"); + + if (tls_available) { + debug_printf (1, " wss\n"); + wss_uri = soup_uri_new (HTTPS_SERVER); + do_one_connection_connect_test (session, wss_uri, + "HTTP/1.1 200 OK", "rRcCtTx"); + } else + debug_printf (1, " wss -- SKIPPING\n"); + + debug_printf (1, " file\n"); + file_uri = soup_uri_new ("file:///foo/bar"); + do_one_connection_connect_fail_test (session, file_uri, + G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, + "r"); + + debug_printf (1, " wrong http (invalid port)\n"); + wrong_http_uri = soup_uri_new (HTTP_SERVER); + wrong_http_uri->port = 1234; + do_one_connection_connect_fail_test (session, wrong_http_uri, + G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, + "rRcr"); /* FIXME: why r again? GLib bug? */ + + proxy_uri = soup_uri_new (HTTP_PROXY); + g_object_set (G_OBJECT (session), + SOUP_SESSION_PROXY_URI, proxy_uri, + NULL); + + debug_printf (1, " http with proxy\n"); + do_one_connection_connect_test (session, http_uri, + "HTTP/1.1 403 Forbidden", "rRcCx"); + + if (tls_available) { + debug_printf (1, " https with proxy\n"); + do_one_connection_connect_test (session, https_uri, + "HTTP/1.1 200 OK", "rRcCpPtTx"); + } else + debug_printf (1, " https with proxy -- SKIPPING\n"); + + debug_printf (1, " ws with proxy\n"); + do_one_connection_connect_test (session, ws_uri, + "HTTP/1.1 403 Forbidden", "rRcCx"); + + if (tls_available) { + debug_printf (1, " wss with proxy\n"); + do_one_connection_connect_test (session, wss_uri, + "HTTP/1.1 200 OK", "rRcCpPtTx"); + } else + debug_printf (1, " wss with proxy -- SKIPPING\n"); + + soup_uri_free (http_uri); + if (https_uri) + soup_uri_free (https_uri); + soup_uri_free (ws_uri); + if (wss_uri) + soup_uri_free (wss_uri); + soup_uri_free (file_uri); + soup_uri_free (wrong_http_uri); + soup_uri_free (proxy_uri); + + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -849,10 +1102,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); apache_init (); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, "http", NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test); g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test); @@ -861,6 +1113,7 @@ main (int argc, char **argv) g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test); g_test_add_func ("/connection/state", do_connection_state_test); g_test_add_func ("/connection/event", do_connection_event_test); + g_test_add_func ("/connection/connect", do_connection_connect_test); ret = g_test_run (); diff --git a/tests/context-test.c b/tests/context-test.c index 727c63ba..567512fe 100644 --- a/tests/context-test.c +++ b/tests/context-test.c @@ -14,12 +14,12 @@ typedef struct { } SlowData; static void -request_failed (SoupMessage *msg, gpointer data) +request_finished (SoupMessage *msg, gpointer data) { SlowData *sd = data; - if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) - g_source_destroy (sd->timeout); + g_source_destroy (sd->timeout); + g_source_unref (sd->timeout); g_free (sd); } @@ -65,10 +65,11 @@ server_callback (SoupServer *server, SoupMessage *msg, sd->server = server; sd->msg = msg; sd->timeout = soup_add_timeout ( - soup_server_get_async_context (server), + g_main_context_get_thread_default (), 200, add_body_chunk, sd); + g_source_ref (sd->timeout); g_signal_connect (msg, "finished", - G_CALLBACK (request_failed), sd); + G_CALLBACK (request_finished), sd); } /* Test 1: An async session in another thread with its own @@ -329,14 +330,16 @@ int main (int argc, char **argv) { SoupServer *server; + SoupURI *uri; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + base_uri = soup_uri_to_string (uri, FALSE); + soup_uri_free (uri); g_test_add_data_func ("/context/blocking/explicit", GINT_TO_POINTER (FALSE), do_test1); g_test_add_data_func ("/context/blocking/thread-default", GINT_TO_POINTER (TRUE), do_test1); diff --git a/tests/continue-test.c b/tests/continue-test.c index b6a5805f..5e0c6603 100644 --- a/tests/continue-test.c +++ b/tests/continue-test.c @@ -10,7 +10,7 @@ #define MAX_POST_LENGTH (sizeof (SHORT_BODY)) -static int port; +static SoupURI *base_uri; static GSList *events; static void @@ -54,17 +54,20 @@ do_message (const char *path, gboolean long_body, SoupSession *session; SoupMessage *msg; const char *body; - char *uri; + SoupURI *uri; va_list ap; const char *expected_event; char *actual_event; int expected_status, actual_status; - uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s", - auth ? "user:pass@" : "", - port, path); - msg = soup_message_new ("POST", uri); - g_free (uri); + uri = soup_uri_copy (base_uri); + if (auth) { + soup_uri_set_user (uri, "user"); + soup_uri_set_password (uri, "pass"); + } + soup_uri_set_path (uri, path); + msg = soup_message_new_from_uri ("POST", uri); + soup_uri_free (uri); body = long_body ? LONG_BODY : SHORT_BODY; soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC, @@ -148,7 +151,7 @@ do_message (const char *path, gboolean long_body, static void do_test_unauth_short_noexpect_nopass (void) { - do_message ("unauth", FALSE, FALSE, FALSE, + do_message ("/unauth", FALSE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -165,7 +168,7 @@ do_test_unauth_short_noexpect_nopass (void) static void do_test_unauth_long_noexpect_nopass (void) { - do_message ("unauth", TRUE, FALSE, FALSE, + do_message ("/unauth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -182,7 +185,7 @@ do_test_unauth_long_noexpect_nopass (void) static void do_test_unauth_short_expect_nopass (void) { - do_message ("unauth", FALSE, TRUE, FALSE, + do_message ("/unauth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_informational", SOUP_STATUS_CONTINUE, @@ -201,7 +204,7 @@ do_test_unauth_short_expect_nopass (void) static void do_test_unauth_long_expect_nopass (void) { - do_message ("unauth", TRUE, TRUE, FALSE, + do_message ("/unauth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE, @@ -216,7 +219,7 @@ do_test_unauth_long_expect_nopass (void) static void do_test_auth_short_noexpect_nopass (void) { - do_message ("auth", FALSE, FALSE, FALSE, + do_message ("/auth", FALSE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -233,7 +236,7 @@ do_test_auth_short_noexpect_nopass (void) static void do_test_auth_long_noexpect_nopass (void) { - do_message ("auth", TRUE, FALSE, FALSE, + do_message ("/auth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -250,7 +253,7 @@ do_test_auth_long_noexpect_nopass (void) static void do_test_auth_short_expect_nopass (void) { - do_message ("auth", FALSE, TRUE, FALSE, + do_message ("/auth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -265,7 +268,7 @@ do_test_auth_short_expect_nopass (void) static void do_test_auth_long_expect_nopass (void) { - do_message ("auth", TRUE, TRUE, FALSE, + do_message ("/auth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -280,7 +283,7 @@ do_test_auth_long_expect_nopass (void) static void do_test_auth_short_noexpect_pass (void) { - do_message ("auth", FALSE, FALSE, TRUE, + do_message ("/auth", FALSE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -306,7 +309,7 @@ do_test_auth_short_noexpect_pass (void) static void do_test_auth_long_noexpect_pass (void) { - do_message ("auth", TRUE, FALSE, TRUE, + do_message ("/auth", TRUE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -332,7 +335,7 @@ do_test_auth_long_noexpect_pass (void) static void do_test_auth_short_expect_pass (void) { - do_message ("auth", FALSE, TRUE, TRUE, + do_message ("/auth", FALSE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -358,7 +361,7 @@ do_test_auth_short_expect_pass (void) static void do_test_auth_long_expect_pass (void) { - do_message ("auth", TRUE, TRUE, TRUE, + do_message ("/auth", TRUE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -452,7 +455,7 @@ setup_server (void) SoupServer *server; SoupAuthDomain *auth_domain; - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); g_signal_connect (server, "request-started", G_CALLBACK (request_started), NULL); @@ -481,7 +484,7 @@ main (int argc, char **argv) test_init (argc, argv, NULL); server = setup_server (); - port = soup_server_get_port (server); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_func ("/continue/unauth_short_noexpect_nopass", do_test_unauth_short_noexpect_nopass); g_test_add_func ("/continue/unauth_long_noexpect_nopass", do_test_unauth_long_noexpect_nopass); @@ -499,6 +502,8 @@ main (int argc, char **argv) ret = g_test_run (); soup_test_server_quit_unref (server); + soup_uri_free (base_uri); + test_cleanup (); return ret; diff --git a/tests/cookies-test.c b/tests/cookies-test.c index 12529d81..8735964c 100644 --- a/tests/cookies-test.c +++ b/tests/cookies-test.c @@ -94,6 +94,61 @@ do_cookies_accept_policy_test (void) soup_test_session_abort_unref (session); } +static void +do_cookies_subdomain_policy_test (void) +{ + SoupCookieJar *jar; + GSList *cookies; + SoupURI *uri1; + SoupURI *uri2; + + g_test_bug ("792130"); + + /* Only the base domain should be considered when deciding + * whether a cookie is a third-party cookie. + */ + uri1 = soup_uri_new ("https://www.gnome.org"); + uri2 = soup_uri_new ("https://foundation.gnome.org"); + + /* We can't check subdomains with a test server running on + * localhost, so we'll just check the cookie jar API itself. + */ + + /* Cookie should be accepted. One cookie in the jar. */ + jar = soup_cookie_jar_new (); + soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + soup_cookie_jar_set_cookie_with_first_party (jar, uri1, uri2, "1=foo"); + cookies = soup_cookie_jar_all_cookies (jar); + g_assert_cmpint (g_slist_length (cookies), ==, 1); + g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free); + + /* Cookie should be accepted. Two cookies in the jar. */ + soup_cookie_jar_set_cookie_with_first_party (jar, uri2, uri1, "2=foo"); + cookies = soup_cookie_jar_all_cookies (jar); + g_assert_cmpint (g_slist_length (cookies), ==, 2); + g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free); + + /* Third-party cookie should be rejected, so there are still + * only two cookies in the jar. + */ + soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, uri1, "3=foo"); + cookies = soup_cookie_jar_all_cookies (jar); + g_assert_cmpint (g_slist_length (cookies), ==, 2); + g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free); + + /* A leading dot in the domain property should not affect things. + * This cookie should be accepted. Three cookies in the jar. + */ + soup_cookie_jar_set_cookie_with_first_party (jar, uri1, uri1, "4=foo; Domain=.www.gnome.org"); + cookies = soup_cookie_jar_all_cookies (jar); + g_assert_cmpint (g_slist_length (cookies), ==, 3); + g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free); + + soup_uri_free (uri1); + soup_uri_free (uri2); + g_object_unref (jar); +} + /* FIXME: moar tests! */ static void do_cookies_parsing_test (void) @@ -167,24 +222,29 @@ do_cookies_parsing_test (void) int main (int argc, char **argv) { + SoupURI *server_uri; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + server_uri = soup_test_server_get_uri (server, "http", NULL); + first_party_uri = soup_uri_new (first_party); third_party_uri = soup_uri_new (third_party); - soup_uri_set_port (first_party_uri, soup_server_get_port (server)); - soup_uri_set_port (third_party_uri, soup_server_get_port (server)); + soup_uri_set_port (first_party_uri, server_uri->port); + soup_uri_set_port (third_party_uri, server_uri->port); g_test_add_func ("/cookies/accept-policy", do_cookies_accept_policy_test); + g_test_add_func ("/cookies/accept-policy-subdomains", do_cookies_subdomain_policy_test); g_test_add_func ("/cookies/parsing", do_cookies_parsing_test); ret = g_test_run (); soup_uri_free (first_party_uri); soup_uri_free (third_party_uri); + soup_uri_free (server_uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/date.c b/tests/date.c index e51fa57f..f623061b 100644 --- a/tests/date.c +++ b/tests/date.c @@ -102,6 +102,9 @@ static const OkDate ok_dates[] = { { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, { "Saturday, 06-Nov-104 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-2004 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, { "Saturday, 06-Nov-04 08:09:07", NULL }, { "06-Nov-04 08:09:07 GMT", NULL }, @@ -130,16 +133,6 @@ static const OkDate ok_dates[] = { { "Sat, 06-Nov-104 08:09:07 GMT", NULL }, { "Sat, 06-Nov-04 08:09:07", NULL }, - /* Netscape cookie spec example syntax, and broken variants */ - { "Saturday, 06-Nov-04 08:09:07 GMT", NULL }, - { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, - { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, - { "Saturday, 06-Nov-104 08:09:07 GMT", NULL }, - { "Saturday, 06-Nov-2004 08:09:07 GMT", NULL }, - { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, - { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, - { "Saturday, 06-Nov-04 08:09:07", NULL }, - /* Miscellaneous broken formats seen on the web */ { "Sat 06-Nov-2004 08:9:07", NULL }, { "Saturday, 06-Nov-04 8:9:07 GMT", NULL }, @@ -177,6 +170,8 @@ check_ok_time_t (void) g_assert_cmpint (date->hour, ==, 8); g_assert_cmpint (date->minute, ==, 9); g_assert_cmpint (date->second, ==, 7); + + soup_date_free (date); } typedef struct { diff --git a/tests/forms-test.c b/tests/forms-test.c index 3915b019..349932bf 100644 --- a/tests/forms-test.c +++ b/tests/forms-test.c @@ -420,41 +420,43 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; - char *uri_str; + SoupURI *base_uri, *uri; int ret = 0; test_init (argc, argv, no_test_entry); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, "/hello", hello_callback, NULL, NULL); soup_server_add_handler (server, "/md5", md5_callback, NULL, NULL); - port = soup_server_get_port (server); + base_uri = soup_test_server_get_uri (server, "http", NULL); loop = g_main_loop_new (NULL, TRUE); if (run_tests) { - uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port); - g_test_add_data_func_full ("/forms/hello", uri_str, do_hello_tests, g_free); + uri = soup_uri_new_with_base (base_uri, "/hello"); + g_test_add_data_func_full ("/forms/hello", soup_uri_to_string (uri, FALSE), do_hello_tests, g_free); + soup_uri_free (uri); - uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port); - g_test_add_data_func_full ("/forms/md5/curl", g_strdup (uri_str), do_md5_test_curl, g_free); - g_test_add_data_func_full ("/forms/md5/libsoup", g_strdup (uri_str), do_md5_test_libsoup, g_free); - g_free (uri_str); + uri = soup_uri_new_with_base (base_uri, "/md5"); + g_test_add_data_func_full ("/forms/md5/curl", soup_uri_to_string (uri, FALSE), do_md5_test_curl, g_free); + g_test_add_data_func_full ("/forms/md5/libsoup", soup_uri_to_string (uri, FALSE), do_md5_test_libsoup, g_free); + soup_uri_free (uri); g_test_add_func ("/forms/decode", do_form_decode_test); ret = g_test_run (); } else { - g_print ("Listening on port %d\n", port); + g_print ("Listening on port %d\n", base_uri->port); g_main_loop_run (loop); } g_main_loop_unref (loop); soup_test_server_quit_unref (server); + soup_uri_free (base_uri); + if (run_tests) test_cleanup (); return ret; diff --git a/tests/header-parsing.c b/tests/header-parsing.c index fdc7885d..9cf06cee 100644 --- a/tests/header-parsing.c +++ b/tests/header-parsing.c @@ -358,6 +358,24 @@ static struct RequestTest { } }, + { "NUL in header name", "760832", + "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36, + SOUP_STATUS_OK, + "GET", "/", SOUP_HTTP_1_1, + { { "Host", "example.com" }, + { NULL } + } + }, + + { "NUL in header value", "760832", + "GET / HTTP/1.1\r\nHost: example\x00" "com\r\n", 35, + SOUP_STATUS_OK, + "GET", "/", SOUP_HTTP_1_1, + { { "Host", "examplecom" }, + { NULL } + } + }, + /************************/ /*** INVALID REQUESTS ***/ /************************/ @@ -418,20 +436,6 @@ static struct RequestTest { { { NULL } } }, - { "NUL in header name", "666316", - "GET / HTTP/1.1\r\n\x00: silly\r\n", 37, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, - { { NULL } } - }, - - { "NUL in header value", NULL, - "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, - { { NULL } } - }, - { "No terminating CRLF", NULL, "GET / HTTP/1.1\r\nHost: example.com", -1, SOUP_STATUS_BAD_REQUEST, @@ -513,6 +517,14 @@ static struct ResponseTest { } }, + { "Response w/ unknown status code", NULL, + "HTTP/1.1 999 Request denied\r\nFoo: bar\r\n", -1, + SOUP_HTTP_1_1, 999, "Request denied", + { { "Foo", "bar" }, + { NULL } + } + }, + { "Connection header on HTTP/1.0 message", NULL, "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1, SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok", @@ -608,6 +620,46 @@ static struct ResponseTest { { NULL } } }, + { "NUL in header name", "760832", + "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28, + SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK", + { { "Foo", "bar" }, + { NULL } + } + }, + + { "NUL in header value", "760832", + "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28, + SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK", + { { "Foo", "bar" }, + { NULL } + } + }, + + /********************************/ + /*** VALID CONTINUE RESPONSES ***/ + /********************************/ + + /* Tests from Cockpit project */ + + { "Response w/ 101 Switching Protocols + spaces after new line", NULL, + "HTTP/1.0 101 Switching Protocols\r\n \r\n", 38, + SOUP_HTTP_1_0, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols", + { { NULL } } + }, + + { "Response w/ 101 Switching Protocols missing \\r + spaces", NULL, + "HTTP/1.0 101 Switching Protocols\r\n \r\n", 40, + SOUP_HTTP_1_0, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols", + { { NULL } } + }, + + { "Response w/ 101 Switching Protocols + spaces after & before new line", NULL, + "HTTP/1.1 101 Switching Protocols \r\n \r\n", 42, + SOUP_HTTP_1_1, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols", + { { NULL } } + }, + /*************************/ /*** INVALID RESPONSES ***/ /*************************/ @@ -660,8 +712,8 @@ static struct ResponseTest { { { NULL } } }, - { "Status code > 599", NULL, - "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1, + { "Status code > 999", NULL, + "HTTP/1.1 1000 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, @@ -678,17 +730,44 @@ static struct ResponseTest { { { NULL } } }, - { "NUL in header name", NULL, - "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28, + /* Failing test from Cockpit */ + + { "Partial response stops after HTTP/", NULL, + "HTTP/", -1, -1, 0, NULL, { { NULL } } }, - { "NUL in header value", NULL, - "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28, + { "Space before HTTP/", NULL, + " HTTP/1.0 101 Switching Protocols\r\n ", -1, -1, 0, NULL, { { NULL } } }, + + { "Missing reason", NULL, + "HTTP/1.0 101\r\n ", -1, + -1, 0, NULL, + { { NULL } } + }, + + { "Response code containing alphabetic character", NULL, + "HTTP/1.1 1A01 Switching Protocols \r\n ", -1, + -1, 0, NULL, + { { NULL } } + }, + + { "TESTONE\\r\\n", NULL, + "TESTONE\r\n ", -1, + -1, 0, NULL, + { { NULL } } + }, + + { "Response w/ 3 headers truncated", NULL, + "HTTP/1.0 200 ok\r\nHeader1: value3\r\nHeader2: field\r\nHead3: Anothe", -1, + -1, 0, NULL, + { { NULL } + } + }, }; static const int num_resptests = G_N_ELEMENTS (resptests); @@ -837,6 +916,17 @@ do_qvalue_tests (void) debug_printf (1, " acceptable: "); if (acceptable) { + /* Kludge to deal with the fact that the sort order of the first + * test is not fully specified. + */ + if (i == 0 && acceptable->next && + !g_str_equal (acceptable->data, qvaluetests[i].acceptable[0]) && + g_str_equal (acceptable->data, qvaluetests[i].acceptable[1])) { + gpointer tmp = acceptable->data; + acceptable->data = acceptable->next->data; + acceptable->next->data = tmp; + } + for (iter = acceptable, j = 0; iter; iter = iter->next, j++) { debug_printf (1, "%s ", (char *)iter->data); g_assert_cmpstr (iter->data, ==, qvaluetests[i].acceptable[j]); diff --git a/tests/httpd.conf.22.in b/tests/httpd.conf.22.in deleted file mode 100644 index b912ca36..00000000 --- a/tests/httpd.conf.22.in +++ /dev/null @@ -1,292 +0,0 @@ -# http.conf used for testing auth-test - -ServerName 127.0.0.1 -Listen 127.0.0.1:47524 - -DocumentRoot . - -# 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_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@/@APACHE_PHP_MODULE@ -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 index.txt -TypesConfig /dev/null -AddType application/x-httpd-php .php -Redirect permanent /redirected /index.txt - -# 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*> - 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 . -Alias /Basic/realm1/realm2 . -Alias /Basic/realm1/subdir . -Alias /Basic/realm1/not . -Alias /Basic/realm1 . -Alias /Basic/realm12/subdir . -Alias /Basic/realm12 . -Alias /Basic/realm2 . -Alias /Basic/realm3 . -Alias /Basic . - -<Location /Basic/realm1> - AuthType Basic - AuthName realm1 - AuthUserFile ./htpasswd - Require user user1 -</Location> - -<Location /Basic/realm1/not> - AuthType Basic - AuthName realm1 - AuthUserFile ./htpasswd - Require user user2 -</Location> - -<Location /Basic/realm12> - AuthType Basic - AuthName realm12 - AuthUserFile ./htpasswd - Require user user1 user2 -</Location> - -<Location /Basic/realm1/realm2> - AuthType Basic - AuthName realm2 - AuthUserFile ./htpasswd - Require user user2 -</Location> - -<Location /Basic/realm1/realm2/realm1> - AuthType Basic - AuthName realm1 - AuthUserFile ./htpasswd - Require user user1 -</Location> - -<Location /Basic/realm2> - AuthType Basic - AuthName realm2 - AuthUserFile ./htpasswd - Require user user2 -</Location> - -<Location /Basic/realm3> - AuthType Basic - AuthName realm3 - AuthUserFile ./htpasswd - Require user user3 -</Location> - -# Digest auth tests -Alias /Digest/realm1/realm2/realm1 . -Alias /Digest/realm1/realm2 . -Alias /Digest/realm1/subdir . -Alias /Digest/realm1/expire . -Alias /Digest/realm1/not . -Alias /Digest/realm1 . -Alias /Digest/realm2 . -Alias /Digest/realm3 . -Alias /Digest . - -<Location /Digest/realm1> - AuthType Digest - AuthName realm1 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 - Require valid-user -</Location> - -<Location /Digest/realm1/expire> - AuthType Digest - AuthName realm1 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 - AuthDigestNonceLifetime 2 - Require valid-user -</Location> - -<Location /Digest/realm1/not> - AuthType Digest - AuthName realm1 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 - Require user user2 -</Location> - -<Location /Digest/realm1/realm2> - AuthType Digest - AuthName realm2 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 - Require valid-user -</Location> - -<Location /Digest/realm1/realm2/realm1> - AuthType Digest - AuthName realm1 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 - Require valid-user -</Location> - -<Location /Digest/realm2> - AuthType Digest - AuthName realm2 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 - Require valid-user -</Location> - -<Location /Digest/realm3> - AuthType Digest - AuthName realm3 - AuthUserFile ./htdigest - AuthDigestDomain /Digest/realm3 - Require valid-user - # test RFC2069-style Digest - AuthDigestQop none -</Location> diff --git a/tests/httpd.conf.24.in b/tests/httpd.conf.in index 850b8393..b818c12d 100644 --- a/tests/httpd.conf.24.in +++ b/tests/httpd.conf.in @@ -24,12 +24,12 @@ 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@/@APACHE_PHP_MODULE@ +@IF_HAVE_PHP@LoadModule php7_module @APACHE_PHP_MODULE_DIR@/libphp7.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 -LoadModule unixd_module @APACHE_SSL_MODULE_DIR@/mod_unixd.so +@IF_HAVE_MOD_UNIXD@LoadModule unixd_module @APACHE_SSL_MODULE_DIR@/mod_unixd.so DirectoryIndex index.txt TypesConfig /dev/null @@ -155,6 +155,7 @@ Alias /Basic/realm12 . Alias /Basic/realm2 . Alias /Basic/realm3 . Alias /Basic . +Alias /BasicRoot . <Location /Basic/realm1> AuthType Basic @@ -205,6 +206,13 @@ Alias /Basic . Require user user3 </Location> +<Location /BasicRoot> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user user1 +</Location> + # Digest auth tests Alias /Digest/realm1/realm2/realm1 . Alias /Digest/realm1/realm2 . diff --git a/tests/libsoup.supp b/tests/libsoup.supp index ae8bda38..2c7ba9b5 100644 --- a/tests/libsoup.supp +++ b/tests/libsoup.supp @@ -208,13 +208,20 @@ glib/tlsdb Memcheck:Leak ... - fun:g_tls_backend_get_default_database + fun:g_tls_backend_gnutls_get_default_database +} +{ + glib/tlsdb_dummy + Memcheck:Leak + ... + fun:g_dummy_tls_backend_get_default_database } { glib/tlscache Memcheck:Leak ... - fun:g_tls_backend_gnutls_cache_session_data + fun:g_bytes_new_with_free_func + fun:g_tls_client_connection_gnutls_constructed } { glib/tlspriority @@ -230,6 +237,12 @@ fun:lookup_attribute } { + glib/gfileinfo2 + Memcheck:Leak + ... + fun:_lookup_namespace +} +{ glib/unixsignalthread Memcheck:Leak ... @@ -312,7 +325,7 @@ glib/unused_thread_queue Memcheck:Leak ... - fun:g_async_queue_new + fun:g_async_queue_new_full fun:g_thread_pool_new } { @@ -336,6 +349,18 @@ fun:g_get_charset } { + glib/tmpdir + Memcheck:Leak + ... + fun:g_get_tmp_dir +} +{ + glib/g_get_user_name + Memcheck:Leak + ... + fun:g_get_user_name +} +{ glib/gtlssessioncache Memcheck:Leak ... @@ -414,6 +439,30 @@ fun:g_thread_pool_new fun:g_task_thread_pool_init } +{ + glib/resources + Memcheck:Leak + ... + fun:g_resource_load +} +{ + glib/resources2 + Memcheck:Leak + ... + fun:g_resources_register +} +{ + glib/worker + Memcheck:Leak + ... + fun:g_get_worker_context +} +{ + glib/worker2 + Memcheck:Leak + ... + fun:glib_worker_main +} # probably inlines the aggressive memcpy/memcmp { @@ -444,10 +493,16 @@ } { - libxml2/xmlInitParser + libxml2/xmlInitGlobals + Memcheck:Leak + ... + fun:xmlInitGlobals +} +{ + libxml2/xmlInitMemory Memcheck:Leak ... - fun:xmlInitParser + fun:xmlInitMemory } { libxml2/xmlInitParserCtxt @@ -459,7 +514,7 @@ libxml2/xmlInitializeDict Memcheck:Leak ... - fun:xmlInitializeDict + fun:__xmlInitializeDict } { libxml2/xmlInitCharEncodingHandlers @@ -504,6 +559,20 @@ fun:g_mutex_get_impl } { + glib/grecmuteximpl + Memcheck:Leak + ... + fun:g_rec_mutex_impl_new + fun:g_rec_mutex_get_impl +} +{ + glib/grwlockimpl + Memcheck:Leak + ... + fun:g_rw_lock_impl_new + fun:g_rw_lock_get_impl +} +{ glib/gcondimpl Memcheck:Leak ... @@ -517,3 +586,10 @@ fun:g_private_impl_new fun:g_private_get_impl } +{ + glib/test_uri_base + Memcheck:Leak + ... + fun:g_strdup + fun:test_case_run +}
\ No newline at end of file diff --git a/tests/misc-test.c b/tests/misc-test.c index 00559a03..8cbda80a 100644 --- a/tests/misc-test.c +++ b/tests/misc-test.c @@ -73,7 +73,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/slow")) { soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); - soup_add_timeout (soup_server_get_async_context (server), + soup_add_timeout (g_main_context_get_thread_default (), 1000, timeout_finish_message, msg); } @@ -120,6 +120,38 @@ do_host_test (void) g_object_unref (two); } +/* request with too big header should be discarded with a IO error to + * prevent DOS attacks. + */ +static void +do_host_big_header (void) +{ + SoupMessage *msg; + SoupSession *session; + int i; + + g_test_bug ("792173"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + msg = soup_message_new_from_uri ("GET", base_uri); + for (i = 0; i < 2048; i++) { + char *key = g_strdup_printf ("test-long-header-key%d", i); + char *value = g_strdup_printf ("test-long-header-key%d", i); + soup_message_headers_append (msg->request_headers, key, value); + g_free (value); + g_free (key); + } + + soup_session_send_message (session, msg); + + soup_test_session_abort_unref (session); + + soup_test_assert_message_status (msg, SOUP_STATUS_IO_ERROR); + + g_object_unref (msg); +} + /* Dropping the application's ref on the session from a callback * should not cause the session to be freed at an incorrect time. * (This test will crash if it fails.) @@ -151,35 +183,28 @@ static void do_callback_unref_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupMessage *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; g_test_bug ("533473"); /* Get a guaranteed-bad URI */ - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); - bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - NULL); - g_object_unref (addr); - - bad_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (bad_server)); - g_object_unref (bad_server); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session); loop = g_main_loop_new (NULL, TRUE); - one = soup_message_new ("GET", bad_uri); + one = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_message_new ("GET", bad_uri); + two = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_session_queue_message (session, one, cu_one_completed, loop); soup_session_queue_message (session, two, cu_two_completed, loop); @@ -245,22 +270,15 @@ static void do_callback_unref_req_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupRequest *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; /* Get a guaranteed-bad URI */ - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); - bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - NULL); - g_object_unref (addr); - - bad_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (bad_server)); - g_object_unref (bad_server); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -269,11 +287,11 @@ do_callback_unref_req_test (void) loop = g_main_loop_new (NULL, TRUE); - one = soup_session_request (session, bad_uri, NULL); + one = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_session_request (session, bad_uri, NULL); + two = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_request_send_async (one, NULL, cur_one_completed, session); g_object_unref (one); @@ -418,7 +436,7 @@ ea_connection_created (SoupSession *session, GObject *conn, gpointer user_data) } static void -ea_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) +ea_message_starting (SoupMessage *msg, SoupSession *session) { soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); } @@ -469,8 +487,8 @@ do_early_abort_test (void) session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); msg = soup_message_new_from_uri ("GET", base_uri); - g_signal_connect (session, "request-started", - G_CALLBACK (ea_request_started), NULL); + g_signal_connect (msg, "starting", + G_CALLBACK (ea_message_starting), session); soup_session_send_message (session, msg); debug_printf (2, " Message 3 completed\n"); @@ -521,13 +539,21 @@ ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop) } static void -ear_request_started (SoupSession *session, SoupMessage *msg, - SoupSocket *socket, gpointer cancellable) +ear_message_starting (SoupMessage *msg, gpointer cancellable) { g_cancellable_cancel (cancellable); } static void +ear_request_queued (SoupSession *session, SoupMessage *msg, + gpointer cancellable) +{ + g_signal_connect (msg, "starting", + G_CALLBACK (ear_message_starting), + cancellable); +} + +static void do_early_abort_req_test (void) { SoupSession *session; @@ -574,8 +600,8 @@ do_early_abort_req_test (void) req = soup_session_request_uri (session, base_uri, NULL); cancellable = g_cancellable_new (); - g_signal_connect (session, "request-started", - G_CALLBACK (ear_request_started), cancellable); + g_signal_connect (session, "request-queued", + G_CALLBACK (ear_request_queued), cancellable); soup_request_send_async (req, cancellable, ear_three_completed, loop); g_main_loop_run (loop); g_object_unref (req); @@ -914,6 +940,254 @@ do_pause_abort_test (void) g_assert_null (ptr); } +static GMainLoop *pause_cancel_loop; + +static void +pause_cancel_got_headers (SoupMessage *msg, gpointer user_data) +{ + SoupSession *session = user_data; + + soup_session_pause_message (session, msg); + g_main_loop_quit (pause_cancel_loop); +} + +static void +pause_cancel_finished (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + gboolean *finished = user_data; + + *finished = TRUE; + g_main_loop_quit (pause_cancel_loop); +} + +static gboolean +pause_cancel_timeout (gpointer user_data) +{ + gboolean *timed_out = user_data; + + *timed_out = TRUE; + g_main_loop_quit (pause_cancel_loop); + return FALSE; +} + +static void +do_pause_cancel_test (void) +{ + SoupSession *session; + SoupMessage *msg; + gboolean finished = FALSE, timed_out = FALSE; + guint timeout_id; + + g_test_bug ("745094"); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + pause_cancel_loop = g_main_loop_new (NULL, FALSE); + + timeout_id = g_timeout_add_seconds (5, pause_cancel_timeout, &timed_out); + + msg = soup_message_new_from_uri ("GET", base_uri); + g_object_ref (msg); + g_signal_connect (msg, "got-headers", + G_CALLBACK (pause_cancel_got_headers), session); + + soup_session_queue_message (session, msg, pause_cancel_finished, &finished); + g_main_loop_run (pause_cancel_loop); + g_assert_false (finished); + + soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); + g_main_loop_run (pause_cancel_loop); + g_assert_true (finished); + g_assert_false (timed_out); + + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + g_main_loop_unref (pause_cancel_loop); + if (!timed_out) + g_source_remove (timeout_id); +} + +static gboolean +run_echo_server (gpointer user_data) +{ + GIOStream *stream = user_data; + GInputStream *istream; + GDataInputStream *distream; + GOutputStream *ostream; + char *str, *caps; + gssize n; + GError *error = NULL; + + istream = g_io_stream_get_input_stream (stream); + distream = G_DATA_INPUT_STREAM (g_data_input_stream_new (istream)); + ostream = g_io_stream_get_output_stream (stream); + + /* Echo until the client disconnects */ + while (TRUE) { + str = g_data_input_stream_read_line (distream, NULL, NULL, &error); + g_assert_no_error (error); + if (!str) + break; + + caps = g_ascii_strup (str, -1); + n = g_output_stream_write (ostream, caps, strlen (caps), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, strlen (caps)); + n = g_output_stream_write (ostream, "\n", 1, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, 1); + g_free (caps); + g_free (str); + } + + g_object_unref (distream); + + g_io_stream_close (stream, NULL, &error); + g_assert_no_error (error); + g_object_unref (stream); + + return FALSE; +} + +static void +steal_after_upgrade (SoupMessage *msg, gpointer user_data) +{ + SoupClientContext *context = user_data; + GIOStream *stream; + GSource *source; + + /* This should not ever be seen. */ + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + + stream = soup_client_context_steal_connection (context); + + source = g_idle_source_new (); + g_source_set_callback (source, run_echo_server, stream, NULL); + g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); +} + +static void +upgrade_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + if (msg->method != SOUP_METHOD_GET) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS); + soup_message_headers_append (msg->request_headers, "Upgrade", "ECHO"); + soup_message_headers_append (msg->request_headers, "Connection", "upgrade"); + + g_signal_connect (msg, "wrote-informational", + G_CALLBACK (steal_after_upgrade), context); +} + +static void +callback_not_reached (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +switching_protocols (SoupMessage *msg, gpointer user_data) +{ + GIOStream **out_iostream = user_data; + SoupSession *session = g_object_get_data (G_OBJECT (msg), "SoupSession"); + + *out_iostream = soup_session_steal_connection (session, msg); +} + +static void +do_stealing_test (gconstpointer data) +{ + gboolean sync = GPOINTER_TO_INT (data); + SoupServer *server; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + GIOStream *iostream; + GInputStream *istream; + GDataInputStream *distream; + GOutputStream *ostream; + int i; + gssize n; + char *str, *caps; + GError *error = NULL; + static const char *strings[] = { "one", "two", "three", "four", "five" }; + + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + uri = soup_test_server_get_uri (server, SOUP_URI_SCHEME_HTTP, "127.0.0.1"); + soup_server_add_handler (server, NULL, upgrade_server_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_message_headers_append (msg->request_headers, "Upgrade", "echo"); + soup_message_headers_append (msg->request_headers, "Connection", "upgrade"); + g_object_set_data (G_OBJECT (msg), "SoupSession", session); + + soup_message_add_status_code_handler (msg, "got-informational", + SOUP_STATUS_SWITCHING_PROTOCOLS, + G_CALLBACK (switching_protocols), &iostream); + + iostream = NULL; + + if (sync) { + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS); + } else { + g_object_ref (msg); + soup_session_queue_message (session, msg, callback_not_reached, NULL); + while (iostream == NULL) + g_main_context_iteration (NULL, TRUE); + } + + g_assert (iostream != NULL); + + g_object_unref (msg); + soup_test_session_abort_unref (session); + soup_uri_free (uri); + + /* Now iostream connects to a (capitalizing) echo server */ + + istream = g_io_stream_get_input_stream (iostream); + distream = G_DATA_INPUT_STREAM (g_data_input_stream_new (istream)); + ostream = g_io_stream_get_output_stream (iostream); + + for (i = 0; i < G_N_ELEMENTS (strings); i++) { + n = g_output_stream_write (ostream, strings[i], strlen (strings[i]), + NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, strlen (strings[i])); + n = g_output_stream_write (ostream, "\n", 1, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, 1); + } + + for (i = 0; i < G_N_ELEMENTS (strings); i++) { + str = g_data_input_stream_read_line (distream, NULL, NULL, &error); + g_assert_no_error (error); + caps = g_ascii_strup (strings[i], -1); + g_assert_cmpstr (caps, ==, str); + g_free (caps); + g_free (str); + } + + g_object_unref (distream); + + g_io_stream_close (iostream, NULL, &error); + g_assert_no_error (error); + g_object_unref (iostream); + + /* We can't do this until the end because it's in another thread, and + * soup_test_server_quit_unref() will wait for that thread to exit. + */ + soup_test_server_quit_unref (server); +} + int main (int argc, char **argv) { @@ -922,10 +1196,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, "http", NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "misc-test", @@ -936,12 +1209,12 @@ main (int argc, char **argv) g_object_unref (auth_domain); if (tls_available) { - ssl_server = soup_test_server_new_ssl (TRUE); + ssl_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); + ssl_base_uri = soup_test_server_get_uri (ssl_server, "https", "127.0.0.1"); } + g_test_add_func ("/misc/bigheader", do_host_big_header); g_test_add_func ("/misc/host", do_host_test); g_test_add_func ("/misc/callback-unref/msg", do_callback_unref_test); g_test_add_func ("/misc/callback-unref/req", do_callback_unref_req_test); @@ -956,6 +1229,9 @@ main (int argc, char **argv) g_test_add_func ("/misc/aliases", do_aliases_test); g_test_add_func ("/misc/idle-on-dispose", do_idle_on_dispose_test); g_test_add_func ("/misc/pause-abort", do_pause_abort_test); + g_test_add_func ("/misc/pause-cancel", do_pause_cancel_test); + g_test_add_data_func ("/misc/stealing/async", GINT_TO_POINTER (FALSE), do_stealing_test); + g_test_add_data_func ("/misc/stealing/sync", GINT_TO_POINTER (TRUE), do_stealing_test); ret = g_test_run (); @@ -967,5 +1243,6 @@ main (int argc, char **argv) soup_test_server_quit_unref (ssl_server); } + test_cleanup (); return ret; } diff --git a/tests/multipart-test.c b/tests/multipart-test.c index e057412e..488865ae 100644 --- a/tests/multipart-test.c +++ b/tests/multipart-test.c @@ -486,10 +486,9 @@ main (int argc, char **argv) buffer = g_malloc (READ_BUFFER_SIZE); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); base_uri_string = soup_uri_to_string (base_uri, FALSE); /* FIXME: I had to raise the number of connections allowed here, otherwise I diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c index 82532c74..c9d9bcad 100644 --- a/tests/no-ssl-test.c +++ b/tests/no-ssl-test.c @@ -3,14 +3,14 @@ #include "test-utils.h" static void -do_ssl_test_for_session (SoupSession *session, const char *uri) +do_ssl_test_for_session (SoupSession *session, SoupURI *uri) { SoupMessage *msg; GTlsCertificate *cert = NULL; GTlsCertificateFlags flags; gboolean is_https; - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED); @@ -24,8 +24,9 @@ do_ssl_test_for_session (SoupSession *session, const char *uri) } static void -do_ssl_tests (gconstpointer uri) +do_ssl_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("700518"); @@ -53,10 +54,18 @@ do_session_property_tests (void) GTlsDatabase *tlsdb; char *ca_file; SoupSession *session; + GParamSpec *pspec; g_test_bug ("700518"); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; session = soup_session_async_new (); + G_GNUC_END_IGNORE_DEPRECATIONS; + + /* Temporarily undeprecate SOUP_SESSION_SSL_CA_FILE to avoid warnings. */ + pspec = g_object_class_find_property (g_type_class_peek (SOUP_TYPE_SESSION), + SOUP_SESSION_SSL_CA_FILE); + pspec->flags &= ~G_PARAM_DEPRECATED; g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, @@ -101,6 +110,9 @@ do_session_property_tests (void) soup_test_assert (ca_file == NULL, "setting tls-database NULL set ssl-ca-file"); soup_test_session_abort_unref (session); + + /* Re-deprecate SOUP_SESSION_SSL_CA_FILE */ + pspec->flags |= G_PARAM_DEPRECATED; } static void @@ -121,7 +133,8 @@ int main (int argc, char **argv) { SoupServer *server; - char *uri; + SoupURI *uri; + guint port; int ret; /* Force this test to use the dummy TLS backend */ @@ -135,15 +148,17 @@ main (int argc, char **argv) */ server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + port = uri->port; + soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS); + soup_uri_set_port (uri, port); g_test_add_func ("/no-ssl/session-properties", do_session_property_tests); g_test_add_data_func ("/no-ssl/request-error", uri, do_ssl_tests); ret = g_test_run (); - g_free (uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c index 24a0f2e4..0cc41a7b 100644 --- a/tests/ntlm-test.c +++ b/tests/ntlm-test.c @@ -11,8 +11,6 @@ #include "test-utils.h" -static SoupURI *uri; - typedef enum { NTLM_UNAUTHENTICATED, NTLM_RECEIVED_REQUEST, @@ -29,9 +27,17 @@ static const char *state_name[] = { #define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA" #define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=" +#define NTLMSSP_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAokAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=" #define NTLM_RESPONSE_USER(response) ((response)[86] == 'E' ? NTLM_AUTHENTICATED_ALICE : ((response)[86] == 'I' ? NTLM_AUTHENTICATED_BOB : NTLM_UNAUTHENTICATED)) +typedef struct { + SoupServer *server; + GHashTable *connections; + SoupURI *uri; + gboolean ntlmssp; +} TestServer; + static void clear_state (gpointer connections, GObject *ex_connection) { @@ -43,8 +49,8 @@ server_callback (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer data) { - GHashTable *connections = data; - SoupSocket *socket; + TestServer *ts = data; + GSocket *socket; const char *auth; NTLMServerState state, required_user = 0; gboolean auth_required, not_found = FALSE; @@ -70,8 +76,8 @@ server_callback (SoupServer *server, SoupMessage *msg, if (strstr (path, "/404")) not_found = TRUE; - socket = soup_client_context_get_socket (client); - state = GPOINTER_TO_INT (g_hash_table_lookup (connections, socket)); + socket = soup_client_context_get_gsocket (client); + state = GPOINTER_TO_INT (g_hash_table_lookup (ts->connections, socket)); auth = soup_message_headers_get_one (msg->request_headers, "Authorization"); @@ -121,7 +127,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (ntlm_allowed && state == NTLM_RECEIVED_REQUEST) { soup_message_headers_append (msg->response_headers, "WWW-Authenticate", - "NTLM " NTLM_CHALLENGE); + ts->ntlmssp ? ("NTLM " NTLMSSP_CHALLENGE) : ("NTLM " NTLM_CHALLENGE)); state = NTLM_SENT_CHALLENGE; } else if (ntlm_allowed) { soup_message_headers_append (msg->response_headers, @@ -141,8 +147,37 @@ server_callback (SoupServer *server, SoupMessage *msg, } debug_printf (2, " (S:%s)", state_name[state]); - g_hash_table_insert (connections, socket, GINT_TO_POINTER (state)); - g_object_weak_ref (G_OBJECT (socket), clear_state, connections); + g_hash_table_insert (ts->connections, socket, GINT_TO_POINTER (state)); + g_object_weak_ref (G_OBJECT (socket), clear_state, ts->connections); +} + +static void +setup_server (TestServer *ts, + gconstpointer test_data) +{ + ts->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + ts->connections = g_hash_table_new (NULL, NULL); + ts->ntlmssp = FALSE; + soup_server_add_handler (ts->server, NULL, server_callback, ts, NULL); + + ts->uri = soup_test_server_get_uri (ts->server, "http", NULL); +} + +static void +setup_ntlmssp_server (TestServer *ts, + gconstpointer test_data) +{ + setup_server (ts, test_data); + ts->ntlmssp = TRUE; +} + +static void +teardown_server (TestServer *ts, + gconstpointer test_data) +{ + soup_uri_free (ts->uri); + soup_test_server_quit_unref (ts->server); + g_hash_table_destroy (ts->connections); } static gboolean authenticated_ntlm = FALSE; @@ -178,8 +213,10 @@ prompt_check (SoupMessage *msg, gpointer user_data) if (header && strstr (header, "Basic ")) state->got_basic_prompt = TRUE; if (header && strstr (header, "NTLM") && - !strstr (header, NTLM_CHALLENGE)) + (!strstr (header, NTLM_CHALLENGE) && + !strstr (header, NTLMSSP_CHALLENGE))) { state->got_ntlm_prompt = TRUE; + } } static void @@ -342,7 +379,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, * asking. */ authenticated_ntlm = FALSE; - do_message (session, base_uri, "/noauth", + do_message (session, base_uri, "/noauth/", FALSE, use_ntlm, FALSE, FALSE, SOUP_STATUS_OK); @@ -358,7 +395,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, * previous step, then we'll just immediately get a 401 here. * So in no case will we see the client try to do_ntlm. */ - do_message (session, base_uri, "/alice", + do_message (session, base_uri, "/alice/", !alice_via_ntlm, FALSE, !alice_via_ntlm, alice_via_basic, alice ? SOUP_STATUS_OK : @@ -378,7 +415,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, SOUP_STATUS_UNAUTHORIZED); /* 4. Should be exactly the same as #3, except the status code */ - do_message (session, base_uri, "/alice", + do_message (session, base_uri, "/alice/", !alice, bob_via_ntlm, !alice, alice_via_basic, alice ? SOUP_STATUS_OK : @@ -390,7 +427,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, * (and fail). Bob-via-NTLM will try to do NTLM right away and * succeed. */ - do_message (session, base_uri, "/bob", + do_message (session, base_uri, "/bob/", !bob_via_ntlm, bob_via_ntlm, !bob_via_ntlm, alice_via_basic, bob ? SOUP_STATUS_OK : @@ -402,7 +439,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, * still knows about this path, so will try Basic right away * and succeed. */ - do_message (session, base_uri, "/alice", + do_message (session, base_uri, "/alice/", !alice_via_ntlm, alice_via_ntlm, !alice_via_ntlm, alice_via_basic, alice ? SOUP_STATUS_OK : @@ -412,19 +449,19 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, * Since Bob-via-NTLM is unauthenticated at this point, he'll try * NTLM before realizing that the server doesn't support it. */ - do_message (session, base_uri, "/basic", + do_message (session, base_uri, "/basic/", FALSE, bob_via_ntlm, TRUE, user != NULL, user != NULL ? SOUP_STATUS_OK : SOUP_STATUS_UNAUTHORIZED); /* 8. Server accepts Basic or NTLM from either user. - * Alice-via-NTLM is still authenticated at this point from #6, - * and Bob-via-NTLM is authenticated from #7, so neither - * of them will do anything. + * NTLM users will try NTLM without getting a prompt (their + * previous NTLM connections will have been closed by the 401 + * from /basic). Non-NTLM users will be prompted for either. */ - do_message (session, base_uri, "/either", - !use_ntlm, FALSE, + do_message (session, base_uri, "/either/", + !use_ntlm, use_ntlm, !use_ntlm, !use_ntlm && user != NULL, user != NULL ? SOUP_STATUS_OK : SOUP_STATUS_UNAUTHORIZED); @@ -461,8 +498,16 @@ static const NtlmTest ntlm_tests[] = { { "/ntlm/fallback/basic", "alice", FALSE, FALLBACK } }; +static const NtlmTest ntlmssp_tests[] = { + { "/ntlm/ssp/none", NULL, FALSE, BUILTIN }, + { "/ntlm/ssp/alice", "alice", TRUE, BUILTIN }, + { "/ntlm/ssp/bob", "bob", TRUE, BUILTIN }, + { "/ntlm/ssp/basic", "alice", FALSE, BUILTIN } +}; + static void -do_ntlm_test (gconstpointer data) +do_ntlm_test (TestServer *ts, + gconstpointer data) { const NtlmTest *test = data; gboolean use_builtin_ntlm = TRUE; @@ -509,7 +554,7 @@ do_ntlm_test (gconstpointer data) break; } - do_ntlm_round (uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm); + do_ntlm_round (ts->uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm); } static void @@ -532,9 +577,9 @@ retry_test_authenticate (SoupSession *session, SoupMessage *msg, } static void -do_retrying_test (gconstpointer data) +do_retrying_test (TestServer *ts, + gconstpointer data) { - SoupURI *base_uri = (SoupURI *)data; SoupSession *session; SoupMessage *msg; SoupURI *uri; @@ -552,7 +597,7 @@ do_retrying_test (gconstpointer data) g_signal_connect (session, "authenticate", G_CALLBACK (retry_test_authenticate), &retried); - uri = soup_uri_new_with_base (base_uri, "/alice"); + uri = soup_uri_new_with_base (ts->uri, "/alice"); msg = soup_message_new_from_uri ("GET", uri); soup_uri_free (uri); @@ -574,7 +619,7 @@ do_retrying_test (gconstpointer data) G_CALLBACK (retry_test_authenticate), &retried); retried = FALSE; - uri = soup_uri_new_with_base (base_uri, "/bob"); + uri = soup_uri_new_with_base (ts->uri, "/bob"); msg = soup_message_new_from_uri ("GET", uri); soup_uri_free (uri); @@ -591,31 +636,25 @@ do_retrying_test (gconstpointer data) int main (int argc, char **argv) { - SoupServer *server; - GHashTable *connections; int i, ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); - connections = g_hash_table_new (NULL, NULL); - soup_server_add_handler (server, NULL, - server_callback, connections, NULL); - - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + for (i = 0; i < G_N_ELEMENTS (ntlm_tests); i++) { + g_test_add (ntlm_tests[i].name, TestServer, &ntlm_tests[i], + setup_server, do_ntlm_test, teardown_server); + } + for (i = 0; i < G_N_ELEMENTS (ntlmssp_tests); i++) { + g_test_add (ntlmssp_tests[i].name, TestServer, &ntlmssp_tests[i], + setup_ntlmssp_server, do_ntlm_test, teardown_server); + } - for (i = 0; i < G_N_ELEMENTS (ntlm_tests); i++) - g_test_add_data_func (ntlm_tests[i].name, &ntlm_tests[i], do_ntlm_test); - g_test_add_data_func ("/ntlm/retry", uri, do_retrying_test); + g_test_add ("/ntlm/retry", TestServer, NULL, + setup_server, do_retrying_test, teardown_server); ret = g_test_run (); - soup_uri_free (uri); - - soup_test_server_quit_unref (server); test_cleanup (); - g_hash_table_destroy (connections); return ret; } diff --git a/tests/proxy-test.c b/tests/proxy-test.c index 4b6679b3..1d68aa05 100644 --- a/tests/proxy-test.c +++ b/tests/proxy-test.c @@ -322,6 +322,84 @@ do_proxy_redirect_test (void) soup_test_session_abort_unref (session); } +static void +do_proxy_auth_request (const char *url, SoupSession *session, gboolean do_read) +{ + SoupRequest *request; + SoupMessage *msg; + GInputStream *stream; + GError *error = NULL; + + request = soup_session_request (session, url, NULL); + msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); + + stream = soup_test_request_send (request, NULL, 0, &error); + g_assert_no_error (error); + g_clear_error (&error); + + if (do_read) { + char buffer[256]; + gsize nread; + + do { + g_input_stream_read_all (stream, buffer, sizeof (buffer), &nread, + NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + } while (nread > 0); + } + + soup_test_request_close_stream (request, stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (stream); + + debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + g_object_unref (msg); + g_object_unref (request); +} + +static void +do_proxy_auth_cache_test (void) +{ + SoupSession *session; + char *cache_dir; + SoupCache *cache; + char *url; + + g_test_bug ("756076"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + + cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL); + debug_printf (2, " Caching to %s\n", cache_dir); + cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER); + g_free (cache_dir); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[AUTH_PROXY], + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + SOUP_SESSION_ADD_FEATURE, cache, + NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (authenticate), NULL); + + url = g_strconcat (HTTP_SERVER, "/Basic/realm1/", NULL); + + debug_printf (1, " GET %s via %s (from network)\n", url, proxy_names[AUTH_PROXY]); + do_proxy_auth_request (url, session, TRUE); + soup_cache_flush (cache); + + debug_printf (1, " GET %s via %s (from cache)\n", url, proxy_names[AUTH_PROXY]); + do_proxy_auth_request (url, session, FALSE); + + g_free (url); + soup_test_session_abort_unref (session); + g_object_unref (cache); +} + int main (int argc, char **argv) { @@ -338,10 +416,9 @@ main (int argc, char **argv) g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts); } - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); for (i = 0; i < ntests; i++) { path = g_strdup_printf ("/proxy/async/%s", tests[i].explanation); @@ -356,11 +433,14 @@ main (int argc, char **argv) g_test_add_data_func ("/proxy/fragment", base_uri, do_proxy_fragment_test); g_test_add_func ("/proxy/redirect", do_proxy_redirect_test); + g_test_add_func ("/proxy/auth-cache", do_proxy_auth_cache_test); ret = g_test_run (); soup_uri_free (base_uri); soup_test_server_quit_unref (server); + for (i = 0; i < 3; i++) + g_object_unref (proxy_resolvers[i]); test_cleanup (); return ret; diff --git a/tests/range-test.c b/tests/range-test.c index c23ba462..d3c49963 100644 --- a/tests/range-test.c +++ b/tests/range-test.c @@ -334,11 +334,7 @@ do_apache_range_test (void) session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); -#if HAVE_APACHE_2_2 - do_range_test (session, "http://127.0.0.1:47524/", FALSE, FALSE); -#else do_range_test (session, "http://127.0.0.1:47524/", TRUE, FALSE); -#endif soup_test_session_abort_unref (session); } @@ -361,16 +357,18 @@ do_libsoup_range_test (void) { SoupSession *session; SoupServer *server; - char *base_uri; + SoupURI *base_uri; + char *base_uri_str; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - base_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); - do_range_test (session, base_uri, TRUE, TRUE); - g_free (base_uri); + base_uri = soup_test_server_get_uri (server, "http", NULL); + base_uri_str = soup_uri_to_string (base_uri, FALSE); + do_range_test (session, base_uri_str, TRUE, TRUE); + soup_uri_free (base_uri); + g_free (base_uri_str); soup_test_server_quit_unref (server); soup_test_session_abort_unref (session); diff --git a/tests/redirect-test.c b/tests/redirect-test.c index ad8dabaa..ccdd4c29 100644 --- a/tests/redirect-test.c +++ b/tests/redirect-test.c @@ -395,24 +395,24 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server, *server2; - guint port; + SoupURI *uri2; char *path; int n, ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); - server2 = soup_test_server_new (TRUE); + server2 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server2, NULL, server2_callback, NULL, NULL); - server2_uri = g_strdup_printf ("http://127.0.0.1:%d/on-server2", - soup_server_get_port (server2)); + uri2 = soup_test_server_get_uri (server2, "http", NULL); + soup_uri_set_path (uri2, "/on-server2"); + server2_uri = soup_uri_to_string (uri2, FALSE); + soup_uri_free (uri2); loop = g_main_loop_new (NULL, TRUE); @@ -422,8 +422,8 @@ main (int argc, char **argv) sync_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); for (n = 0; n < n_tests; n++) { - path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n - , tests[n].requests[0].method, + path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n, + tests[n].requests[0].method, tests[n].requests[0].status_code); g_test_add_data_func (path, &tests[n], do_async_msg_api_test); g_free (path); @@ -458,5 +458,6 @@ main (int argc, char **argv) soup_test_session_abort_unref (async_session); soup_test_session_abort_unref (sync_session); + test_cleanup (); return ret; } diff --git a/tests/requester-test.c b/tests/requester-test.c index 39b30bd5..6d6d5728 100644 --- a/tests/requester-test.c +++ b/tests/requester-test.c @@ -17,6 +17,12 @@ SoupBuffer *response, *auth_response; #define REDIRECT_HTML_BODY "<html><body>Try again</body></html>\r\n" #define AUTH_HTML_BODY "<html><body>Unauthorized</body></html>\r\n" +typedef enum { + NO_CANCEL, + SYNC_CANCEL, + PAUSE_AND_CANCEL_ON_IDLE +} CancelPolicy; + static gboolean slow_finish_message (gpointer msg) { @@ -205,6 +211,32 @@ cancel_message (SoupMessage *msg, gpointer session) soup_session_cancel_message (session, msg, SOUP_STATUS_FORBIDDEN); } +typedef struct { + SoupMessage *msg; + SoupSession *session; +} CancelData; + +static gboolean +cancel_message_idle (CancelData *data) +{ + cancel_message (data->msg, data->session); + return FALSE; +} + +static void +pause_and_cancel_message (SoupMessage *msg, gpointer session) +{ + CancelData *data = g_new (CancelData, 1); + GSource *source = g_idle_source_new (); + + soup_session_pause_message (session, msg); + data->msg = msg; + data->session = session; + g_source_set_callback (source, (GSourceFunc)cancel_message_idle, data, g_free); + g_source_attach (source, soup_session_get_async_context (session)); + g_source_unref (source); +} + static void request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) @@ -219,7 +251,7 @@ static void do_async_test (SoupSession *session, SoupURI *uri, GAsyncReadyCallback callback, guint expected_status, SoupBuffer *expected_response, - gboolean persistent, gboolean cancel) + gboolean persistent, CancelPolicy cancel_policy) { SoupRequester *requester; SoupRequest *request; @@ -234,16 +266,24 @@ do_async_test (SoupSession *session, SoupURI *uri, requester = NULL; data.body = g_string_new (NULL); - data.cancel = cancel; + data.cancel = cancel_policy != NO_CANCEL; if (requester) request = soup_requester_request_uri (requester, uri, NULL); else request = soup_session_request_uri (session, uri, NULL); msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); - if (cancel) { + switch (cancel_policy) { + case SYNC_CANCEL: g_signal_connect (msg, "got-headers", G_CALLBACK (cancel_message), session); + break; + case PAUSE_AND_CANCEL_ON_IDLE: + g_signal_connect (msg, "got-headers", + G_CALLBACK (pause_and_cancel_message), session); + break; + case NO_CANCEL: + break; } started_id = g_signal_connect (session, "request-started", @@ -279,7 +319,7 @@ do_async_test (SoupSession *session, SoupURI *uri, } static void -do_test_for_thread_and_context (SoupSession *session, const char *base_uri) +do_test_for_thread_and_context (SoupSession *session, SoupURI *base_uri) { SoupRequester *requester; SoupURI *uri; @@ -292,48 +332,50 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); debug_printf (1, " basic test\n"); - uri = soup_uri_new (base_uri); - do_async_test (session, uri, test_sent, + do_async_test (session, base_uri, test_sent, SOUP_STATUS_OK, response, - TRUE, FALSE); - soup_uri_free (uri); + TRUE, NO_CANCEL); debug_printf (1, " chunked test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/chunked"); + uri = soup_uri_new_with_base (base_uri, "/chunked"); do_async_test (session, uri, test_sent, SOUP_STATUS_OK, response, - TRUE, FALSE); + TRUE, NO_CANCEL); soup_uri_free (uri); debug_printf (1, " auth test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/auth"); + uri = soup_uri_new_with_base (base_uri, "/auth"); do_async_test (session, uri, auth_test_sent, SOUP_STATUS_UNAUTHORIZED, auth_response, - TRUE, FALSE); + TRUE, NO_CANCEL); soup_uri_free (uri); debug_printf (1, " non-persistent test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/non-persistent"); + uri = soup_uri_new_with_base (base_uri, "/non-persistent"); do_async_test (session, uri, test_sent, SOUP_STATUS_OK, response, - FALSE, FALSE); + FALSE, NO_CANCEL); soup_uri_free (uri); debug_printf (1, " cancellation test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/"); + uri = soup_uri_new_with_base (base_uri, "/"); do_async_test (session, uri, test_sent, SOUP_STATUS_FORBIDDEN, NULL, - FALSE, TRUE); + FALSE, SYNC_CANCEL); + soup_uri_free (uri); + + debug_printf (1, " cancellation after paused test\n"); + uri = soup_uri_new_with_base (base_uri, "/"); + do_async_test (session, uri, test_sent, + SOUP_STATUS_FORBIDDEN, NULL, + FALSE, PAUSE_AND_CANCEL_ON_IDLE); soup_uri_free (uri); } static void -do_simple_plain_test (gconstpointer uri) +do_simple_plain_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("653707"); @@ -344,8 +386,9 @@ do_simple_plain_test (gconstpointer uri) } static void -do_simple_async_test (gconstpointer uri) +do_simple_async_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("653707"); @@ -358,7 +401,7 @@ do_simple_async_test (gconstpointer uri) } static void -do_test_with_context_and_type (const char *uri, gboolean plain_session) +do_test_with_context_and_type (SoupURI *uri, gboolean plain_session) { GMainContext *async_context; SoupSession *session; @@ -381,14 +424,18 @@ do_test_with_context_and_type (const char *uri, gboolean plain_session) } static void -do_async_test_with_context (gconstpointer uri) +do_async_test_with_context (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; + do_test_with_context_and_type (uri, FALSE); } static void -do_plain_test_with_context (gconstpointer uri) +do_plain_test_with_context (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; + do_test_with_context_and_type (uri, TRUE); } @@ -407,8 +454,9 @@ plain_test_thread (gpointer uri) } static void -do_async_test_in_thread (gconstpointer uri) +do_async_test_in_thread (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; GThread *thread; thread = g_thread_new ("do_async_test_in_thread", @@ -418,8 +466,9 @@ do_async_test_in_thread (gconstpointer uri) } static void -do_plain_test_in_thread (gconstpointer uri) +do_plain_test_in_thread (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; GThread *thread; thread = g_thread_new ("do_plain_test_in_thread", @@ -431,7 +480,7 @@ do_plain_test_in_thread (gconstpointer uri) static void do_sync_request (SoupSession *session, SoupRequest *request, guint expected_status, SoupBuffer *expected_response, - gboolean persistent, gboolean cancel) + gboolean persistent, CancelPolicy cancel_policy) { GInputStream *in; SoupMessage *msg; @@ -443,7 +492,7 @@ do_sync_request (SoupSession *session, SoupRequest *request, SoupSocket *socket = NULL; msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); - if (cancel) { + if (cancel_policy == SYNC_CANCEL) { g_signal_connect (msg, "got-headers", G_CALLBACK (cancel_message), session); } @@ -454,7 +503,7 @@ do_sync_request (SoupSession *session, SoupRequest *request, in = soup_request_send (request, NULL, &error); g_signal_handler_disconnect (session, started_id); - if (cancel) { + if (cancel_policy == SYNC_CANCEL) { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_object_unref (msg); @@ -504,7 +553,7 @@ do_sync_request (SoupSession *session, SoupRequest *request, } static void -do_sync_tests_for_session (SoupSession *session, const char *uri_string) +do_sync_tests_for_session (SoupSession *session, SoupURI *base_uri) { SoupRequester *requester; SoupRequest *request; @@ -512,7 +561,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER)); - uri = soup_uri_new (uri_string); + uri = soup_uri_copy (base_uri); debug_printf (1, " basic test\n"); if (requester) @@ -521,7 +570,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, - TRUE, FALSE); + TRUE, NO_CANCEL); g_object_unref (request); debug_printf (1, " chunked test\n"); @@ -532,7 +581,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, - TRUE, FALSE); + TRUE, NO_CANCEL); g_object_unref (request); debug_printf (1, " auth test\n"); @@ -543,7 +592,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_UNAUTHORIZED, auth_response, - TRUE, FALSE); + TRUE, NO_CANCEL); g_object_unref (request); debug_printf (1, " non-persistent test\n"); @@ -554,7 +603,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, - FALSE, FALSE); + FALSE, NO_CANCEL); g_object_unref (request); debug_printf (1, " cancel test\n"); @@ -565,15 +614,16 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_FORBIDDEN, NULL, - TRUE, TRUE); + TRUE, SYNC_CANCEL); g_object_unref (request); soup_uri_free (uri); } static void -do_plain_sync_test (gconstpointer uri) +do_plain_sync_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -582,8 +632,9 @@ do_plain_sync_test (gconstpointer uri) } static void -do_sync_sync_test (gconstpointer uri) +do_sync_sync_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupRequester *requester; @@ -743,6 +794,7 @@ do_close_test_for_session (SoupSession *session, if (error) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); + g_object_unref (cancellable); g_assert_true (finished); @@ -751,16 +803,16 @@ do_close_test_for_session (SoupSession *session, } static void -do_async_close_test (gconstpointer uri) +do_async_close_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupURI *slow_uri; g_test_bug ("695652"); g_test_bug ("711260"); - slow_uri = soup_uri_new (uri); - soup_uri_set_path (slow_uri, "/slow"); + slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -772,18 +824,17 @@ do_async_close_test (gconstpointer uri) } static void -do_sync_close_test (gconstpointer uri) +do_sync_close_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupURI *slow_uri; g_test_bug ("695652"); g_test_bug ("711260"); - slow_uri = soup_uri_new (uri); - soup_uri_set_path (slow_uri, "/slow"); + slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow"); - debug_printf (1, " SoupSessionSync\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); @@ -796,7 +847,7 @@ do_sync_close_test (gconstpointer uri) int main (int argc, char **argv) { - char *uri; + SoupURI *uri; int ret; test_init (argc, argv, NULL); @@ -806,10 +857,11 @@ main (int argc, char **argv) AUTH_HTML_BODY, strlen (AUTH_HTML_BODY)); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (uri, "/foo"); g_test_add_data_func ("/requester/simple/SoupSession", uri, do_simple_plain_test); g_test_add_data_func ("/requester/simple/SoupSessionAsync", uri, do_simple_async_test); @@ -826,7 +878,7 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); + soup_uri_free (uri); soup_buffer_free (auth_response); soup_test_server_quit_unref (server); diff --git a/tests/resources/misc.xml b/tests/resources/misc.xml new file mode 100644 index 00000000..15361e62 --- /dev/null +++ b/tests/resources/misc.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource> + <file compressed="true">test1.txt</file> + <file preprocess="xml-stripblanks">test.gresource.xml</file> + </gresource> + <gresource prefix="/a_prefix"> + <file alias="test2-alias.txt">test2.txt</file> + <file>test2.txt</file> + </gresource> +</gresources> diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c index f386f526..34c297bc 100644 --- a/tests/server-auth-test.c +++ b/tests/server-auth-test.c @@ -36,7 +36,11 @@ do_test (SoupURI *base_uri, const char *path, GPid pid; gboolean done; - uri = soup_uri_new_with_base (base_uri, path); + /* We build the URI this way to avoid having soup_uri_new() + normalize the path, hence losing the encoded characters in + tests 4. and 5. below. */ + uri = soup_uri_copy (base_uri); + soup_uri_set_path (uri, path); uri_str = soup_uri_to_string (uri, FALSE); soup_uri_free (uri); @@ -156,7 +160,46 @@ do_server_auth_test (gconstpointer data) /* success? */ TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i)); - /* 4. Any auth required. */ + /* 4. Digest auth with encoded URI. See #794208. + */ + do_test (base_uri, "/Digest/A%20B", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i), + /* expected from server */ + FALSE, TRUE, + /* success? */ + TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i)); + + /* 5. Digest auth with a mixture of encoded and decoded chars in the URI. See #794208. + */ + do_test (base_uri, "/Digest/A%20|%20B", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i), + /* expected from server */ + FALSE, TRUE, + /* success? */ + TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i)); + + /* 6. Digest auth with UTF-8 chars in the URI. See #794208. + */ + do_test (base_uri, "/Digest/A௹B", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i), + /* expected from server */ + FALSE, TRUE, + /* success? */ + TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i)); + + /* 7. Any auth required. */ do_test (base_uri, "/Any/foo", TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), /* request */ @@ -168,7 +211,7 @@ do_server_auth_test (gconstpointer data) /* success? */ (TEST_USES_BASIC (i) || TEST_USES_DIGEST (i)) && TEST_GOOD_AUTH (i)); - /* 5. No auth required again. (Makes sure that + /* 8. No auth required again. (Makes sure that * SOUP_AUTH_DOMAIN_REMOVE_PATH works.) */ do_test (base_uri, "/Any/Not/foo", @@ -282,7 +325,7 @@ main (int argc, char **argv) test_init (argc, argv, no_test_entry); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); g_signal_connect (server, "request_started", G_CALLBACK (request_started_callback), NULL); soup_server_add_handler (server, NULL, @@ -310,12 +353,10 @@ main (int argc, char **argv) loop = g_main_loop_new (NULL, TRUE); + base_uri = soup_test_server_get_uri (server, "http", NULL); if (run_tests) { int i; - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); - for (i = 0; i < 16; i++) { char *path; const char *authtypes; @@ -344,13 +385,12 @@ main (int argc, char **argv) } ret = g_test_run (); - - soup_uri_free (base_uri); } else { - g_print ("Listening on port %d\n", soup_server_get_port (server)); + g_print ("Listening on port %d\n", base_uri->port); g_main_loop_run (loop); ret = 0; } + soup_uri_free (base_uri); g_main_loop_unref (loop); soup_test_server_quit_unref (server); diff --git a/tests/server-test.c b/tests/server-test.c index 0c980908..cf132b33 100644 --- a/tests/server-test.c +++ b/tests/server-test.c @@ -5,8 +5,13 @@ #include "test-utils.h" -SoupServer *server, *ssl_server; -SoupURI *base_uri, *ssl_base_uri; +#include <gio/gnetworking.h> + +typedef struct { + SoupServer *server; + SoupURI *base_uri, *ssl_base_uri; + GSList *handlers; +} ServerData; static void server_callback (SoupServer *server, SoupMessage *msg, @@ -33,6 +38,58 @@ server_callback (SoupServer *server, SoupMessage *msg, } static void +server_setup_nohandler (ServerData *sd, gconstpointer test_data) +{ + sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL); + if (tls_available) + sd->ssl_base_uri = soup_test_server_get_uri (sd->server, "https", NULL); +} + +static void +server_add_handler (ServerData *sd, + const char *path, + SoupServerCallback callback, + gpointer user_data, + GDestroyNotify destroy) +{ + soup_server_add_handler (sd->server, path, callback, user_data, destroy); + sd->handlers = g_slist_prepend (sd->handlers, g_strdup (path)); +} + +static void +server_add_early_handler (ServerData *sd, + const char *path, + SoupServerCallback callback, + gpointer user_data, + GDestroyNotify destroy) +{ + soup_server_add_early_handler (sd->server, path, callback, user_data, destroy); + sd->handlers = g_slist_prepend (sd->handlers, g_strdup (path)); +} + +static void +server_setup (ServerData *sd, gconstpointer test_data) +{ + server_setup_nohandler (sd, test_data); + server_add_handler (sd, NULL, server_callback, NULL, NULL); +} + +static void +server_teardown (ServerData *sd, gconstpointer test_data) +{ + GSList *iter; + + for (iter = sd->handlers; iter; iter = iter->next) + soup_server_remove_handler (sd->server, iter->data); + g_slist_free_full (sd->handlers, g_free); + + g_clear_pointer (&sd->server, soup_test_server_quit_unref); + g_clear_pointer (&sd->base_uri, soup_uri_free); + g_clear_pointer (&sd->ssl_base_uri, soup_uri_free); +} + +static void server_star_callback (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *context, gpointer data) @@ -58,7 +115,7 @@ server_star_callback (SoupServer *server, SoupMessage *msg, * all other URIs. #590751 */ static void -do_star_test (void) +do_star_test (ServerData *sd, gconstpointer test_data) { SoupSession *session; SoupMessage *msg; @@ -68,7 +125,7 @@ do_star_test (void) g_test_bug ("590751"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - star_uri = soup_uri_copy (base_uri); + star_uri = soup_uri_copy (sd->base_uri); soup_uri_set_path (star_uri, "*"); debug_printf (1, " Testing with no handler\n"); @@ -81,7 +138,7 @@ do_star_test (void) g_assert_cmpstr (handled_by, ==, NULL); g_object_unref (msg); - soup_server_add_handler (server, "*", server_star_callback, NULL, NULL); + server_add_handler (sd, "*", server_star_callback, NULL, NULL); debug_printf (1, " Testing with handler\n"); msg = soup_message_new_from_uri ("OPTIONS", star_uri); @@ -169,8 +226,10 @@ do_one_server_aliases_test (SoupURI *uri, } static void -do_server_aliases_test (void) +do_server_aliases_test (ServerData *sd, gconstpointer test_data) { + char *http_aliases[] = { "dav", NULL }; + char *https_aliases[] = { "davs", NULL }; char *http_good[] = { "http", "dav", NULL }; char *http_bad[] = { "https", "davs", "fred", NULL }; char *https_good[] = { "https", "davs", NULL }; @@ -179,21 +238,26 @@ do_server_aliases_test (void) g_test_bug ("703694"); + g_object_set (G_OBJECT (sd->server), + SOUP_SERVER_HTTP_ALIASES, http_aliases, + SOUP_SERVER_HTTPS_ALIASES, https_aliases, + NULL); + for (i = 0; http_good[i]; i++) - do_one_server_aliases_test (base_uri, http_good[i], TRUE); + do_one_server_aliases_test (sd->base_uri, http_good[i], TRUE); for (i = 0; http_bad[i]; i++) - do_one_server_aliases_test (base_uri, http_bad[i], FALSE); + do_one_server_aliases_test (sd->base_uri, http_bad[i], FALSE); if (tls_available) { for (i = 0; https_good[i]; i++) - do_one_server_aliases_test (ssl_base_uri, https_good[i], TRUE); + do_one_server_aliases_test (sd->ssl_base_uri, https_good[i], TRUE); for (i = 0; https_bad[i]; i++) - do_one_server_aliases_test (ssl_base_uri, https_bad[i], FALSE); + do_one_server_aliases_test (sd->ssl_base_uri, https_bad[i], FALSE); } } static void -do_dot_dot_test (void) +do_dot_dot_test (ServerData *sd, gconstpointer test_data) { SoupSession *session; SoupMessage *msg; @@ -203,7 +267,7 @@ do_dot_dot_test (void) session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - uri = soup_uri_new_with_base (base_uri, "/..%2ftest"); + uri = soup_uri_new_with_base (sd->base_uri, "/..%2ftest"); msg = soup_message_new_from_uri ("GET", uri); soup_uri_free (uri); @@ -220,10 +284,13 @@ ipv6_server_callback (SoupServer *server, SoupMessage *msg, SoupClientContext *context, gpointer data) { const char *host; + GSocketAddress *addr; char expected_host[128]; + addr = soup_client_context_get_local_address (context); g_snprintf (expected_host, sizeof (expected_host), - "[::1]:%d", soup_server_get_port (server)); + "[::1]:%d", + g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr))); host = soup_message_headers_get_one (msg->request_headers, "Host"); g_assert_cmpstr (host, ==, expected_host); @@ -235,95 +302,1052 @@ ipv6_server_callback (SoupServer *server, SoupMessage *msg, } static void -do_ipv6_test (void) +do_ipv6_test (ServerData *sd, gconstpointer test_data) { - SoupServer *ipv6_server; - SoupURI *ipv6_uri; - SoupAddress *ipv6_addr; SoupSession *session; SoupMessage *msg; + GError *error = NULL; g_test_bug ("666399"); - ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (ipv6_addr, NULL); - ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr, - NULL); - g_object_unref (ipv6_addr); - if (!ipv6_server) { - debug_printf (1, " skipping due to lack of IPv6 support\n"); + sd->server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + server_add_handler (sd, NULL, ipv6_server_callback, NULL, NULL); + + if (!soup_server_listen_local (sd->server, 0, + SOUP_SERVER_LISTEN_IPV6_ONLY, + &error)) { +#if GLIB_CHECK_VERSION (2, 41, 0) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); +#endif + g_test_skip ("no IPv6 support"); return; } - soup_server_add_handler (ipv6_server, NULL, ipv6_server_callback, NULL, NULL); - soup_server_run_async (ipv6_server); - - ipv6_uri = soup_uri_new ("http://[::1]/"); - soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server)); + sd->base_uri = soup_test_server_get_uri (sd->server, "http", "::1"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); debug_printf (1, " HTTP/1.1\n"); - msg = soup_message_new_from_uri ("GET", ipv6_uri); + msg = soup_message_new_from_uri ("GET", sd->base_uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); debug_printf (1, " HTTP/1.0\n"); - msg = soup_message_new_from_uri ("GET", ipv6_uri); + msg = soup_message_new_from_uri ("GET", sd->base_uri); soup_message_set_http_version (msg, SOUP_HTTP_1_0); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); - soup_uri_free (ipv6_uri); soup_test_session_abort_unref (session); - soup_test_server_quit_unref (ipv6_server); } -int -main (int argc, char **argv) +static void +multi_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) { - char *http_aliases[] = { "dav", NULL }; - char *https_aliases[] = { "davs", NULL }; - int ret; + GSocketAddress *addr; + GInetSocketAddress *iaddr; + SoupURI *uri; + char *uristr, *addrstr; - test_init (argc, argv, NULL); + addr = soup_client_context_get_local_address (context); + iaddr = G_INET_SOCKET_ADDRESS (addr); - server = soup_test_server_new (TRUE); - soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + uri = soup_message_get_uri (msg); + uristr = soup_uri_to_string (uri, FALSE); - g_object_set (G_OBJECT (server), - SOUP_SERVER_HTTP_ALIASES, http_aliases, - NULL); + addrstr = g_inet_address_to_string (g_inet_socket_address_get_address (iaddr)); + g_assert_cmpstr (addrstr, ==, uri->host); + g_free (addrstr); - if (tls_available) { - ssl_server = soup_test_server_new_ssl (TRUE); - soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); - g_object_set (G_OBJECT (ssl_server), - SOUP_SERVER_HTTPS_ALIASES, https_aliases, - NULL); + g_assert_cmpint (g_inet_socket_address_get_port (iaddr), ==, uri->port); + + /* FIXME ssl */ + + soup_message_set_response (msg, "text/plain", + SOUP_MEMORY_TAKE, uristr, strlen (uristr)); + soup_message_set_status (msg, SOUP_STATUS_OK); +} + +static void +do_multi_test (ServerData *sd, SoupURI *uri1, SoupURI *uri2) +{ + char *uristr; + SoupSession *session; + SoupMessage *msg; + + server_add_handler (sd, NULL, multi_server_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + uristr = soup_uri_to_string (uri1, FALSE); + msg = soup_message_new ("GET", uristr); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, uristr); + g_object_unref (msg); + g_free (uristr); + + uristr = soup_uri_to_string (uri2, FALSE); + msg = soup_message_new ("GET", uristr); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, uristr); + g_object_unref (msg); + g_free (uristr); + + soup_test_session_abort_unref (session); + + soup_uri_free (uri1); + soup_uri_free (uri2); +} + +static void +do_multi_port_test (ServerData *sd, gconstpointer test_data) +{ + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + sd->server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (sd->server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + if (!soup_server_listen_local (sd->server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; } - g_test_add_func ("/server/OPTIONS *", do_star_test); - g_test_add_func ("/server/aliases", do_server_aliases_test); - g_test_add_func ("/server/..-in-path", do_dot_dot_test); - g_test_add_func ("/server/ipv6", do_ipv6_test); + uris = soup_server_get_uris (sd->server); + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); - ret = g_test_run (); + g_assert_cmpint (uri1->port, !=, uri2->port); + + do_multi_test (sd, uri1, uri2); +} + +static void +do_multi_scheme_test (ServerData *sd, gconstpointer test_data) +{ + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + SOUP_TEST_SKIP_IF_NO_TLS; + + sd->server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (sd->server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + if (!soup_server_listen_local (sd->server, 0, + SOUP_SERVER_LISTEN_IPV4_ONLY | SOUP_SERVER_LISTEN_HTTPS, + &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + uris = soup_server_get_uris (sd->server); + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); + + g_assert_cmpstr (uri1->scheme, !=, uri2->scheme); + + do_multi_test (sd, uri1, uri2); +} + +static void +do_multi_family_test (ServerData *sd, gconstpointer test_data) +{ + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + sd->server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (sd->server, 0, 0, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + uris = soup_server_get_uris (sd->server); + if (g_slist_length (uris) == 1) { + gboolean ipv6_works; + + /* No IPv6? Double-check */ + ipv6_works = soup_server_listen_local (sd->server, 0, + SOUP_SERVER_LISTEN_IPV6_ONLY, + NULL); + if (ipv6_works) + g_assert_false (ipv6_works); + else + g_test_skip ("no IPv6 support"); + return; + } + + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); + + g_assert_cmpstr (uri1->host, !=, uri2->host); + g_assert_cmpint (uri1->port, ==, uri2->port); + + do_multi_test (sd, uri1, uri2); +} + +static void +do_gsocket_import_test (void) +{ + GSocket *gsock; + GSocketAddress *gaddr; + SoupServer *server; + GSList *listeners; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + GError *error = NULL; + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + g_object_unref (gaddr); + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 0); + g_slist_free (listeners); + + soup_server_listen_socket (server, gsock, 0, &error); + g_assert_no_error (error); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); + g_assert_nonnull (uri); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + + soup_uri_free (uri); + soup_test_server_quit_unref (server); + + g_assert_false (g_socket_is_connected (gsock)); + g_object_unref (gsock); +} - soup_uri_free (base_uri); +static void +do_fd_import_test (void) +{ + GSocket *gsock; + GSocketAddress *gaddr; + SoupServer *server; + GSList *listeners; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + int type; + GError *error = NULL; + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + g_object_unref (gaddr); + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 0); + g_slist_free (listeners); + + soup_server_listen_fd (server, g_socket_get_fd (gsock), 0, &error); + g_assert_no_error (error); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); + g_assert_nonnull (uri); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + + soup_uri_free (uri); soup_test_server_quit_unref (server); - if (tls_available) { - soup_uri_free (ssl_base_uri); - soup_test_server_quit_unref (ssl_server); + /* @server should have closed our socket, although @gsock doesn't + * know this. + */ + g_socket_get_option (gsock, SOL_SOCKET, SO_TYPE, &type, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + g_object_unref (gsock); +} + +typedef struct +{ + GIOStream parent; + GInputStream *input_stream; + GOutputStream *output_stream; +} GTestIOStream; + +typedef struct +{ + GIOStreamClass parent_class; +} GTestIOStreamClass; + +static GType g_test_io_stream_get_type (void); +G_DEFINE_TYPE (GTestIOStream, g_test_io_stream, G_TYPE_IO_STREAM); + + +static GInputStream * +get_input_stream (GIOStream *io_stream) +{ + GTestIOStream *self = (GTestIOStream *) io_stream; + + return self->input_stream; +} + +static GOutputStream * +get_output_stream (GIOStream *io_stream) +{ + GTestIOStream *self = (GTestIOStream *) io_stream; + + return self->output_stream; +} + +static void +finalize (GObject *object) +{ + GTestIOStream *self = (GTestIOStream *) object; + + if (self->input_stream != NULL) + g_object_unref (self->input_stream); + + if (self->output_stream != NULL) + g_object_unref (self->output_stream); + + G_OBJECT_CLASS (g_test_io_stream_parent_class)->finalize (object); +} + +static void +g_test_io_stream_class_init (GTestIOStreamClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GIOStreamClass *io_class = G_IO_STREAM_CLASS (klass); + + object_class->finalize = finalize; + + io_class->get_input_stream = get_input_stream; + io_class->get_output_stream = get_output_stream; +} + +static void +g_test_io_stream_init (GTestIOStream *self) +{ +} + +static GIOStream * +g_test_io_stream_new (GInputStream *input, GOutputStream *output) +{ + GTestIOStream *self; + + self = g_object_new (g_test_io_stream_get_type (), NULL); + self->input_stream = g_object_ref (input); + self->output_stream = g_object_ref (output); + + return G_IO_STREAM (self); +} + +static void +mem_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + GSocketAddress *addr; + GSocket *sock; + const char *host; + + addr = soup_client_context_get_local_address (context); + g_assert_nonnull (addr); + + addr = soup_client_context_get_remote_address (context); + g_assert_nonnull (addr); + + sock = soup_client_context_get_gsocket (context); + g_assert_null (sock); + + host = soup_client_context_get_host (context); + g_assert_cmpstr (host, ==, "127.0.0.1"); + + server_callback (server, msg, path, query, context, data); +} + +static void +do_iostream_accept_test (void) +{ + GError *error = NULL; + SoupServer *server; + GInputStream *input; + GOutputStream *output; + GIOStream *stream; + GSocketAddress *addr; + const char req[] = "GET / HTTP/1.0\r\n\r\n"; + gchar *reply; + gsize reply_size; + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (server, NULL, mem_server_callback, NULL, NULL); + + input = g_memory_input_stream_new_from_data (req, sizeof(req), NULL); + output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + stream = g_test_io_stream_new (input, output); + + addr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + + soup_server_accept_iostream (server, stream, addr, addr, &error); + g_assert_no_error (error); + + soup_test_server_quit_unref (server); + + reply = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output)); + reply_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (output)); + g_assert_true (reply_size > 0); + g_assert_true (g_str_has_prefix (reply, "HTTP/1.0 200 OK")); + + g_clear_object (&addr); + g_clear_object (&stream); + g_clear_object (&input); + g_clear_object (&output); + g_clear_error (&error); +} + +typedef struct { + SoupServer *server; + SoupMessage *smsg; + gboolean handler_called; + gboolean paused; +} UnhandledServerData; + +static gboolean +idle_unpause_message (gpointer user_data) +{ + UnhandledServerData *usd = user_data; + + soup_server_unpause_message (usd->server, usd->smsg); + return FALSE; +} + +static void +unhandled_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + UnhandledServerData *usd = data; + + usd->handler_called = TRUE; + + if (soup_message_headers_get_one (msg->request_headers, "X-Test-Server-Pause")) { + usd->paused = TRUE; + usd->server = server; + usd->smsg = msg; + soup_server_pause_message (server, msg); + g_idle_add (idle_unpause_message, usd); + } +} + +static void +do_fail_404_test (ServerData *sd, gconstpointer test_data) +{ + SoupSession *session; + SoupMessage *msg; + UnhandledServerData usd; + + usd.handler_called = usd.paused = FALSE; + + server_add_handler (sd, "/not-a-match", unhandled_server_callback, &usd, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", sd->base_uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_NOT_FOUND); + g_object_unref (msg); + + g_assert_false (usd.handler_called); + g_assert_false (usd.paused); + + soup_test_session_abort_unref (session); +} + +static void +do_fail_500_test (ServerData *sd, gconstpointer pause) +{ + SoupSession *session; + SoupMessage *msg; + UnhandledServerData usd; + + usd.handler_called = usd.paused = FALSE; + + server_add_handler (sd, NULL, unhandled_server_callback, &usd, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", sd->base_uri); + if (pause) + soup_message_headers_append (msg->request_headers, "X-Test-Server-Pause", "true"); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + g_object_unref (msg); + + g_assert_true (usd.handler_called); + if (pause) + g_assert_true (usd.paused); + else + g_assert_false (usd.paused); + + soup_test_session_abort_unref (session); +} + +static void +stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) +{ + GChecksum *checksum = user_data; + + g_checksum_update (checksum, (const guchar *)chunk->data, chunk->length); +} + +static void +stream_got_body (SoupMessage *msg, gpointer user_data) +{ + GChecksum *checksum = user_data; + const char *md5 = g_checksum_get_string (checksum); + + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, + md5, strlen (md5)); + g_checksum_free (checksum); +} + +static void +early_stream_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + GChecksum *checksum; + + if (msg->method != SOUP_METHOD_POST) { + soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED); + return; } + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_signal_connect (msg, "got-chunk", + G_CALLBACK (stream_got_chunk), checksum); + g_signal_connect (msg, "got-body", + G_CALLBACK (stream_got_body), checksum); + + soup_message_body_set_accumulate (msg->request_body, TRUE); +} + +static void +do_early_stream_test (ServerData *sd, gconstpointer test_data) +{ + SoupSession *session; + SoupMessage *msg; + SoupBuffer *index; + char *md5; + + server_add_early_handler (sd, NULL, early_stream_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + msg = soup_message_new_from_uri ("POST", sd->base_uri); + + index = soup_test_get_index (); + soup_message_body_append (msg->request_body, SOUP_MEMORY_COPY, + index->data, index->length); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, + (guchar *) index->data, index->length); + g_assert_cmpstr (md5, ==, msg->response_body->data); + g_free (md5); + + g_object_unref (msg); + soup_test_session_abort_unref (session); +} + +static void +early_respond_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + if (!strcmp (path, "/")) + soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN); +} + +static void +do_early_respond_test (ServerData *sd, gconstpointer test_data) +{ + SoupSession *session; + SoupMessage *msg; + SoupURI *uri2; + + server_add_early_handler (sd, NULL, early_respond_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + /* The early handler will intercept, and the normal handler will be skipped */ + msg = soup_message_new_from_uri ("GET", sd->base_uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_FORBIDDEN); + g_assert_cmpint (msg->response_body->length, ==, 0); + g_object_unref (msg); + + /* The early handler will ignore this one */ + uri2 = soup_uri_new_with_base (sd->base_uri, "/subdir"); + msg = soup_message_new_from_uri ("GET", uri2); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, "index"); + g_object_unref (msg); + soup_uri_free (uri2); + + soup_test_session_abort_unref (session); +} + +static void +early_multi_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + soup_message_headers_append (msg->response_headers, "X-Early", "yes"); +} + +static void +do_early_multi_test (ServerData *sd, gconstpointer test_data) +{ + SoupSession *session; + SoupMessage *msg; + SoupURI *uri; + struct { + const char *path; + gboolean expect_normal, expect_early; + } multi_tests[] = { + { "/", FALSE, FALSE }, + { "/normal", TRUE, FALSE }, + { "/normal/subdir", TRUE, FALSE }, + { "/normal/early", FALSE, TRUE }, + { "/normal/early/subdir", FALSE, TRUE }, + { "/early", FALSE, TRUE }, + { "/early/subdir", FALSE, TRUE }, + { "/early/normal", TRUE, FALSE }, + { "/early/normal/subdir", TRUE, FALSE }, + { "/both", TRUE, TRUE }, + { "/both/subdir", TRUE, TRUE } + }; + int i; + const char *header; + + server_add_handler (sd, "/normal", server_callback, NULL, NULL); + server_add_early_handler (sd, "/normal/early", early_multi_callback, NULL, NULL); + server_add_early_handler (sd, "/early", early_multi_callback, NULL, NULL); + server_add_handler (sd, "/early/normal", server_callback, NULL, NULL); + server_add_handler (sd, "/both", server_callback, NULL, NULL); + server_add_early_handler (sd, "/both", early_multi_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + for (i = 0; i < G_N_ELEMENTS (multi_tests); i++) { + uri = soup_uri_new_with_base (sd->base_uri, multi_tests[i].path); + msg = soup_message_new_from_uri ("GET", uri); + soup_uri_free (uri); + + soup_session_send_message (session, msg); + + /* The normal handler sets status to OK. The early handler doesn't + * touch status, meaning that if it runs and the normal handler doesn't, + * then SoupServer will set the status to INTERNAL_SERVER_ERROR + * (since a handler ran, but didn't set the status). If neither handler + * runs then SoupServer will set the status to NOT_FOUND. + */ + if (multi_tests[i].expect_normal) + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + else if (multi_tests[i].expect_early) + soup_test_assert_message_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + else + soup_test_assert_message_status (msg, SOUP_STATUS_NOT_FOUND); + + header = soup_message_headers_get_one (msg->response_headers, "X-Early"); + if (multi_tests[i].expect_early) + g_assert_cmpstr (header, ==, "yes"); + else + g_assert_cmpstr (header, ==, NULL); + if (multi_tests[i].expect_normal) + g_assert_cmpstr (msg->response_body->data, ==, "index"); + else + g_assert_cmpint (msg->response_body->length, ==, 0); + + g_object_unref (msg); + } + + soup_test_session_abort_unref (session); +} + +typedef struct { + GIOStream *iostream; + GInputStream *istream; + GOutputStream *ostream; + + gssize nread, nwrote; + guchar *buffer; +} TunnelEnd; + +typedef struct { + SoupServer *self; + SoupMessage *msg; + SoupClientContext *context; + GCancellable *cancellable; + + TunnelEnd client, server; +} Tunnel; + +#define BUFSIZE 8192 + +static void tunnel_read_cb (GObject *object, + GAsyncResult *result, + gpointer user_data); + +static void +tunnel_close (Tunnel *tunnel) +{ + if (tunnel->cancellable) { + g_cancellable_cancel (tunnel->cancellable); + g_object_unref (tunnel->cancellable); + } + + if (tunnel->client.iostream) { + g_io_stream_close (tunnel->client.iostream, NULL, NULL); + g_object_unref (tunnel->client.iostream); + } + if (tunnel->server.iostream) { + g_io_stream_close (tunnel->server.iostream, NULL, NULL); + g_object_unref (tunnel->server.iostream); + } + + g_free (tunnel->client.buffer); + g_free (tunnel->server.buffer); + + g_clear_object (&tunnel->self); + g_clear_object (&tunnel->msg); + + g_free (tunnel); +} + +static void +tunnel_wrote_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + Tunnel *tunnel = user_data; + TunnelEnd *write_end, *read_end; + GError *error = NULL; + gssize nwrote; + + nwrote = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, &error); + if (nwrote <= 0) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + return; + } else if (error) { + g_print ("Tunnel write failed: %s\n", error->message); + g_error_free (error); + } + tunnel_close (tunnel); + return; + } + + if (object == (GObject *)tunnel->client.ostream) { + write_end = &tunnel->client; + read_end = &tunnel->server; + } else { + write_end = &tunnel->server; + read_end = &tunnel->client; + } + + write_end->nwrote += nwrote; + if (write_end->nwrote < read_end->nread) { + g_output_stream_write_async (write_end->ostream, + read_end->buffer + write_end->nwrote, + read_end->nread - write_end->nwrote, + G_PRIORITY_DEFAULT, tunnel->cancellable, + tunnel_wrote_cb, tunnel); + } else { + g_input_stream_read_async (read_end->istream, + read_end->buffer, BUFSIZE, + G_PRIORITY_DEFAULT, tunnel->cancellable, + tunnel_read_cb, tunnel); + } +} + +static void +tunnel_read_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + Tunnel *tunnel = user_data; + TunnelEnd *read_end, *write_end; + GError *error = NULL; + gssize nread; + + nread = g_input_stream_read_finish (G_INPUT_STREAM (object), result, &error); + if (nread <= 0) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + return; + } else if (error) { + g_print ("Tunnel read failed: %s\n", error->message); + g_error_free (error); + } + tunnel_close (tunnel); + return; + } + + if (object == (GObject *)tunnel->client.istream) { + read_end = &tunnel->client; + write_end = &tunnel->server; + } else { + read_end = &tunnel->server; + write_end = &tunnel->client; + } + + read_end->nread = nread; + write_end->nwrote = 0; + g_output_stream_write_async (write_end->ostream, + read_end->buffer, read_end->nread, + G_PRIORITY_DEFAULT, tunnel->cancellable, + tunnel_wrote_cb, tunnel); +} + +static void +start_tunnel (SoupMessage *msg, gpointer user_data) +{ + Tunnel *tunnel = user_data; + + tunnel->client.iostream = soup_client_context_steal_connection (tunnel->context); + tunnel->client.istream = g_io_stream_get_input_stream (tunnel->client.iostream); + tunnel->client.ostream = g_io_stream_get_output_stream (tunnel->client.iostream); + g_clear_object (&tunnel->self); + g_clear_object (&tunnel->msg); + + tunnel->client.buffer = g_malloc (BUFSIZE); + tunnel->server.buffer = g_malloc (BUFSIZE); + + tunnel->cancellable = g_cancellable_new (); + + g_input_stream_read_async (tunnel->client.istream, + tunnel->client.buffer, BUFSIZE, + G_PRIORITY_DEFAULT, tunnel->cancellable, + tunnel_read_cb, tunnel); + g_input_stream_read_async (tunnel->server.istream, + tunnel->server.buffer, BUFSIZE, + G_PRIORITY_DEFAULT, tunnel->cancellable, + tunnel_read_cb, tunnel); +} + + +static void +tunnel_connected_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + Tunnel *tunnel = user_data; + GError *error = NULL; + + tunnel->server.iostream = (GIOStream *) + g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object), result, &error); + if (!tunnel->server.iostream) { + soup_message_set_status (tunnel->msg, SOUP_STATUS_BAD_GATEWAY); + soup_message_set_response (tunnel->msg, "text/plain", + SOUP_MEMORY_COPY, + error->message, strlen (error->message)); + g_error_free (error); + soup_server_unpause_message (tunnel->self, tunnel->msg); + tunnel_close (tunnel); + return; + } + + tunnel->server.istream = g_io_stream_get_input_stream (tunnel->server.iostream); + tunnel->server.ostream = g_io_stream_get_output_stream (tunnel->server.iostream); + + soup_message_set_status (tunnel->msg, SOUP_STATUS_OK); + soup_server_unpause_message (tunnel->self, tunnel->msg); + g_signal_connect (tunnel->msg, "wrote-body", + G_CALLBACK (start_tunnel), tunnel); +} + +static void +proxy_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + GSocketClient *sclient; + SoupURI *dest_uri; + Tunnel *tunnel; + + if (msg->method != SOUP_METHOD_CONNECT) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_server_pause_message (server, msg); + + tunnel = g_new0 (Tunnel, 1); + tunnel->self = g_object_ref (server); + tunnel->msg = g_object_ref (msg); + tunnel->context = context; + + dest_uri = soup_message_get_uri (msg); + sclient = g_socket_client_new (); + g_socket_client_connect_to_host_async (sclient, dest_uri->host, dest_uri->port, + NULL, tunnel_connected_cb, tunnel); + g_object_unref (sclient); +} + +static void +do_steal_connect_test (ServerData *sd, gconstpointer test_data) +{ + SoupServer *proxy; + SoupURI *proxy_uri; + SoupSession *session; + SoupMessage *msg; + const char *handled_by; + + SOUP_TEST_SKIP_IF_NO_TLS; + + proxy = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + proxy_uri = soup_test_server_get_uri (proxy, SOUP_URI_SCHEME_HTTP, "127.0.0.1"); + soup_server_add_handler (proxy, NULL, proxy_server_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION, + SOUP_SESSION_PROXY_URI, proxy_uri, + NULL); + msg = soup_message_new_from_uri ("GET", sd->ssl_base_uri); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + handled_by = soup_message_headers_get_one (msg->response_headers, "X-Handled-By"); + g_assert_cmpstr (handled_by, ==, "server_callback"); + + g_object_unref (msg); + soup_test_session_abort_unref (session); + + soup_test_server_quit_unref (proxy); + soup_uri_free (proxy_uri); +} + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, NULL); + + g_test_add ("/server/OPTIONS *", ServerData, NULL, + server_setup, do_star_test, server_teardown); + g_test_add ("/server/aliases", ServerData, NULL, + server_setup, do_server_aliases_test, server_teardown); + g_test_add ("/server/..-in-path", ServerData, NULL, + server_setup, do_dot_dot_test, server_teardown); + g_test_add ("/server/ipv6", ServerData, NULL, + NULL, do_ipv6_test, server_teardown); + g_test_add ("/server/multi/port", ServerData, NULL, + NULL, do_multi_port_test, server_teardown); + g_test_add ("/server/multi/scheme", ServerData, NULL, + NULL, do_multi_scheme_test, server_teardown); + g_test_add ("/server/multi/family", ServerData, NULL, + NULL, do_multi_family_test, server_teardown); + g_test_add_func ("/server/import/gsocket", do_gsocket_import_test); + g_test_add_func ("/server/import/fd", do_fd_import_test); + g_test_add_func ("/server/accept/iostream", do_iostream_accept_test); + g_test_add ("/server/fail/404", ServerData, NULL, + server_setup_nohandler, do_fail_404_test, server_teardown); + g_test_add ("/server/fail/500", ServerData, GINT_TO_POINTER (FALSE), + server_setup_nohandler, do_fail_500_test, server_teardown); + g_test_add ("/server/fail/500-pause", ServerData, GINT_TO_POINTER (TRUE), + server_setup_nohandler, do_fail_500_test, server_teardown); + g_test_add ("/server/early/stream", ServerData, NULL, + server_setup_nohandler, do_early_stream_test, server_teardown); + g_test_add ("/server/early/respond", ServerData, NULL, + server_setup, do_early_respond_test, server_teardown); + g_test_add ("/server/early/multi", ServerData, NULL, + server_setup_nohandler, do_early_multi_test, server_teardown); + g_test_add ("/server/steal/CONNECT", ServerData, NULL, + server_setup, do_steal_connect_test, server_teardown); + + ret = g_test_run (); + test_cleanup (); return ret; } diff --git a/tests/session-test.c b/tests/session-test.c index 15072058..8957a258 100644 --- a/tests/session-test.c +++ b/tests/session-test.c @@ -25,7 +25,7 @@ server_handler (SoupServer *server, gpointer user_data) { if (!strcmp (path, "/request-timeout")) { - GMainContext *context = soup_server_get_async_context (server); + GMainContext *context = g_main_context_get_thread_default (); GSource *timer; timer = g_timeout_source_new (100); @@ -57,8 +57,7 @@ cancel_message_cb (SoupMessage *msg, gpointer session) } static void -do_test_for_session (SoupSession *session, - const char *uri, +do_test_for_session (SoupSession *session, SoupURI *uri, gboolean queue_is_async, gboolean send_is_blocking, gboolean cancel_is_immediate) @@ -66,17 +65,17 @@ do_test_for_session (SoupSession *session, SoupMessage *msg; gboolean finished, local_timeout; guint timeout_id; - char *timeout_uri; + SoupURI *timeout_uri; debug_printf (1, " queue_message\n"); debug_printf (2, " requesting timeout\n"); - timeout_uri = g_strdup_printf ("%s/request-timeout", uri); - msg = soup_message_new ("GET", timeout_uri); - g_free (timeout_uri); + timeout_uri = soup_uri_new_with_base (uri, "/request-timeout"); + msg = soup_message_new_from_uri ("GET", timeout_uri); + soup_uri_free (timeout_uri); soup_session_send_message (session, msg); g_object_unref (msg); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); server_processed_message = timeout = finished = FALSE; soup_session_queue_message (session, msg, finished_cb, &finished); while (!timeout) @@ -98,10 +97,11 @@ do_test_for_session (SoupSession *session, } debug_printf (1, " send_message\n"); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); server_processed_message = local_timeout = FALSE; timeout_id = g_idle_add_full (G_PRIORITY_HIGH, timeout_cb, &local_timeout, NULL); soup_session_send_message (session, msg); + g_object_unref (msg); g_assert_true (server_processed_message); @@ -120,7 +120,7 @@ do_test_for_session (SoupSession *session, return; debug_printf (1, " cancel_message\n"); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); g_object_ref (msg); finished = FALSE; soup_session_queue_message (session, msg, finished_cb, &finished); @@ -140,14 +140,16 @@ do_test_for_session (SoupSession *session, while (!finished) g_main_context_iteration (NULL, TRUE); } + g_main_loop_unref (loop); soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); g_object_unref (msg); } static void -do_plain_tests (gconstpointer uri) +do_plain_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -156,8 +158,9 @@ do_plain_tests (gconstpointer uri) } static void -do_async_tests (gconstpointer uri) +do_async_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -166,8 +169,9 @@ do_async_tests (gconstpointer uri) } static void -do_sync_tests (gconstpointer uri) +do_sync_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); @@ -194,7 +198,7 @@ priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user static void do_priority_tests (gconstpointer data) { - const char *uri = data; + SoupURI *uri = (SoupURI *)data; SoupSession *session; int i, finished_count = 0; SoupMessagePriority priorities[] = @@ -212,12 +216,14 @@ do_priority_tests (gconstpointer data) expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW; for (i = 0; i < 3; i++) { - char *msg_uri; + SoupURI *msg_uri; SoupMessage *msg; + char buf[5]; - msg_uri = g_strdup_printf ("%s/%d", uri, i); - msg = soup_message_new ("GET", uri); - g_free (msg_uri); + g_snprintf (buf, sizeof (buf), "%d", i); + msg_uri = soup_uri_new_with_base (uri, buf); + msg = soup_message_new_from_uri ("GET", msg_uri); + soup_uri_free (msg_uri); soup_message_set_priority (msg, priorities[i]); soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count); @@ -368,16 +374,14 @@ int main (int argc, char **argv) { SoupServer *server; - char *uri, *timeout_uri; + SoupURI *uri; int ret; test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u", - soup_server_get_port (server)); - timeout_uri = g_strdup_printf ("%s/request-timeout", uri); + uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/session/SoupSession", uri, do_plain_tests); g_test_add_data_func ("/session/SoupSessionAsync", uri, do_async_tests); @@ -387,8 +391,7 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); - g_free (timeout_uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c index 32fad9fc..7b391178 100644 --- a/tests/sniffing-test.c +++ b/tests/sniffing-test.c @@ -440,10 +440,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -529,6 +528,10 @@ main (int argc, char **argv) g_test_add_data_func ("/sniffing/type/unknown-leading-space", "unknown/leading_space.html => text/html", do_sniffing_test); + /* https://bugs.webkit.org/show_bug.cgi?id=173923 */ + g_test_add_data_func ("/sniffing/type/unknown-xml", + "unknown/misc.xml => text/xml", + do_sniffing_test); /* Test the XML sniffing path */ g_test_add_data_func ("/sniffing/type/xml", diff --git a/tests/socket-test.c b/tests/socket-test.c index 5bcc3b0c..5b2b390e 100644 --- a/tests/socket-test.c +++ b/tests/socket-test.c @@ -5,7 +5,9 @@ */ #include "test-utils.h" +#include "libsoup/soup-socket-private.h" +#include <fcntl.h> #include <gio/gnetworking.h> static void @@ -74,7 +76,10 @@ do_unconnected_socket_test (void) /* listening socket fails with ENOTCONN */ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - "*endpoint is not connected*"); + /* We can't check the error message since it comes from + * libc and is locale-dependent. + */ + "*"); addr = soup_socket_get_remote_address (sock); g_test_assert_expected_messages (); g_assert_null (addr); @@ -82,6 +87,7 @@ do_unconnected_socket_test (void) soup_socket_disconnect (sock); g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + /* This error message comes from soup-socket.c though */ "*socket not connected*"); addr = soup_socket_get_remote_address (sock); g_test_assert_expected_messages (); @@ -108,6 +114,230 @@ do_unconnected_socket_test (void) g_object_unref (sock); } +static void +do_socket_from_fd_client_test (void) +{ + SoupServer *server; + SoupURI *uri; + GSocket *gsock; + SoupSocket *sock; + SoupAddress *local, *remote; + GSocketAddress *gaddr; + gboolean is_server; + GError *error = NULL; + + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", uri->port); + g_socket_connect (gsock, gaddr, NULL, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_assert_true (g_socket_is_connected (gsock)); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + + sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, + SOUP_SOCKET_FD, g_socket_get_fd (gsock), + NULL); + g_assert_no_error (error); + g_assert_nonnull (sock); + + g_object_get (G_OBJECT (sock), + SOUP_SOCKET_LOCAL_ADDRESS, &local, + SOUP_SOCKET_REMOTE_ADDRESS, &remote, + SOUP_SOCKET_IS_SERVER, &is_server, + NULL); + g_assert_cmpint (soup_socket_get_fd (sock), ==, g_socket_get_fd (gsock)); + g_assert_false (is_server); + g_assert_true (soup_socket_is_connected (sock)); + + g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1"); + g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); + g_assert_cmpstr (soup_address_get_physical (remote), ==, "127.0.0.1"); + g_assert_cmpint (soup_address_get_port (remote), ==, uri->port); + + g_object_unref (local); + g_object_unref (remote); + g_object_unref (gaddr); + + g_object_unref (sock); + g_object_unref (gsock); + + soup_test_server_quit_unref (server); + soup_uri_free (uri); +} + +static void +do_socket_from_fd_server_test (void) +{ + GSocket *gsock; + SoupSocket *sock; + SoupAddress *local; + GSocketAddress *gaddr; + gboolean is_server; + GError *error = NULL; + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + g_assert_false (g_socket_is_connected (gsock)); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + + sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, + SOUP_SOCKET_GSOCKET, gsock, + NULL); + g_assert_no_error (error); + g_assert_nonnull (sock); + + g_object_get (G_OBJECT (sock), + SOUP_SOCKET_LOCAL_ADDRESS, &local, + SOUP_SOCKET_IS_SERVER, &is_server, + NULL); + g_assert_cmpint (soup_socket_get_fd (sock), ==, g_socket_get_fd (gsock)); + g_assert_true (is_server); + g_assert_true (soup_socket_is_connected (sock)); + + g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1"); + g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); + g_object_unref (local); + g_object_unref (gaddr); + + g_object_unref (sock); + + /* Closing the SoupSocket should have closed the GSocket */ + g_assert_true (g_socket_is_closed (gsock)); + + g_object_unref (gsock); +} + +static void +do_socket_from_fd_bad_test (void) +{ + GSocket *gsock, *gsock2, *gsockcli; + SoupSocket *sock, *sock2; + SoupAddress *local, *remote; + GSocketAddress *gaddr; + gboolean is_server; + int fd; + GError *error = NULL; + + /* Importing a non-socket fd gives an error */ + fd = open (g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL), O_RDONLY); + g_assert_cmpint (fd, !=, -1); + + sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, + SOUP_SOCKET_FD, fd, + NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + g_assert_null (sock); + close (fd); + + /* Importing an unconnected socket gives an error */ + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + g_assert_false (g_socket_is_connected (gsock)); + + sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, + SOUP_SOCKET_FD, g_socket_get_fd (gsock), + NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + g_assert_null (sock); + g_object_unref (gsock); + + /* Importing a non-listening server-side socket works, but + * gives the wrong answer for soup_socket_is_server(). + */ + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + g_assert_false (g_socket_is_connected (gsock)); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + + gsockcli = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + g_socket_connect (gsockcli, gaddr, NULL, &error); + g_assert_no_error (error); + g_assert_true (g_socket_is_connected (gsockcli)); + + gsock2 = g_socket_accept (gsock, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (gsock2); + + sock2 = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, + SOUP_SOCKET_GSOCKET, gsock2, + NULL); + g_assert_no_error (error); + g_assert_nonnull (sock2); + + g_object_get (G_OBJECT (sock2), + SOUP_SOCKET_LOCAL_ADDRESS, &local, + SOUP_SOCKET_REMOTE_ADDRESS, &remote, + SOUP_SOCKET_IS_SERVER, &is_server, + NULL); + g_assert_cmpint (soup_socket_get_fd (sock2), ==, g_socket_get_fd (gsock2)); + g_assert_true (soup_socket_is_connected (sock2)); + /* This is wrong, but can't be helped. */ + g_assert_false (is_server); + + g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1"); + g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); + g_object_unref (gaddr); + + gaddr = g_socket_get_local_address (gsockcli, &error); + g_assert_no_error (error); + g_assert_cmpstr (soup_address_get_physical (remote), ==, "127.0.0.1"); + g_assert_cmpint (soup_address_get_port (remote), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); + g_object_unref (gaddr); + + g_object_unref (local); + g_object_unref (remote); + + g_object_unref (sock2); + + g_object_unref (gsock); + g_object_unref (gsock2); + g_object_unref (gsockcli); +} + int main (int argc, char **argv) { @@ -116,6 +346,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); g_test_add_func ("/sockets/unconnected", do_unconnected_socket_test); + g_test_add_func ("/sockets/from-fd/client", do_socket_from_fd_client_test); + g_test_add_func ("/sockets/from-fd/server", do_socket_from_fd_server_test); + g_test_add_func ("/sockets/from-fd/bad", do_socket_from_fd_bad_test); ret = g_test_run (); diff --git a/tests/soup-tests.gresource.xml b/tests/soup-tests.gresource.xml index b24a7297..9c08d170 100644 --- a/tests/soup-tests.gresource.xml +++ b/tests/soup-tests.gresource.xml @@ -13,6 +13,7 @@ <file>resources/mbox.gz</file> <file>resources/mbox.raw</file> <file>resources/mbox.zlib</file> + <file>resources/misc.xml</file> <file>resources/ps_binary.ps</file> <file>resources/rss20.xml</file> <file>resources/test.aiff</file> diff --git a/tests/ssl-test.c b/tests/ssl-test.c index e6bbb615..735ba416 100644 --- a/tests/ssl-test.c +++ b/tests/ssl-test.c @@ -2,16 +2,17 @@ #include "test-utils.h" -static char *uri; +SoupURI *uri; +GTlsDatabase *null_tlsdb; static void -do_properties_test_for_session (SoupSession *session, const char *uri) +do_properties_test_for_session (SoupSession *session) { SoupMessage *msg; GTlsCertificate *cert; GTlsCertificateFlags flags; - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); @@ -36,10 +37,10 @@ do_async_properties_tests (void) session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_CA_FILE, "/dev/null", + SOUP_SESSION_TLS_DATABASE, null_tlsdb, SOUP_SESSION_SSL_STRICT, FALSE, NULL); - do_properties_test_for_session (session, uri); + do_properties_test_for_session (session); soup_test_session_abort_unref (session); } @@ -52,10 +53,10 @@ do_sync_properties_tests (void) session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_CA_FILE, "/dev/null", + SOUP_SESSION_TLS_DATABASE, null_tlsdb, SOUP_SESSION_SSL_STRICT, FALSE, NULL); - do_properties_test_for_session (session, uri); + do_properties_test_for_session (session); soup_test_session_abort_unref (session); } @@ -105,11 +106,11 @@ do_strictness_test (gconstpointer data) } if (!test->with_ca_list) { g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_CA_FILE, "/dev/null", + SOUP_SESSION_TLS_DATABASE, null_tlsdb, NULL); } - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, test->expected_status); @@ -127,6 +128,7 @@ do_strictness_test (gconstpointer data) debug_printf (1, " tls error flags: 0x%x\n", flags); g_object_unref (msg); + soup_test_session_abort_unref (session); } @@ -146,12 +148,21 @@ do_session_property_tests (void) GTlsDatabase *tlsdb; char *ca_file; SoupSession *session; + GParamSpec *pspec; g_test_bug ("673678"); SOUP_TEST_SKIP_IF_NO_TLS; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; session = soup_session_async_new (); + G_GNUC_END_IGNORE_DEPRECATIONS; + + /* Temporarily undeprecate SOUP_SESSION_SSL_CA_FILE to avoid warnings. */ + pspec = g_object_class_find_property (g_type_class_peek (SOUP_TYPE_SESSION), + SOUP_SESSION_SSL_CA_FILE); + pspec->flags &= ~G_PARAM_DEPRECATED; + g_signal_connect (session, "notify::ssl-use-system-ca-file", G_CALLBACK (property_changed), &use_system_changed); g_signal_connect (session, "notify::tls-database", @@ -232,6 +243,175 @@ do_session_property_tests (void) g_assert_true (ca_file_changed); soup_test_session_abort_unref (session); + + /* Re-deprecate SOUP_SESSION_SSL_CA_FILE */ + pspec->flags |= G_PARAM_DEPRECATED; +} + +/* GTlsInteraction subclass for do_interaction_test */ +typedef GTlsInteraction TestTlsInteraction; +typedef GTlsInteractionClass TestTlsInteractionClass; + +GType test_tls_interaction_get_type (void); + +G_DEFINE_TYPE (TestTlsInteraction, test_tls_interaction, G_TYPE_TLS_INTERACTION); + +static void +test_tls_interaction_init (TestTlsInteraction *interaction) +{ + +} + +static GTlsInteractionResult +test_tls_interaction_request_certificate (GTlsInteraction *interaction, + GTlsConnection *connection, + GTlsCertificateRequestFlags flags, + GCancellable *cancellable, + GError **error) +{ + GTlsCertificate *cert; + const char *ssl_cert_file, *ssl_key_file; + GError *my_error = NULL; + + /* Yes, we use the same certificate for the client as for the server. Shrug */ + ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL); + ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL); + cert = g_tls_certificate_new_from_files (ssl_cert_file, + ssl_key_file, + &my_error); + g_assert_no_error (my_error); + + g_tls_connection_set_certificate (connection, cert); + g_object_unref (cert); + + return G_TLS_INTERACTION_HANDLED; +} + +static void +test_tls_interaction_class_init (TestTlsInteractionClass *klass) +{ + GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass); + + interaction_class->request_certificate = test_tls_interaction_request_certificate; +} + + +#define INTERACTION_TEST_HTTP_RESPONSE "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nOK\r\n" + +static gboolean +accept_client_certificate (GTlsConnection *server, + GTlsCertificate *client_cert, + GTlsCertificateFlags errors) +{ + return TRUE; +} + +static void +got_connection (GThreadedSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + GIOStream *tls; + GTlsCertificate *server_cert; + GError *error = NULL; + const char *ssl_cert_file, *ssl_key_file; + GMainContext *thread_context; + + thread_context = g_main_context_new (); + g_main_context_push_thread_default (thread_context); + + ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL); + ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL); + server_cert = g_tls_certificate_new_from_files (ssl_cert_file, + ssl_key_file, + &error); + g_assert_no_error (error); + + tls = g_tls_server_connection_new (G_IO_STREAM (connection), + server_cert, &error); + g_assert_no_error (error); + g_object_unref (server_cert); + + g_object_set (G_OBJECT (tls), + "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED, + NULL); + g_signal_connect (tls, "accept-certificate", + G_CALLBACK (accept_client_certificate), NULL); + + if (g_tls_connection_handshake (G_TLS_CONNECTION (tls), NULL, &error)) { + g_output_stream_write_all (g_io_stream_get_output_stream (tls), + INTERACTION_TEST_HTTP_RESPONSE, + strlen (INTERACTION_TEST_HTTP_RESPONSE), + NULL, NULL, &error); + g_assert_no_error (error); + } else { + g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED); + g_clear_error (&error); + } + + g_io_stream_close (tls, NULL, &error); + g_assert_no_error (error); + + g_object_unref (tls); + + g_main_context_pop_thread_default (thread_context); + g_main_context_unref (thread_context); +} + +static void +do_tls_interaction_test (void) +{ + GSocketService *service; + GSocketAddress *address, *bound_address; + SoupSession *session; + SoupMessage *msg; + GTlsInteraction *interaction; + SoupURI *test_uri; + GError *error = NULL; + + SOUP_TEST_SKIP_IF_NO_TLS; + + service = g_threaded_socket_service_new (1); + address = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_listener_add_address (G_SOCKET_LISTENER (service), address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_TCP, + NULL, &bound_address, &error); + g_assert_no_error (error); + g_object_unref (address); + g_signal_connect (service, "run", G_CALLBACK (got_connection), NULL); + g_socket_service_start (service); + + test_uri = soup_uri_new ("https://127.0.0.1"); + soup_uri_set_port (test_uri, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (bound_address))); + g_object_unref (bound_address); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + /* Without a GTlsInteraction */ + msg = soup_message_new_from_uri ("GET", test_uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED); + g_object_unref (msg); + + interaction = g_object_new (test_tls_interaction_get_type (), NULL); + g_object_set (G_OBJECT (session), + SOUP_SESSION_TLS_INTERACTION, interaction, + NULL); + g_object_unref (interaction); + + /* With a GTlsInteraction */ + msg = soup_message_new_from_uri ("GET", test_uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_true (soup_message_get_https_status (msg, NULL, NULL)); + g_object_unref (msg); + + soup_uri_free (test_uri); + soup_test_session_abort_unref (session); + + g_socket_service_stop (service); + g_object_unref (service); } static void @@ -251,21 +431,26 @@ server_handler (SoupServer *server, int main (int argc, char **argv) { - SoupServer *server; + SoupServer *server = NULL; int i, ret; + GError *error = NULL; test_init (argc, argv, NULL); if (tls_available) { - server = soup_test_server_new_ssl (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (server)); - } + uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); + + null_tlsdb = g_tls_file_database_new ("/dev/null", &error); + g_assert_no_error (error); + } else + uri = NULL; g_test_add_func ("/ssl/session-properties", do_session_property_tests); g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests); g_test_add_func ("/ssl/message-properties/sync", do_sync_properties_tests); + g_test_add_func ("/ssl/tls-interaction", do_tls_interaction_test); for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) { g_test_add_data_func (strictness_tests[i].name, @@ -276,8 +461,9 @@ main (int argc, char **argv) ret = g_test_run (); if (tls_available) { - g_free (uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); + g_object_unref (null_tlsdb); } test_cleanup (); diff --git a/tests/streaming-test.c b/tests/streaming-test.c index 8d8c02ef..bd4a5194 100644 --- a/tests/streaming-test.c +++ b/tests/streaming-test.c @@ -138,7 +138,6 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; SoupURI *base_uri; int ret; @@ -149,15 +148,13 @@ main (int argc, char **argv) (guchar *)full_response->data, full_response->length); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); loop = g_main_loop_new (NULL, TRUE); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/streaming/chunked", base_uri, do_chunked_test); g_test_add_data_func ("/streaming/content-length", base_uri, do_content_length_test); diff --git a/tests/test-cert.pem b/tests/test-cert.pem index 7f206266..ff863b4d 100644 --- a/tests/test-cert.pem +++ b/tests/test-cert.pem @@ -1,17 +1,18 @@ -----BEGIN CERTIFICATE----- -MIICpDCCAYwCCQC8Suc8hjfgujANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDDAkx -MjcuMC4wLjEwHhcNMTEwOTE5MTkyMjA1WhcNMjEwOTE2MTkyMjA1WjAUMRIwEAYD -VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr -OH7kblu+5zkYTk/ZG21OgbIyltxhLDHPmUpl4yDUFqX5BEtoVfg0Ms4ZuaoeDi4t -b2LV6Em3UDQwmwPMm2SakfJvRd3nfL6G3UkkBsVqT3V04M9u8fk6YgHPT8PN1Lj7 -5bv9AMRyQRV1QIPondMhbt8JhlmCR6ALbxYtsXkbQF7qzbj7Y2cjvoHzPQSk0QpB -rEUpj6Schm1NkPen48Z1X1faGL0F3roFHEsf6U1AjP5A4A/UGQsRtq35VzVnKgxW -N7jumUevEMIvyqLjmvK864AHMIRVCOls9GcIta80bViuVqgtuGgVGM/7SoZfIvPF -A10jIe7KQoXWAwRi4WclAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJfihY634dRr -DeEA4SQ1e0/kB6EF8oeaC+5EuGOJxtoX+yXJfWJsEtmjRwobyOBVV997hdOtdZjo -mdJOCKerOFKccO9PLNJZ+/l4+NHv9OwOcu4UqvrSsps/pmr/22SIyQswbLLJfPAT -KjGTDLlj//zrLxzUGsu7lgGsY4s4fVbftFZv7P5AyErpwiFk8qM1BP0NMkn4XWSA -uSyTeB6O+tWYdh3bA1BeKC2P85sl6xFJI2gxvNTxtdcg9beDqNuEheJ+mEtD3P4w -HDG1vFaAX0MH1RJSDO/dIoJerN6LTjiTYYYg8yV0lmBxijv25Z/3Gi33OuG9jkdR -vXDwJpC+/ko= +MIIC2zCCAcOgAwIBAgIJALRbg2WnuAAqMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMMCTEyNy4wLjAuMTAeFw0xNzA2MjAxNDI3MzBaFw0yNzA2MTgxNDI3MzBaMBQx +EjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKs4fuRuW77nORhOT9kbbU6BsjKW3GEsMc+ZSmXjINQWpfkES2hV+DQyzhm5 +qh4OLi1vYtXoSbdQNDCbA8ybZJqR8m9F3ed8vobdSSQGxWpPdXTgz27x+TpiAc9P +w83UuPvlu/0AxHJBFXVAg+id0yFu3wmGWYJHoAtvFi2xeRtAXurNuPtjZyO+gfM9 +BKTRCkGsRSmPpJyGbU2Q96fjxnVfV9oYvQXeugUcSx/pTUCM/kDgD9QZCxG2rflX +NWcqDFY3uO6ZR68Qwi/KouOa8rzrgAcwhFUI6Wz0Zwi1rzRtWK5WqC24aBUYz/tK +hl8i88UDXSMh7spChdYDBGLhZyUCAwEAAaMwMC4wLAYDVR0RBCUwI4IJbG9jYWxo +b3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBj ++U8tebwg5/pof5Rht6TMHqeg6Fcr4OJkL2ph2g+T/AMTS7kEGeFIKJN5AZ+S/qIY +cdoDKHwc8+bCK/mG6DPmJ4z/2Eamb85YhplOLVrLRwfxRebTK9CtnjcjnflAiU9H +7vPVwXIvkwebhBSQNKTdkBlPXKaTNWXuygeFG2OVQkPf/KAxSdtg2R+owv/s802Z +HISk26wY9oFIQz6AiXWdrY1QqNOltZ7rlU5iofAH7X+9ryZlxPWj/gHg2YQRvvLl +dq6nCF+ED0ke7h0lg5nU0beKEygwli8DlLVbu0JK0PkARFp5t7wUtzC9DCjzvfOc +gxR44PyZX7/2oaTDm4PS -----END CERTIFICATE----- diff --git a/tests/test-utils.c b/tests/test-utils.c index bc160aea..9c742060 100644 --- a/tests/test-utils.c +++ b/tests/test-utils.c @@ -66,6 +66,7 @@ test_init (int argc, char **argv, GOptionEntry *entries) setlocale (LC_ALL, ""); g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); g_setenv ("GIO_USE_PROXY_RESOLVER", "dummy", TRUE); + g_setenv ("GIO_USE_VFS", "local", TRUE); name = strrchr (argv[0], '/'); if (!name++) @@ -228,18 +229,31 @@ soup_test_session_new (GType type, ...) va_list args; const char *propname; SoupSession *session; + GTlsDatabase *tlsdb; char *cafile; + GError *error = NULL; va_start (args, type); propname = va_arg (args, const char *); session = (SoupSession *)g_object_new_valist (type, propname, args); va_end (args); - cafile = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); - g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_CA_FILE, cafile, - NULL); - g_free (cafile); + if (tls_available) { + cafile = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); + tlsdb = g_tls_file_database_new (cafile, &error); + g_free (cafile); + if (error) { + if (g_strcmp0 (g_getenv ("GIO_USE_TLS"), "dummy") == 0) + g_clear_error (&error); + else + g_assert_no_error (error); + } + + g_object_set (G_OBJECT (session), + SOUP_SESSION_TLS_DATABASE, tlsdb, + NULL); + g_clear_object (&tlsdb); + } if (http_debug_level && !logger) { SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY); @@ -262,79 +276,240 @@ soup_test_session_abort_unref (SoupSession *session) g_object_unref (session); } -static gpointer run_server_thread (gpointer user_data); +static void +server_listen (SoupServer *server) +{ + GError *error = NULL; + + soup_server_listen_local (server, 0, 0, &error); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } +} + +static GMutex server_start_mutex; +static GCond server_start_cond; -static SoupServer * -test_server_new (gboolean in_own_thread, gboolean ssl) +static gpointer +run_server_thread (gpointer user_data) +{ + SoupServer *server = user_data; + SoupTestServerOptions options = + GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (server), "options")); + GMainContext *context; + GMainLoop *loop; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + loop = g_main_loop_new (context, FALSE); + g_object_set_data (G_OBJECT (server), "GMainLoop", loop); + + if (!(options & SOUP_TEST_SERVER_NO_DEFAULT_LISTENER)) + server_listen (server); + + g_mutex_lock (&server_start_mutex); + g_cond_signal (&server_start_cond); + g_mutex_unlock (&server_start_mutex); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + soup_server_disconnect (server); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); + + return NULL; +} + +SoupServer * +soup_test_server_new (SoupTestServerOptions options) { SoupServer *server; - GMainContext *async_context; - char *ssl_cert_file, *ssl_key_file; - SoupAddress *addr; + GTlsCertificate *cert = NULL; + GError *error = NULL; - async_context = in_own_thread ? g_main_context_new () : NULL; + if (tls_available) { + char *ssl_cert_file, *ssl_key_file; - if (ssl) { ssl_cert_file = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); ssl_key_file = g_test_build_filename (G_TEST_DIST, "test-key.pem", NULL); - } else - ssl_cert_file = ssl_key_file = NULL; - - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); + cert = g_tls_certificate_new_from_files (ssl_cert_file, + ssl_key_file, + &error); + g_free (ssl_cert_file); + g_free (ssl_key_file); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } + } - server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - SOUP_SERVER_ASYNC_CONTEXT, async_context, - SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file, - SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, + server = soup_server_new (SOUP_SERVER_TLS_CERTIFICATE, cert, NULL); - g_object_unref (addr); - if (async_context) - g_main_context_unref (async_context); - g_free (ssl_cert_file); - g_free (ssl_key_file); - - if (!server) { - g_printerr ("Unable to create server\n"); - exit (1); - } + g_clear_object (&cert); + + g_object_set_data (G_OBJECT (server), "options", GUINT_TO_POINTER (options)); - if (in_own_thread) { + if (options & SOUP_TEST_SERVER_IN_THREAD) { GThread *thread; + g_mutex_lock (&server_start_mutex); + thread = g_thread_new ("server_thread", run_server_thread, server); + g_cond_wait (&server_start_cond, &server_start_mutex); + g_mutex_unlock (&server_start_mutex); + g_object_set_data (G_OBJECT (server), "thread", thread); - } else - soup_server_run_async (server); + } else if (!(options & SOUP_TEST_SERVER_NO_DEFAULT_LISTENER)) + server_listen (server); return server; } -SoupServer * -soup_test_server_new (gboolean in_own_thread) +static SoupURI * +find_server_uri (SoupServer *server, const char *scheme, const char *host) +{ + GSList *uris, *u; + SoupURI *uri, *ret_uri = NULL; + + uris = soup_server_get_uris (server); + for (u = uris; u; u = u->next) { + uri = u->data; + + if (scheme && strcmp (uri->scheme, scheme) != 0) + continue; + if (host && strcmp (uri->host, host) != 0) + continue; + + ret_uri = soup_uri_copy (uri); + break; + } + g_slist_free_full (uris, (GDestroyNotify)soup_uri_free); + + return ret_uri; +} + +static SoupURI * +add_listener (SoupServer *server, const char *scheme, const char *host) { - return test_server_new (in_own_thread, FALSE); + SoupServerListenOptions options = 0; + GError *error = NULL; + + if (!g_strcmp0 (scheme, SOUP_URI_SCHEME_HTTPS)) + options |= SOUP_SERVER_LISTEN_HTTPS; + if (!g_strcmp0 (host, "127.0.0.1")) + options |= SOUP_SERVER_LISTEN_IPV4_ONLY; + else if (!g_strcmp0 (host, "::1")) + options |= SOUP_SERVER_LISTEN_IPV6_ONLY; + + soup_server_listen_local (server, 0, options, &error); + g_assert_no_error (error); + + return find_server_uri (server, scheme, host); } -SoupServer * -soup_test_server_new_ssl (gboolean in_own_thread) +typedef struct { + GMutex mutex; + GCond cond; + + SoupServer *server; + const char *scheme; + const char *host; + + SoupURI *uri; +} AddListenerData; + +static gboolean +add_listener_in_thread (gpointer user_data) { - return test_server_new (in_own_thread, TRUE); + AddListenerData *data = user_data; + + data->uri = add_listener (data->server, data->scheme, data->host); + g_mutex_lock (&data->mutex); + g_cond_signal (&data->cond); + g_mutex_unlock (&data->mutex); + + return FALSE; } -static gpointer -run_server_thread (gpointer user_data) +SoupURI * +soup_test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host) { - SoupServer *server = user_data; + SoupURI *uri; + GMainLoop *loop; - soup_server_run (server); - return NULL; + uri = find_server_uri (server, scheme, host); + if (uri) + return uri; + + /* Need to add a new listener */ + loop = g_object_get_data (G_OBJECT (server), "GMainLoop"); + if (loop) { + GMainContext *context = g_main_loop_get_context (loop); + AddListenerData data; + + g_mutex_init (&data.mutex); + g_cond_init (&data.cond); + data.server = server; + data.scheme = scheme; + data.host = host; + data.uri = NULL; + + g_mutex_lock (&data.mutex); + soup_add_completion (context, add_listener_in_thread, &data); + + while (!data.uri) + g_cond_wait (&data.cond, &data.mutex); + + g_mutex_unlock (&data.mutex); + g_mutex_clear (&data.mutex); + g_cond_clear (&data.cond); + uri = data.uri; + } else + uri = add_listener (server, scheme, host); + + return uri; } static gboolean -idle_quit_server (gpointer server) +done_waiting (gpointer user_data) { - soup_server_quit (server); + gboolean *done = user_data; + + *done = TRUE; + return FALSE; +} + +static void +disconnect_and_wait (SoupServer *server, + GMainContext *context) +{ + GSource *source; + gboolean done = FALSE; + + source = g_idle_source_new (); + g_source_set_priority (source, G_PRIORITY_LOW); + g_source_set_callback (source, done_waiting, &done, NULL); + g_source_attach (source, context); + g_source_unref (source); + + soup_server_disconnect (server); + while (!done) + g_main_context_iteration (context, TRUE); +} + +static gboolean +idle_quit_server (gpointer user_data) +{ + SoupServer *server = user_data; + GMainLoop *loop = g_object_get_data (G_OBJECT (server), "GMainLoop"); + + disconnect_and_wait (server, g_main_loop_get_context (loop)); + g_main_loop_quit (loop); return FALSE; } @@ -345,11 +520,17 @@ soup_test_server_quit_unref (SoupServer *server) thread = g_object_get_data (G_OBJECT (server), "thread"); if (thread) { - soup_add_completion (soup_server_get_async_context (server), - idle_quit_server, server); + GMainLoop *loop; + GMainContext *context; + + loop = g_object_get_data (G_OBJECT (server), "GMainLoop"); + context = g_main_loop_get_context (loop); + g_main_context_ref (context); + soup_add_completion (context, idle_quit_server, server); + g_main_context_unref (context); g_thread_join (thread); } else - soup_server_quit (server); + disconnect_and_wait (server, NULL); g_assert_cmpint (G_OBJECT (server)->ref_count, ==, 1); g_object_unref (server); @@ -400,7 +581,7 @@ create_cancel_data (SoupRequest *req, return cancel_data; } -static void inline +inline static void cancel_message_or_cancellable (CancelData *cancel_data) { if (cancel_data->flags & SOUP_TEST_REQUEST_CANCEL_MESSAGE) { @@ -461,7 +642,7 @@ soup_test_request_send (SoupRequest *req, g_timeout_add_full (G_PRIORITY_HIGH, interval, cancel_request_timeout, cancel_data, NULL); } if (cancel_data && (flags & SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE)) - g_cancellable_cancel (cancellable); + cancel_message_or_cancellable (cancel_data); soup_request_send_async (req, cancellable, async_as_sync_callback, &data); g_main_loop_run (data.loop); diff --git a/tests/test-utils.h b/tests/test-utils.h index 03637dcf..0bc065fc 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -52,12 +52,25 @@ typedef enum { SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH = (1 << 5), } SoupTestRequestFlags; +#undef SOUP_TYPE_SESSION_ASYNC +#define SOUP_TYPE_SESSION_ASYNC (_soup_session_async_get_type_undeprecated ()) +#undef SOUP_TYPE_SESSION_SYNC +#define SOUP_TYPE_SESSION_SYNC (_soup_session_sync_get_type_undeprecated ()) + SoupSession *soup_test_session_new (GType type, ...); void soup_test_session_abort_unref (SoupSession *session); -SoupServer *soup_test_server_new (gboolean in_own_thread); -SoupServer *soup_test_server_new_ssl (gboolean in_own_thread); -void soup_test_server_quit_unref (SoupServer *server); +typedef enum { + SOUP_TEST_SERVER_DEFAULT = 0, + SOUP_TEST_SERVER_IN_THREAD = (1 << 0), + SOUP_TEST_SERVER_NO_DEFAULT_LISTENER = (1 << 1) +} SoupTestServerOptions; + +SoupServer *soup_test_server_new (SoupTestServerOptions options); +SoupURI *soup_test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host); +void soup_test_server_quit_unref (SoupServer *server); GInputStream *soup_test_request_send (SoupRequest *req, GCancellable *cancellable, diff --git a/tests/timeout-test.c b/tests/timeout-test.c index 81fb4331..96505e9b 100644 --- a/tests/timeout-test.c +++ b/tests/timeout-test.c @@ -22,7 +22,7 @@ request_started_cb (SoupSession *session, SoupMessage *msg, } static void -do_message_to_session (SoupSession *session, const char *uri, +do_message_to_session (SoupSession *session, SoupURI *uri, const char *comment, guint expected_status) { SoupMessage *msg; @@ -30,7 +30,7 @@ do_message_to_session (SoupSession *session, const char *uri, if (comment) debug_printf (1, " msg %s\n", comment); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); g_signal_connect (msg, "finished", G_CALLBACK (message_finished), &finished); @@ -51,11 +51,11 @@ static void do_msg_tests_for_session (SoupSession *timeout_session, SoupSession *idle_session, SoupSession *plain_session, - const char *fast_uri, - const char *slow_uri) + SoupURI *fast_uri, + SoupURI *slow_uri) { - SoupSocket *ret, *idle_first, *idle_second; - SoupSocket *plain_first, *plain_second; + SoupSocket *ret, *idle_first = NULL, *idle_second; + SoupSocket *plain_first = NULL, *plain_second; if (idle_session) { g_signal_connect (idle_session, "request-started", @@ -100,7 +100,7 @@ do_msg_tests_for_session (SoupSession *timeout_session, } static void -do_request_to_session (SoupSession *session, const char *uri, +do_request_to_session (SoupSession *session, SoupURI *uri, const char *comment, gboolean expect_timeout) { SoupRequest *req; @@ -110,7 +110,7 @@ do_request_to_session (SoupSession *session, const char *uri, gboolean finished = FALSE; debug_printf (1, " req %s\n", comment); - req = soup_session_request (session, uri, NULL); + req = soup_session_request_uri (session, uri, NULL); msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); g_signal_connect (msg, "finished", @@ -149,11 +149,11 @@ static void do_req_tests_for_session (SoupSession *timeout_session, SoupSession *idle_session, SoupSession *plain_session, - const char *fast_uri, - const char *slow_uri) + SoupURI *fast_uri, + SoupURI *slow_uri) { - SoupSocket *ret, *idle_first, *idle_second; - SoupSocket *plain_first, *plain_second; + SoupSocket *ret, *idle_first = NULL, *idle_second; + SoupSocket *plain_first = NULL, *plain_second; if (idle_session) { g_signal_connect (idle_session, "request-started", @@ -201,11 +201,11 @@ static void do_async_timeout_tests (gconstpointer data) { SoupSession *timeout_session, *idle_session, *plain_session; - const char *fast_uri = data; - const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + SoupURI *fast_uri = (SoupURI *)data; + SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow"); gboolean extra_slow; - if (g_str_has_prefix (fast_uri, "https")) { + if (fast_uri->scheme == SOUP_URI_SCHEME_HTTPS) { SOUP_TEST_SKIP_IF_NO_TLS; extra_slow = slow_https; @@ -236,17 +236,19 @@ do_async_timeout_tests (gconstpointer data) soup_test_session_abort_unref (timeout_session); soup_test_session_abort_unref (idle_session); soup_test_session_abort_unref (plain_session); + + soup_uri_free (slow_uri); } static void do_sync_timeout_tests (gconstpointer data) { SoupSession *timeout_session, *plain_session; - const char *fast_uri = data; - const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + SoupURI *fast_uri = (SoupURI *)data; + SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow"); gboolean extra_slow; - if (g_str_has_prefix (fast_uri, "https")) { + if (fast_uri->scheme == SOUP_URI_SCHEME_HTTPS) { SOUP_TEST_SKIP_IF_NO_TLS; extra_slow = slow_https; @@ -263,6 +265,8 @@ do_sync_timeout_tests (gconstpointer data) do_req_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri); soup_test_session_abort_unref (timeout_session); soup_test_session_abort_unref (plain_session); + + soup_uri_free (slow_uri); } static gboolean @@ -290,7 +294,7 @@ server_handler (SoupServer *server, if (!strcmp (path, "/slow")) { soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); - soup_add_timeout (soup_server_get_async_context (server), + soup_add_timeout (g_main_context_get_thread_default (), 4000, timeout_finish_message, msg); } } @@ -299,24 +303,22 @@ int main (int argc, char **argv) { SoupServer *server, *https_server = NULL; - char *uri, *https_uri = NULL; + SoupURI *uri, *https_uri = NULL; int ret; test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); if (tls_available) { SoupSession *test_session; gint64 start, end; - https_server = soup_test_server_new_ssl (TRUE); + https_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (https_server, NULL, server_handler, NULL, NULL); - https_uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (https_server)); + https_uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); /* The 1-second timeouts are too fast for some machines... */ test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -333,7 +335,7 @@ main (int argc, char **argv) slow_https = FALSE; } } else - https_uri = g_strdup ("https://fail."); + https_uri = soup_uri_new ("https://fail."); g_test_add_data_func ("/timeout/http/async", uri, do_async_timeout_tests); g_test_add_data_func ("/timeout/http/sync", uri, do_sync_timeout_tests); @@ -342,8 +344,8 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); - g_free (https_uri); + soup_uri_free (uri); + soup_uri_free (https_uri); soup_test_server_quit_unref (server); if (https_server) soup_test_server_quit_unref (https_server); diff --git a/tests/tld-test.c b/tests/tld-test.c index 4fad8625..31cbb4b8 100644 --- a/tests/tld-test.c +++ b/tests/tld-test.c @@ -38,10 +38,10 @@ static struct { { "a.b.example.uk.com", "example.uk.com", -1 }, { "test.ac", "test.ac", -1 }, /* TLD with only 1 (wildcard) rule. */ - { "cy", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, - { "c.cy", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, - { "b.c.cy", "b.c.cy", -1 }, - { "a.b.c.cy", "b.c.cy", -1 }, + { "bn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "c.bn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "b.c.bn", "b.c.bn", -1 }, + { "a.b.c.bn", "b.c.bn", -1 }, /* More complex TLD. */ { "jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, { "test.jp", "test.jp", -1 }, diff --git a/tests/uri-parsing.c b/tests/uri-parsing.c index d56b655f..85f09b9e 100644 --- a/tests/uri-parsing.c +++ b/tests/uri-parsing.c @@ -151,7 +151,11 @@ static struct { { "http://[fe80::dead:beef%25em1]/", "http://[fe80::dead:beef%25em1]/", NULL, { "http", NULL, NULL, "fe80::dead:beef%em1", 80, "/", NULL, NULL } }, { "http://[fe80::dead:beef%10]/", "http://[fe80::dead:beef%2510]/", NULL, - { "http", NULL, NULL, "fe80::dead:beef%10", 80, "/", NULL, NULL } } + { "http", NULL, NULL, "fe80::dead:beef%10", 80, "/", NULL, NULL } }, + + /* ".." past top */ + { "http://example.com/..", "http://example.com/", "785042", + { "http", NULL, NULL, "example.com", 80, "/", NULL, NULL } }, }; static int num_abs_tests = G_N_ELEMENTS(abs_tests); @@ -493,6 +497,72 @@ do_normalization_tests (void) } } +typedef struct { + const char *uri; + const char *mime_type; + const char *body; +} DataURITest; + +static const DataURITest data_tests[] = { + { "data:text/plain,foo%20bar", + "text/plain", + "foo bar" }, + { "data:text/plain;charset=utf-8,foo%20bar", + "text/plain;charset=utf-8", + "foo bar" }, + { "data:text/plain;base64,Zm9vIGJhcg==", + "text/plain", + "foo bar" }, + { "data:,foo%20bar", + "text/plain;charset=US-ASCII", + "foo bar" }, + { "data:;base64,Zm9vIGJhcg==", + "text/plain;charset=US-ASCII", + "foo bar" }, + { "data:,", + "text/plain;charset=US-ASCII", + "" }, + { "data:text/plain,", + "text/plain", + "" } +}; + +static void +do_data_tests (void) +{ + SoupSession *session; + SoupRequest *req; + GInputStream *stream; + char buf[128]; + gsize nread; + int i; + GError *error = NULL; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + for (i = 0; i < G_N_ELEMENTS (data_tests); i++) { + req = soup_session_request (session, data_tests[i].uri, &error); + g_assert_no_error (error); + + stream = soup_request_send (req, NULL, &error); + g_assert_no_error (error); + + g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error); + + g_assert_no_error (error); + g_assert_cmpint (nread, ==, strlen (data_tests[i].body)); + buf[nread] = 0; + g_assert_cmpstr (buf, ==, data_tests[i].body); + + g_assert_cmpstr (soup_request_get_content_type (req), ==, data_tests[i].mime_type); + + g_input_stream_close (stream, NULL, &error); + g_assert_no_error (error); + g_object_unref (stream); + g_object_unref (req); + } + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -505,6 +575,7 @@ main (int argc, char **argv) g_test_add_func ("/uri/equality", do_equality_tests); g_test_add_func ("/uri/null", do_soup_uri_null_tests); g_test_add_func ("/uri/normalization", do_normalization_tests); + g_test_add_func ("/uri/data", do_data_tests); ret = g_test_run (); diff --git a/tests/websocket-test.c b/tests/websocket-test.c new file mode 100644 index 00000000..722ccbdf --- /dev/null +++ b/tests/websocket-test.c @@ -0,0 +1,971 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * This file was originally part of Cockpit. + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Cockpit 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. + * + * Cockpit 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 Cockpit; If not, see <http://www.gnu.org/licenses/>. + */ + +#include "test-utils.h" + +typedef struct { + GSocket *listener; + gushort port; + + SoupSession *session; + SoupMessage *msg; + SoupWebsocketConnection *client; + GError *client_error; + + SoupServer *soup_server; + SoupWebsocketConnection *server; + + gboolean no_server; + GIOStream *raw_server; + + GMutex mutex; +} Test; + +#define WAIT_UNTIL(cond) \ + G_STMT_START \ + while (!(cond)) g_main_context_iteration (NULL, TRUE); \ + G_STMT_END + +static void +on_error_not_reached (SoupWebsocketConnection *ws, + GError *error, + gpointer user_data) +{ + /* At this point we know this will fail, but is informative */ + g_assert_no_error (error); +} + +static void +on_error_copy (SoupWebsocketConnection *ws, + GError *error, + gpointer user_data) +{ + GError **copy = user_data; + g_assert (*copy == NULL); + *copy = g_error_copy (error); +} + +static void +setup_listener (Test *test) +{ + GSocketAddress *addr; + GError *error = NULL; + + test->listener = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_TCP, + &error); + g_assert_no_error (error); + + addr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_assert_no_error (error); + + g_socket_bind (test->listener, addr, TRUE, &error); + g_assert_no_error (error); + g_object_unref (addr); + + addr = g_socket_get_local_address (test->listener, &error); + g_assert_no_error (error); + + test->port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)); + g_object_unref (addr); + + g_socket_listen (test->listener, &error); + g_assert_no_error (error); +} + +static void +direct_connection_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + GSocketConnection *conn; + SoupURI *uri; + GError *error = NULL; + + conn = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object), + result, &error); + g_assert_no_error (error); + + uri = soup_uri_new ("http://127.0.0.1/"); + test->client = soup_websocket_connection_new (G_IO_STREAM (conn), uri, + SOUP_WEBSOCKET_CONNECTION_CLIENT, + NULL, NULL); + soup_uri_free (uri); + g_object_unref (conn); +} + +static gboolean +got_connection (GSocket *listener, + GIOCondition cond, + gpointer user_data) +{ + Test *test = user_data; + GSocket *sock; + GSocketConnection *conn; + SoupURI *uri; + GError *error = NULL; + + sock = g_socket_accept (listener, NULL, &error); + g_assert_no_error (error); + + conn = g_socket_connection_factory_create_connection (sock); + g_assert (conn != NULL); + g_object_unref (sock); + + if (test->no_server) + test->raw_server = G_IO_STREAM (conn); + else { + uri = soup_uri_new ("http://127.0.0.1/"); + test->server = soup_websocket_connection_new (G_IO_STREAM (conn), uri, + SOUP_WEBSOCKET_CONNECTION_SERVER, + NULL, NULL); + soup_uri_free (uri); + g_object_unref (conn); + } + + return FALSE; +} + +static void +setup_direct_connection (Test *test, + gconstpointer data) +{ + GSocketClient *client; + GSource *listen_source; + + setup_listener (test); + + client = g_socket_client_new (); + g_socket_client_connect_to_host_async (client, "127.0.0.1", test->port, + NULL, direct_connection_complete, test); + + listen_source = g_socket_create_source (test->listener, G_IO_IN, NULL); + g_source_set_callback (listen_source, (GSourceFunc) got_connection, test, NULL); + g_source_attach (listen_source, NULL); + + while (test->client == NULL || (test->server == NULL && test->raw_server == NULL)) + g_main_context_iteration (NULL, TRUE); + + g_source_destroy (listen_source); + g_source_unref (listen_source); + g_object_unref (client); +} + +static void +setup_half_direct_connection (Test *test, + gconstpointer data) +{ + test->no_server = TRUE; + setup_direct_connection (test, data); +} + +static void +teardown_direct_connection (Test *test, + gconstpointer data) +{ + g_clear_object (&test->listener); + g_clear_object (&test->client); + g_clear_object (&test->server); + g_clear_object (&test->raw_server); +} + +static void +setup_soup_server (Test *test, + const char *origin, + const char **protocols, + SoupServerWebsocketCallback callback, + gpointer user_data) +{ + GError *error = NULL; + + setup_listener (test); + + test->soup_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + soup_server_listen_socket (test->soup_server, test->listener, 0, &error); + g_assert_no_error (error); + + soup_server_add_websocket_handler (test->soup_server, "/unix", + origin, (char **) protocols, + callback, user_data, NULL); +} + +static void +client_connect (Test *test, + const char *origin, + const char **protocols, + GAsyncReadyCallback callback, + gpointer user_data) +{ + char *url; + + if (!test->session) + test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + + url = g_strdup_printf ("ws://127.0.0.1:%u/unix", test->port); + test->msg = soup_message_new ("GET", url); + g_free (url); + + soup_session_websocket_connect_async (test->session, test->msg, + origin, (char **) protocols, + NULL, callback, user_data); +} + +static void +got_server_connection (SoupServer *server, + SoupWebsocketConnection *connection, + const char *path, + SoupClientContext *client, + gpointer user_data) +{ + Test *test = user_data; + + test->server = g_object_ref (connection); +} + +static void +got_client_connection (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + test->client = soup_session_websocket_connect_finish (SOUP_SESSION (object), + result, &test->client_error); +} + +static void +setup_soup_connection (Test *test, + gconstpointer data) +{ + setup_soup_server (test, NULL, NULL, got_server_connection, test); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); +} + +static void +teardown_soup_connection (Test *test, + gconstpointer data) +{ + teardown_direct_connection (test, data); + + g_clear_object (&test->msg); + g_clear_error (&test->client_error); + g_clear_pointer (&test->session, soup_test_session_abort_unref); + g_clear_pointer (&test->soup_server, soup_test_server_quit_unref); +} + + +static void +on_text_message (SoupWebsocketConnection *ws, + SoupWebsocketDataType type, + GBytes *message, + gpointer user_data) +{ + GBytes **receive = user_data; + + g_assert_cmpint (type, ==, SOUP_WEBSOCKET_DATA_TEXT); + g_assert (*receive == NULL); + g_assert (message != NULL); + + *receive = g_bytes_ref (message); +} + +static void +on_close_set_flag (SoupWebsocketConnection *ws, + gpointer user_data) +{ + gboolean *flag = user_data; + + g_assert (*flag == FALSE); + + *flag = TRUE; +} + + +static void +test_handshake (Test *test, + gconstpointer data) +{ + g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_OPEN); + g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_OPEN); +} + +#define TEST_STRING "this is a test" + +static void +test_send_client_to_server (Test *test, + gconstpointer data) +{ + GBytes *received = NULL; + const char *contents; + gsize len; + + g_signal_connect (test->server, "message", G_CALLBACK (on_text_message), &received); + + soup_websocket_connection_send_text (test->client, TEST_STRING); + + WAIT_UNTIL (received != NULL); + + /* Received messages should be null terminated (outside of len) */ + contents = g_bytes_get_data (received, &len); + g_assert_cmpstr (contents, ==, TEST_STRING); + g_assert_cmpint (len, ==, strlen (TEST_STRING)); + + g_bytes_unref (received); +} + +static void +test_send_server_to_client (Test *test, + gconstpointer data) +{ + GBytes *received = NULL; + const char *contents; + gsize len; + + g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + + soup_websocket_connection_send_text (test->server, TEST_STRING); + + WAIT_UNTIL (received != NULL); + + /* Received messages should be null terminated (outside of len) */ + contents = g_bytes_get_data (received, &len); + g_assert_cmpstr (contents, ==, TEST_STRING); + g_assert_cmpint (len, ==, strlen (TEST_STRING)); + + g_bytes_unref (received); +} + +static void +test_send_big_packets (Test *test, + gconstpointer data) +{ + GBytes *sent = NULL; + GBytes *received = NULL; + + g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + + sent = g_bytes_new_take (g_strnfill (400, '!'), 400); + soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL)); + WAIT_UNTIL (received != NULL); + g_assert (g_bytes_equal (sent, received)); + g_bytes_unref (sent); + g_bytes_unref (received); + received = NULL; + + sent = g_bytes_new_take (g_strnfill (100 * 1000, '?'), 100 * 1000); + soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL)); + WAIT_UNTIL (received != NULL); + g_assert (g_bytes_equal (sent, received)); + g_bytes_unref (sent); + g_bytes_unref (received); + received = NULL; + + soup_websocket_connection_set_max_incoming_payload_size (test->client, 1000 * 1000 + 1); + g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->client) == (1000 * 1000 + 1)); + soup_websocket_connection_set_max_incoming_payload_size (test->server, 1000 * 1000 + 1); + g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->server) == (1000 * 1000 + 1)); + + sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000); + soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL)); + WAIT_UNTIL (received != NULL); + g_assert (g_bytes_equal (sent, received)); + g_bytes_unref (sent); + g_bytes_unref (received); +} + +static void +test_send_bad_data (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GIOStream *io; + gsize written; + const char *frame; + + g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL); + g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error); + + io = soup_websocket_connection_get_io_stream (test->client); + + /* Bad UTF-8 frame */ + frame = "\x81\x04\xEE\xEE\xEE\xEE"; + if (!g_output_stream_write_all (g_io_stream_get_output_stream (io), + frame, 6, &written, NULL, NULL)) + g_assert_not_reached (); + g_assert_cmpuint (written, ==, 6); + + WAIT_UNTIL (error != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_BAD_DATA); + g_clear_error (&error); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA); +} + +static const char *negotiate_client_protocols[] = { "bbb", "ccc", NULL }; +static const char *negotiate_server_protocols[] = { "aaa", "bbb", "ccc", NULL }; +static const char *negotiated_protocol = "bbb"; + +static void +test_protocol_negotiate_direct (Test *test, + gconstpointer unused) +{ + SoupMessage *msg; + gboolean ok; + const char *protocol; + GError *error = NULL; + + msg = soup_message_new ("GET", "http://127.0.0.1"); + soup_websocket_client_prepare_handshake (msg, NULL, + (char **) negotiate_client_protocols); + + ok = soup_websocket_server_check_handshake (msg, NULL, + (char **) negotiate_server_protocols, + &error); + g_assert_no_error (error); + g_assert_true (ok); + + ok = soup_websocket_server_process_handshake (msg, NULL, + (char **) negotiate_server_protocols); + g_assert_true (ok); + + protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"); + g_assert_cmpstr (protocol, ==, negotiated_protocol); + + ok = soup_websocket_client_verify_handshake (msg, &error); + g_assert_no_error (error); + g_assert_true (ok); + + g_object_unref (msg); +} + +static void +test_protocol_negotiate_soup (Test *test, + gconstpointer unused) +{ + setup_soup_server (test, NULL, negotiate_server_protocols, got_server_connection, test); + client_connect (test, NULL, negotiate_client_protocols, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, negotiated_protocol); + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, negotiated_protocol); +} + +static const char *mismatch_client_protocols[] = { "ddd", NULL }; +static const char *mismatch_server_protocols[] = { "aaa", "bbb", "ccc", NULL }; + +static void +test_protocol_mismatch_direct (Test *test, + gconstpointer unused) +{ + SoupMessage *msg; + gboolean ok; + const char *protocol; + GError *error = NULL; + + msg = soup_message_new ("GET", "http://127.0.0.1"); + soup_websocket_client_prepare_handshake (msg, NULL, + (char **) mismatch_client_protocols); + + ok = soup_websocket_server_check_handshake (msg, NULL, + (char **) mismatch_server_protocols, + &error); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE); + g_clear_error (&error); + g_assert_false (ok); + + ok = soup_websocket_server_process_handshake (msg, NULL, + (char **) mismatch_server_protocols); + g_assert_false (ok); + soup_test_assert_message_status (msg, SOUP_STATUS_BAD_REQUEST); + + protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"); + g_assert_cmpstr (protocol, ==, NULL); + + ok = soup_websocket_client_verify_handshake (msg, &error); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE); + g_clear_error (&error); + g_assert_false (ok); + + g_object_unref (msg); +} + +static void +test_protocol_mismatch_soup (Test *test, + gconstpointer unused) +{ + setup_soup_server (test, NULL, mismatch_server_protocols, got_server_connection, test); + client_connect (test, NULL, mismatch_client_protocols, got_client_connection, test); + WAIT_UNTIL (test->client_error != NULL); + + g_assert_error (test->client_error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET); +} + +static const char *all_protocols[] = { "aaa", "bbb", "ccc", NULL }; + +static void +test_protocol_server_any_direct (Test *test, + gconstpointer unused) +{ + SoupMessage *msg; + gboolean ok; + const char *protocol; + GError *error = NULL; + + msg = soup_message_new ("GET", "http://127.0.0.1"); + soup_websocket_client_prepare_handshake (msg, NULL, (char **) all_protocols); + + ok = soup_websocket_server_check_handshake (msg, NULL, NULL, &error); + g_assert_no_error (error); + g_assert_true (ok); + + ok = soup_websocket_server_process_handshake (msg, NULL, NULL); + g_assert_true (ok); + + protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"); + g_assert_cmpstr (protocol, ==, NULL); + + ok = soup_websocket_client_verify_handshake (msg, &error); + g_assert_no_error (error); + g_assert_true (ok); + + g_object_unref (msg); +} + +static void +test_protocol_server_any_soup (Test *test, + gconstpointer unused) +{ + setup_soup_server (test, NULL, NULL, got_server_connection, test); + client_connect (test, NULL, all_protocols, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, NULL); + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, NULL); + g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Protocol"), ==, NULL); +} + +static void +test_protocol_client_any_direct (Test *test, + gconstpointer unused) +{ + SoupMessage *msg; + gboolean ok; + const char *protocol; + GError *error = NULL; + + msg = soup_message_new ("GET", "http://127.0.0.1"); + soup_websocket_client_prepare_handshake (msg, NULL, NULL); + + ok = soup_websocket_server_check_handshake (msg, NULL, (char **) all_protocols, &error); + g_assert_no_error (error); + g_assert_true (ok); + + ok = soup_websocket_server_process_handshake (msg, NULL, (char **) all_protocols); + g_assert_true (ok); + + protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"); + g_assert_cmpstr (protocol, ==, NULL); + + ok = soup_websocket_client_verify_handshake (msg, &error); + g_assert_no_error (error); + g_assert_true (ok); + + g_object_unref (msg); +} + +static void +test_protocol_client_any_soup (Test *test, + gconstpointer unused) +{ + setup_soup_server (test, NULL, all_protocols, got_server_connection, test); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, NULL); + g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, NULL); + g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Protocol"), ==, NULL); +} + +static void +test_close_clean_client (Test *test, + gconstpointer data) +{ + gboolean close_event_client = FALSE; + gboolean close_event_server = FALSE; + + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); + g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server); + + soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "give me a reason"); + g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED); + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert (close_event_client); + g_assert (close_event_server); + + g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, "give me a reason"); +} + +static void +test_close_clean_server (Test *test, + gconstpointer data) +{ + gboolean close_event_client = FALSE; + gboolean close_event_server = FALSE; + + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); + g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server); + + soup_websocket_connection_close (test->server, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "another reason"); + g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_CLOSING); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED); + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert (close_event_client); + g_assert (close_event_server); + + g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "another reason"); +} + +static gboolean +on_closing_send_message (SoupWebsocketConnection *ws, + gpointer data) +{ + GBytes *message = data; + + soup_websocket_connection_send_text (ws, g_bytes_get_data (message, NULL)); + g_signal_handlers_disconnect_by_func (ws, on_closing_send_message, data); + return TRUE; +} + +static void +test_message_after_closing (Test *test, + gconstpointer data) +{ + gboolean close_event_client = FALSE; + gboolean close_event_server = FALSE; + GBytes *received = NULL; + GBytes *message; + + message = g_bytes_new_static ("another test because", strlen ("another test because")); + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); + g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server); + g_signal_connect (test->server, "closing", G_CALLBACK (on_closing_send_message), message); + + soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "another reason"); + g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED); + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert (close_event_client); + g_assert (close_event_server); + + g_assert (received != NULL); + g_assert (g_bytes_equal (message, received)); + + g_bytes_unref (received); + g_bytes_unref (message); +} + +static gpointer +timeout_server_thread (gpointer user_data) +{ + Test *test = user_data; + GError *error = NULL; + + /* don't close until the client has timed out */ + g_mutex_lock (&test->mutex); + g_mutex_unlock (&test->mutex); + + g_io_stream_close (test->raw_server, NULL, &error); + g_assert_no_error (error); + + return NULL; +} + +static void +test_close_after_timeout (Test *test, + gconstpointer data) +{ + gboolean close_event = FALSE; + GThread *thread; + + g_mutex_lock (&test->mutex); + + /* Note that no real server is around in this test, so no close happens */ + thread = g_thread_new ("timeout-thread", timeout_server_thread, test); + + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event); + g_signal_connect (test->client, "error", G_CALLBACK (on_error_not_reached), NULL); + + /* Now try and close things */ + soup_websocket_connection_close (test->client, 0, NULL); + g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert (close_event == TRUE); + + /* Now actually close the server side stream */ + g_mutex_unlock (&test->mutex); + g_thread_join (thread); +} + +static gpointer +send_fragments_server_thread (gpointer user_data) +{ + Test *test = user_data; + gsize written; + const char fragments[] = "\x01\x04""one " /* !fin | opcode */ + "\x00\x04""two " /* !fin | no opcode */ + "\x80\x05""three"; /* fin | no opcode */ + GError *error = NULL; + + g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server), + fragments, sizeof (fragments) -1, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, sizeof (fragments) - 1); + g_io_stream_close (test->raw_server, NULL, &error); + g_assert_no_error (error); + + return NULL; +} + +static void +test_receive_fragmented (Test *test, + gconstpointer data) +{ + GThread *thread; + GBytes *received = NULL; + GBytes *expect; + + thread = g_thread_new ("fragment-thread", send_fragments_server_thread, test); + + g_signal_connect (test->client, "error", G_CALLBACK (on_error_not_reached), NULL); + g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + + WAIT_UNTIL (received != NULL); + expect = g_bytes_new ("one two three", 13); + g_assert (g_bytes_equal (expect, received)); + g_bytes_unref (expect); + g_bytes_unref (received); + + g_thread_join (thread); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); +} + +static void +test_client_context_got_server_connection (SoupServer *server, + SoupWebsocketConnection *connection, + const char *path, + SoupClientContext *client, + gpointer user_data) +{ + Test *test = user_data; + GSocketAddress *addr; + GInetAddress *iaddr; + char *str; + const char *remote_ip; + + addr = soup_client_context_get_local_address (client); + iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); + str = g_inet_address_to_string (iaddr); + if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4) + g_assert_cmpstr (str, ==, "127.0.0.1"); + else + g_assert_cmpstr (str, ==, "::1"); + g_free (str); + + addr = soup_client_context_get_remote_address (client); + iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); + str = g_inet_address_to_string (iaddr); + if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4) + g_assert_cmpstr (str, ==, "127.0.0.1"); + else + g_assert_cmpstr (str, ==, "::1"); + + remote_ip = soup_client_context_get_host (client); + g_assert_cmpstr (remote_ip, ==, str); + g_free (str); + + test->server = g_object_ref (connection); +} + +static void +test_client_context (Test *test, + gconstpointer unused) +{ + setup_soup_server (test, NULL, NULL, test_client_context_got_server_connection, test); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); +} + +int +main (int argc, + char *argv[]) +{ + int ret; + + test_init (argc, argv, NULL); + + g_test_add ("/websocket/soup/handshake", Test, NULL, + setup_soup_connection, + test_handshake, + teardown_soup_connection); + + g_test_add ("/websocket/direct/send-client-to-server", Test, NULL, + setup_direct_connection, + test_send_client_to_server, + teardown_direct_connection); + g_test_add ("/websocket/soup/send-client-to-server", Test, NULL, + setup_soup_connection, + test_send_client_to_server, + teardown_soup_connection); + + g_test_add ("/websocket/direct/send-server-to-client", Test, NULL, + setup_direct_connection, + test_send_server_to_client, + teardown_direct_connection); + g_test_add ("/websocket/soup/send-server-to-client", Test, NULL, + setup_soup_connection, + test_send_server_to_client, + teardown_soup_connection); + + g_test_add ("/websocket/direct/send-big-packets", Test, NULL, + setup_direct_connection, + test_send_big_packets, + teardown_direct_connection); + g_test_add ("/websocket/soup/send-big-packets", Test, NULL, + setup_soup_connection, + test_send_big_packets, + teardown_soup_connection); + + g_test_add ("/websocket/direct/send-bad-data", Test, NULL, + setup_direct_connection, + test_send_bad_data, + teardown_direct_connection); + g_test_add ("/websocket/soup/send-bad-data", Test, NULL, + setup_soup_connection, + test_send_bad_data, + teardown_soup_connection); + + g_test_add ("/websocket/direct/close-clean-client", Test, NULL, + setup_direct_connection, + test_close_clean_client, + teardown_direct_connection); + g_test_add ("/websocket/soup/close-clean-client", Test, NULL, + setup_soup_connection, + test_close_clean_client, + teardown_soup_connection); + + g_test_add ("/websocket/direct/close-clean-server", Test, NULL, + setup_direct_connection, + test_close_clean_server, + teardown_direct_connection); + g_test_add ("/websocket/soup/close-clean-server", Test, NULL, + setup_soup_connection, + test_close_clean_server, + teardown_soup_connection); + + g_test_add ("/websocket/direct/message-after-closing", Test, NULL, + setup_direct_connection, + test_message_after_closing, + teardown_direct_connection); + g_test_add ("/websocket/soup/message-after-closing", Test, NULL, + setup_soup_connection, + test_message_after_closing, + teardown_soup_connection); + + + g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL, + test_protocol_negotiate_direct, + NULL); + g_test_add ("/websocket/soup/protocol-negotiate", Test, NULL, NULL, + test_protocol_negotiate_soup, + teardown_soup_connection); + + g_test_add ("/websocket/direct/protocol-mismatch", Test, NULL, NULL, + test_protocol_mismatch_direct, + NULL); + g_test_add ("/websocket/soup/protocol-mismatch", Test, NULL, NULL, + test_protocol_mismatch_soup, + teardown_soup_connection); + + g_test_add ("/websocket/direct/protocol-server-any", Test, NULL, NULL, + test_protocol_server_any_direct, + NULL); + g_test_add ("/websocket/soup/protocol-server-any", Test, NULL, NULL, + test_protocol_server_any_soup, + teardown_soup_connection); + + g_test_add ("/websocket/direct/protocol-client-any", Test, NULL, NULL, + test_protocol_client_any_direct, + NULL); + g_test_add ("/websocket/soup/protocol-client-any", Test, NULL, NULL, + test_protocol_client_any_soup, + teardown_soup_connection); + + + g_test_add ("/websocket/direct/receive-fragmented", Test, NULL, + setup_half_direct_connection, + test_receive_fragmented, + teardown_direct_connection); + + if (g_test_slow ()) { + g_test_add ("/websocket/direct/close-after-timeout", Test, NULL, + setup_half_direct_connection, + test_close_after_timeout, + teardown_direct_connection); + } + + g_test_add ("/websocket/soup/client-context", Test, NULL, NULL, + test_client_context, + teardown_soup_connection); + + ret = g_test_run (); + + test_cleanup (); + return ret; +} diff --git a/tests/xmlrpc-old-server-test.c b/tests/xmlrpc-old-server-test.c new file mode 100644 index 00000000..a7076b5a --- /dev/null +++ b/tests/xmlrpc-old-server-test.c @@ -0,0 +1,374 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include "test-utils.h" + +static char *uri; + +#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#endif + +static void +type_error (SoupMessage *msg, GType expected, GValueArray *params, int bad_value) +{ + soup_xmlrpc_set_fault (msg, + SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, + "Bad parameter #%d: expected %s, got %s", + bad_value + 1, g_type_name (expected), + g_type_name (G_VALUE_TYPE (¶ms->values[bad_value]))); +} + +static void +args_error (SoupMessage *msg, GValueArray *params, int expected) +{ + soup_xmlrpc_set_fault (msg, + SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, + "Wrong number of parameters: expected %d, got %d", + expected, params->n_values); +} + +static void +do_sum (SoupMessage *msg, GValueArray *params) +{ + int i; + double sum = 0.0, val; + GValueArray *nums; + + if (params->n_values != 1) { + args_error (msg, params, 1); + return; + } + if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &nums)) { + type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + return; + } + + for (i = 0; i < nums->n_values; i++) { + if (!soup_value_array_get_nth (nums, i, G_TYPE_DOUBLE, &val)) { + type_error (msg, G_TYPE_DOUBLE, nums, i); + return; + } + sum += val; + } + + soup_xmlrpc_set_response (msg, G_TYPE_DOUBLE, sum); + +} + +static void +do_countBools (SoupMessage *msg, GValueArray *params) +{ + int i, trues = 0, falses = 0; + GValueArray *bools; + GHashTable *ret = soup_value_hash_new (); + gboolean val; + + if (params->n_values != 1) { + args_error (msg, params, 1); + return; + } + if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &bools)) { + type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + return; + } + + for (i = 0; i < bools->n_values; i++) { + if (!soup_value_array_get_nth (bools, i, G_TYPE_BOOLEAN, &val)) { + type_error (msg, G_TYPE_BOOLEAN, params, i); + return; + } + if (val) + trues++; + else + falses++; + } + + soup_value_hash_insert (ret, "true", G_TYPE_INT, trues); + soup_value_hash_insert (ret, "false", G_TYPE_INT, falses); + soup_xmlrpc_set_response (msg, G_TYPE_HASH_TABLE, ret); + g_hash_table_destroy (ret); + +} + +static void +do_md5sum (SoupMessage *msg, GValueArray *params) +{ + GChecksum *checksum; + GByteArray *data, *digest; + gsize digest_len = 16; + + if (params->n_values != 1) { + args_error (msg, params, 1); + return; + } + + if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_BYTE_ARRAY, &data)) { + type_error (msg, SOUP_TYPE_BYTE_ARRAY, params, 0); + return; + } + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, data->data, data->len); + digest = g_byte_array_new (); + g_byte_array_set_size (digest, digest_len); + g_checksum_get_digest (checksum, digest->data, &digest_len); + g_checksum_free (checksum); + + soup_xmlrpc_set_response (msg, SOUP_TYPE_BYTE_ARRAY, digest); + g_byte_array_free (digest, TRUE); +} + + +static void +do_dateChange (SoupMessage *msg, GValueArray *params) +{ + GHashTable *arg; + SoupDate *date; + int val; + + if (params->n_values != 2) { + args_error (msg, params, 2); + return; + } + + if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_DATE, &date)) { + type_error (msg, SOUP_TYPE_DATE, params, 0); + return; + } + if (!soup_value_array_get_nth (params, 1, G_TYPE_HASH_TABLE, &arg)) { + type_error (msg, G_TYPE_HASH_TABLE, params, 1); + return; + } + + if (soup_value_hash_lookup (arg, "tm_year", G_TYPE_INT, &val)) + date->year = val + 1900; + if (soup_value_hash_lookup (arg, "tm_mon", G_TYPE_INT, &val)) + date->month = val + 1; + if (soup_value_hash_lookup (arg, "tm_mday", G_TYPE_INT, &val)) + date->day = val; + if (soup_value_hash_lookup (arg, "tm_hour", G_TYPE_INT, &val)) + date->hour = val; + if (soup_value_hash_lookup (arg, "tm_min", G_TYPE_INT, &val)) + date->minute = val; + if (soup_value_hash_lookup (arg, "tm_sec", G_TYPE_INT, &val)) + date->second = val; + + soup_xmlrpc_set_response (msg, SOUP_TYPE_DATE, date); +} + +static void +do_echo (SoupMessage *msg, GValueArray *params) +{ + int i; + const char *val; + GValueArray *in, *out; + + if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &in)) { + type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + return; + } + + out = g_value_array_new (in->n_values); + for (i = 0; i < in->n_values; i++) { + if (!soup_value_array_get_nth (in, i, G_TYPE_STRING, &val)) { + type_error (msg, G_TYPE_STRING, in, i); + return; + } + soup_value_array_append (out, G_TYPE_STRING, val); + } + + soup_xmlrpc_set_response (msg, G_TYPE_VALUE_ARRAY, out); + g_value_array_free (out); +} + +static void +do_ping (SoupMessage *msg, GValueArray *params) +{ + if (params->n_values) { + args_error (msg, params, 0); + return; + } + + soup_xmlrpc_set_response (msg, G_TYPE_STRING, "pong"); +} + +static void +server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + char *method_name; + GValueArray *params; + + if (msg->method != SOUP_METHOD_POST) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_OK); + + if (!soup_xmlrpc_parse_method_call (msg->request_body->data, + msg->request_body->length, + &method_name, ¶ms)) { + soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED, + "Could not parse method call"); + return; + } + + if (!strcmp (method_name, "sum")) + do_sum (msg, params); + else if (!strcmp (method_name, "countBools")) + do_countBools (msg, params); + else if (!strcmp (method_name, "md5sum")) + do_md5sum (msg, params); + else if (!strcmp (method_name, "dateChange")) + do_dateChange (msg, params); + else if (!strcmp (method_name, "echo")) + do_echo (msg, params); + else if (!strcmp (method_name, "ping")) + do_ping (msg, params); + else { + soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND, + "Unknown method %s", method_name); + } + + g_free (method_name); + g_value_array_free (params); +} + +static gboolean +run_xmlrpc_test (char **argv, + char **stdout_out, + char **stderr_out, + GError **error) +{ + gboolean ok; + int status; + + argv[0] = g_test_build_filename (G_TEST_BUILT, "xmlrpc-old-test", NULL); + ok = g_spawn_sync (NULL, argv, NULL, 0, NULL, NULL, + stdout_out, stderr_out, &status, + error); + g_free (argv[0]); + + if (!ok) + return FALSE; + + return g_spawn_check_exit_status (status, error); +} + +static void +do_one_xmlrpc_test (gconstpointer data) +{ + const char *path = data; + char *argv[12]; + char *stdout_out, *stderr_out; + GError *error = NULL; + int arg; + + argv[0] = NULL; + argv[1] = "-S"; + argv[2] = "-U"; + argv[3] = uri; + argv[4] = "-q"; + argv[5] = "-p"; + argv[6] = (char *) path; + + for (arg = 0; arg < debug_level && arg < 3; arg++) + argv[arg + 7] = "-d"; + argv[arg + 7] = NULL; + + run_xmlrpc_test (argv, &stdout_out, &stderr_out, &error); + if (stdout_out) { + g_print ("%s", stdout_out); + g_free (stdout_out); + } + if (stderr_out) { + g_printerr ("%s", stderr_out); + g_free (stderr_out); + } + + if ( g_error_matches (error, G_SPAWN_EXIT_ERROR, 1) + || g_error_matches (error, G_SPAWN_EXIT_ERROR, 77)) + g_test_fail (); + else + g_assert_no_error (error); + g_clear_error (&error); +} + +gboolean run_tests = TRUE; + +static GOptionEntry no_test_entry[] = { + { "no-tests", 'n', G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &run_tests, + "Don't run tests, just run the test server", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + SoupServer *server; + SoupURI *server_uri; + int ret; + + test_init (argc, argv, no_test_entry); + + server = soup_test_server_new (run_tests ? SOUP_TEST_SERVER_IN_THREAD : SOUP_TEST_SERVER_DEFAULT); + soup_server_add_handler (server, "/xmlrpc-server.php", + server_callback, NULL, NULL); + server_uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (server_uri, "/xmlrpc-server.php"); + uri = soup_uri_to_string (server_uri, FALSE); + + if (run_tests) { + char *out, **tests, *path; + char *list_argv[4]; + GError *error = NULL; + int i; + + list_argv[0] = NULL; + list_argv[1] = "-S"; + list_argv[2] = "-l"; + list_argv[3] = NULL; + + if (!run_xmlrpc_test (list_argv, &out, NULL, &error)) { + g_printerr ("'xmlrpc-old-test -l' failed: %s\n", error->message); + g_error_free (error); + return 1; + } + + tests = g_strsplit (out, "\n", -1); + g_free (out); + + for (i = 0; tests[i] && *tests[i]; i++) { + g_assert_true (g_str_has_prefix (tests[i], "/xmlrpc-old/")); + path = g_strdup_printf ("/xmlrpc-old-server/%s", tests[i] + strlen ("/xmlrpc-old/")); + g_test_add_data_func (path, tests[i], do_one_xmlrpc_test); + g_free (path); + } + + ret = g_test_run (); + + g_strfreev (tests); + } else { + GMainLoop *loop; + + g_print ("Listening on port %d\n", server_uri->port); + + loop = g_main_loop_new (NULL, TRUE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + ret = 0; + } + + soup_test_server_quit_unref (server); + soup_uri_free (server_uri); + g_free (uri); + if (run_tests) + test_cleanup (); + return ret; +} diff --git a/tests/xmlrpc-old-test.c b/tests/xmlrpc-old-test.c new file mode 100644 index 00000000..ab7b34d3 --- /dev/null +++ b/tests/xmlrpc-old-test.c @@ -0,0 +1,502 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2001-2003, Ximian, Inc. + */ + +#include "test-utils.h" + +#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#endif + +static SoupSession *session; +static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php"; +static const char *uri = NULL; +static gboolean server_test = FALSE; + +#ifdef HAVE_PHP_XMLRPC +#define SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER +#else +#define SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER \ + G_STMT_START { \ + if (!server_test) { \ + g_test_skip ("php-xmlrpc is not available"); \ + return; \ + } \ + } G_STMT_END +#endif + +static gboolean +send_xmlrpc (const char *body, GValue *retval) +{ + SoupMessage *msg; + GError *err = NULL; + + msg = soup_message_new ("POST", uri); + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, + body, strlen (body)); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + if (!soup_xmlrpc_parse_method_response (msg->response_body->data, + msg->response_body->length, + retval, &err)) { + if (err) { + soup_test_assert (FALSE, "FAULT: %d %s\n", err->code, err->message); + g_error_free (err); + } else + soup_test_assert (FALSE, "ERROR: could not parse response\n"); + g_object_unref (msg); + return FALSE; + } + g_object_unref (msg); + + return TRUE; +} + +static gboolean +do_xmlrpc (const char *method, GValue *retval, ...) +{ + va_list args; + GValueArray *params; + char *body; + gboolean ret; + + va_start (args, retval); + params = soup_value_array_from_args (args); + va_end (args); + + body = soup_xmlrpc_build_method_call (method, params->values, + params->n_values); + g_value_array_free (params); + if (!body) + return FALSE; + + ret = send_xmlrpc (body, retval); + g_free (body); + + return ret; +} + +static gboolean +check_xmlrpc (GValue *value, GType type, ...) +{ + va_list args; + + if (!G_VALUE_HOLDS (value, type)) { + g_assert_true (G_VALUE_HOLDS (value, type)); + return FALSE; + } + + va_start (args, type); + SOUP_VALUE_GETV (value, type, args); + va_end (args); + return TRUE; +} + +static void +test_sum (void) +{ + GValueArray *dbls; + int i; + double val, sum, result; + GValue retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "sum (array of double -> double): "); + + dbls = g_value_array_new (10); + for (i = sum = 0; i < 10; i++) { + val = g_random_int_range (0, 400) / 4.0; + debug_printf (2, "%s%.2f", i == 0 ? "[" : ", ", val); + soup_value_array_append (dbls, G_TYPE_DOUBLE, val); + sum += val; + } + debug_printf (2, "] -> "); + + ok = (do_xmlrpc ("sum", &retval, + G_TYPE_VALUE_ARRAY, dbls, + G_TYPE_INVALID) && + check_xmlrpc (&retval, G_TYPE_DOUBLE, &result)); + g_value_array_free (dbls); + + if (!ok) + return; + + debug_printf (2, "%.2f\n", result); + g_assert_cmpfloat (result, ==, sum); +} + +static void +test_countBools (void) +{ + GValueArray *bools; + int i, trues, falses; + GValue retval; + int ret_trues, ret_falses; + gboolean val, ok; + GHashTable *result; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "countBools (array of boolean -> struct of ints): "); + + bools = g_value_array_new (10); + for (i = trues = falses = 0; i < 10; i++) { + val = g_random_boolean (); + debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F'); + soup_value_array_append (bools, G_TYPE_BOOLEAN, val); + if (val) + trues++; + else + falses++; + } + debug_printf (2, "] -> "); + + ok = (do_xmlrpc ("countBools", &retval, + G_TYPE_VALUE_ARRAY, bools, + G_TYPE_INVALID) && + check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result)); + g_value_array_free (bools); + if (!ok) + return; + + g_assert_true (soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)); + g_assert_true (soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)); + + g_hash_table_destroy (result); + + debug_printf (2, "{ true: %d, false: %d }\n", ret_trues, ret_falses); + g_assert_cmpint (trues, ==, ret_trues); + g_assert_cmpint (falses, ==, ret_falses); +} + +static void +test_md5sum (void) +{ + GByteArray *data, *result; + int i; + GChecksum *checksum; + guchar digest[16]; + gsize digest_len = sizeof (digest); + GValue retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "md5sum (base64 -> base64)\n"); + + data = g_byte_array_new (); + g_byte_array_set_size (data, 256); + for (i = 0; i < data->len; i++) + data->data[i] = (char)(g_random_int_range (0, 256)); + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, data->data, data->len); + g_checksum_get_digest (checksum, digest, &digest_len); + g_checksum_free (checksum); + + ok = (do_xmlrpc ("md5sum", &retval, + SOUP_TYPE_BYTE_ARRAY, data, + G_TYPE_INVALID) && + check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result)); + g_byte_array_free (data, TRUE); + if (!ok) + return; + + soup_assert_cmpmem (result->data, result->len, + digest, digest_len); + g_byte_array_free (result, TRUE); +} + +static void +test_dateChange (void) +{ + GHashTable *structval; + SoupDate *date, *result; + char *timestamp; + GValue retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "dateChange (date, struct of ints -> time)\n"); + + date = soup_date_new (1970 + (g_random_int_range (0, 50)), + 1 + g_random_int_range (0, 12), + 1 + g_random_int_range (0, 28), + g_random_int_range (0, 24), + g_random_int_range (0, 60), + g_random_int_range (0, 60)); + if (debug_level >= 2) { + timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC); + debug_printf (2, "date: %s, {", timestamp); + g_free (timestamp); + } + + structval = soup_value_hash_new (); + +#define MAYBE (g_random_int_range (0, 3) != 0) + + if (MAYBE) { + date->year = 1970 + (g_random_int_range (0, 50)); + debug_printf (2, "tm_year: %d, ", date->year - 1900); + soup_value_hash_insert (structval, "tm_year", + G_TYPE_INT, date->year - 1900); + } + if (MAYBE) { + date->month = 1 + g_random_int_range (0, 12); + debug_printf (2, "tm_mon: %d, ", date->month - 1); + soup_value_hash_insert (structval, "tm_mon", + G_TYPE_INT, date->month - 1); + } + if (MAYBE) { + date->day = 1 + g_random_int_range (0, 28); + debug_printf (2, "tm_mday: %d, ", date->day); + soup_value_hash_insert (structval, "tm_mday", + G_TYPE_INT, date->day); + } + if (MAYBE) { + date->hour = g_random_int_range (0, 24); + debug_printf (2, "tm_hour: %d, ", date->hour); + soup_value_hash_insert (structval, "tm_hour", + G_TYPE_INT, date->hour); + } + if (MAYBE) { + date->minute = g_random_int_range (0, 60); + debug_printf (2, "tm_min: %d, ", date->minute); + soup_value_hash_insert (structval, "tm_min", + G_TYPE_INT, date->minute); + } + if (MAYBE) { + date->second = g_random_int_range (0, 60); + debug_printf (2, "tm_sec: %d, ", date->second); + soup_value_hash_insert (structval, "tm_sec", + G_TYPE_INT, date->second); + } + + debug_printf (2, "} -> "); + + ok = (do_xmlrpc ("dateChange", &retval, + SOUP_TYPE_DATE, date, + G_TYPE_HASH_TABLE, structval, + G_TYPE_INVALID) && + check_xmlrpc (&retval, SOUP_TYPE_DATE, &result)); + g_hash_table_destroy (structval); + if (!ok) { + soup_date_free (date); + return; + } + + if (debug_level >= 2) { + timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC); + debug_printf (2, "%s\n", timestamp); + g_free (timestamp); + } + + g_assert_cmpint (date->year, ==, result->year); + g_assert_cmpint (date->month, ==, result->month); + g_assert_cmpint (date->day, ==, result->day); + g_assert_cmpint (date->hour, ==, result->hour); + g_assert_cmpint (date->minute, ==, result->minute); + g_assert_cmpint (date->second, ==, result->second); + + soup_date_free (date); + soup_date_free (result); +} + +static const char *const echo_strings[] = { + "This is a test", + "& so is this", + "and so is <this>", + "& so is <this>" +}; +#define N_ECHO_STRINGS G_N_ELEMENTS (echo_strings) + +static void +test_echo (void) +{ + GValueArray *originals, *echoes; + GValue retval; + int i; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "echo (array of string -> array of string):\n"); + + originals = g_value_array_new (N_ECHO_STRINGS); + for (i = 0; i < N_ECHO_STRINGS; i++) { + soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]); + debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]); + } + debug_printf (2, "] -> "); + + if (!(do_xmlrpc ("echo", &retval, + G_TYPE_VALUE_ARRAY, originals, + G_TYPE_INVALID) && + check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) { + g_value_array_free (originals); + return; + } + g_value_array_free (originals); + + if (debug_level >= 2) { + for (i = 0; i < echoes->n_values; i++) { + debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", + g_value_get_string (&echoes->values[i])); + } + debug_printf (2, "]\n"); + } + + g_assert_cmpint (echoes->n_values, ==, N_ECHO_STRINGS); + + for (i = 0; i < echoes->n_values; i++) + g_assert_cmpstr (echo_strings[i], ==, g_value_get_string (&echoes->values[i])); + + g_value_array_free (echoes); +} + +static void +test_ping (gconstpointer include_params) +{ + GValueArray *params; + GValue retval; + char *request; + char *out; + gboolean ret; + + g_test_bug ("671661"); + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "ping (void (%s) -> string)\n", + include_params ? "empty <params>" : "no <params>"); + + params = soup_value_array_new (); + request = soup_xmlrpc_build_method_call ("ping", params->values, + params->n_values); + g_value_array_free (params); + if (!request) + return; + + if (!include_params) { + char *params, *end; + + params = strstr (request, "<params/>"); + if (!params) { + soup_test_assert (FALSE, "ERROR: XML did not contain <params/>!"); + return; + } + end = params + strlen ("<params/>"); + memmove (params, end, strlen (end) + 1); + } + + ret = send_xmlrpc (request, &retval); + g_free (request); + + if (!ret || !check_xmlrpc (&retval, G_TYPE_STRING, &out)) + return; + + g_assert_cmpstr (out, ==, "pong"); + + g_free (out); +} + +static void +do_bad_xmlrpc (const char *body) +{ + SoupMessage *msg; + GError *err = NULL; + GValue retval; + + msg = soup_message_new ("POST", uri); + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, + body, strlen (body)); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + if (!soup_xmlrpc_parse_method_response (msg->response_body->data, + msg->response_body->length, + &retval, &err)) { + if (err) { + debug_printf (1, "FAULT: %d %s (OK!)\n", + err->code, err->message); + g_error_free (err); + g_object_unref (msg); + return; + } else + soup_test_assert (FALSE, "ERROR: could not parse response\n"); + } else + soup_test_assert (FALSE, "Unexpectedly got successful response!\n"); + + g_object_unref (msg); +} + +static void +test_fault_malformed (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall/>"); +} + +static void +test_fault_method (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); +} + +static void +test_fault_args (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); +} + +static GOptionEntry xmlrpc_entries[] = { + { "uri", 'U', 0, G_OPTION_ARG_STRING, &uri, + "Alternate URI for server", NULL }, + { "server-test", 'S', 0, G_OPTION_ARG_NONE, &server_test, + "If this is being run from xmlrpc-old-server-test", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, xmlrpc_entries); + + if (!uri && !server_test) { + apache_init (); + uri = default_uri; + } + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + g_test_add_func ("/xmlrpc-old/sum", test_sum); + g_test_add_func ("/xmlrpc-old/countBools", test_countBools); + g_test_add_func ("/xmlrpc-old/md5sum", test_md5sum); + g_test_add_func ("/xmlrpc-old/dateChange", test_dateChange); + g_test_add_func ("/xmlrpc-old/echo", test_echo); + g_test_add_data_func ("/xmlrpc-old/ping/empty-params", GINT_TO_POINTER (TRUE), test_ping); + g_test_add_data_func ("/xmlrpc-old/ping/no-params", GINT_TO_POINTER (FALSE), test_ping); + g_test_add_func ("/xmlrpc-old/fault/malformed", test_fault_malformed); + g_test_add_func ("/xmlrpc-old/fault/method", test_fault_method); + g_test_add_func ("/xmlrpc-old/fault/args", test_fault_args); + + ret = g_test_run (); + + soup_test_session_abort_unref (session); + + test_cleanup (); + return ret; +} diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c index bfeb200a..80f04ea8 100644 --- a/tests/xmlrpc-server-test.c +++ b/tests/xmlrpc-server-test.c @@ -1,196 +1,194 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2008 Red Hat, Inc. + * Copyright 2008 Red Hat, Inc. + * Copyright 2015, Collabora ltd. */ #include "test-utils.h" static char *uri; -#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS -G_GNUC_BEGIN_IGNORE_DEPRECATIONS -#endif - -static void -type_error (SoupMessage *msg, GType expected, GValueArray *params, int bad_value) +static GVariant * +parse_params (SoupMessage *msg, SoupXMLRPCParams *params, const char *signature) { - soup_xmlrpc_set_fault (msg, - SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, - "Bad parameter #%d: expected %s, got %s", - bad_value + 1, g_type_name (expected), - g_type_name (G_VALUE_TYPE (¶ms->values[bad_value]))); -} + GVariant *args; + GError *error = NULL; -static void -args_error (SoupMessage *msg, GValueArray *params, int expected) -{ - soup_xmlrpc_set_fault (msg, - SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, - "Wrong number of parameters: expected %d, got %d", - expected, params->n_values); + args = soup_xmlrpc_params_parse (params, signature, &error); + if (!args) { + soup_xmlrpc_message_set_fault (msg, + SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, + "Wrong method signature: expected %s: %s", + signature, error->message); + } + + return args; } static void -do_sum (SoupMessage *msg, GValueArray *params) +do_sum (SoupMessage *msg, SoupXMLRPCParams *params) { - int sum = 0, i, val; - GValueArray *nums; + GVariant *args; + GVariant *child; + GVariantIter iter; + double sum = 0.0, val; - if (params->n_values != 1) { - args_error (msg, params, 1); - return; - } - if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &nums)) { - type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + if (!(args = parse_params (msg, params, "(ad)"))) return; - } - for (i = 0; i < nums->n_values; i++) { - if (!soup_value_array_get_nth (nums, i, G_TYPE_INT, &val)) { - type_error (msg, G_TYPE_INT, nums, i); - return; - } + child = g_variant_get_child_value (args, 0); + + g_variant_iter_init (&iter, child); + while (g_variant_iter_loop (&iter, "d", &val)) sum += val; - } - soup_xmlrpc_set_response (msg, G_TYPE_INT, sum); + soup_xmlrpc_message_set_response (msg, g_variant_new_double (sum), NULL); + g_variant_unref (args); + g_variant_unref (child); } static void -do_countBools (SoupMessage *msg, GValueArray *params) +do_countBools (SoupMessage *msg, SoupXMLRPCParams *params) { - int i, trues = 0, falses = 0; - GValueArray *bools; - GHashTable *ret = soup_value_hash_new (); + GVariant *args; + GVariant *child; + GVariantIter iter; gboolean val; + int trues = 0, falses = 0; + GVariantDict dict; - if (params->n_values != 1) { - args_error (msg, params, 1); - return; - } - if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &bools)) { - type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + if (!(args = parse_params (msg, params, "(ab)"))) return; - } - for (i = 0; i < bools->n_values; i++) { - if (!soup_value_array_get_nth (bools, i, G_TYPE_BOOLEAN, &val)) { - type_error (msg, G_TYPE_BOOLEAN, params, i); - return; - } + child = g_variant_get_child_value (args, 0); + + g_variant_iter_init (&iter, child); + while (g_variant_iter_loop (&iter, "b", &val)) { if (val) trues++; else falses++; } - soup_value_hash_insert (ret, "true", G_TYPE_INT, trues); - soup_value_hash_insert (ret, "false", G_TYPE_INT, falses); - soup_xmlrpc_set_response (msg, G_TYPE_HASH_TABLE, ret); - g_hash_table_destroy (ret); + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "true", "i", trues); + g_variant_dict_insert (&dict, "false", "i", falses); + + soup_xmlrpc_message_set_response (msg, g_variant_dict_end (&dict), NULL); + g_variant_unref (args); + g_variant_unref (child); } static void -do_md5sum (SoupMessage *msg, GValueArray *params) +do_md5sum (SoupMessage *msg, SoupXMLRPCParams *params) { + GVariant *args; + GVariant *child; GChecksum *checksum; - GByteArray *data, *digest; + GByteArray *digest; gsize digest_len = 16; - if (params->n_values != 1) { - args_error (msg, params, 1); + if (!(args = parse_params (msg, params, "(ay)"))) return; - } - if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_BYTE_ARRAY, &data)) { - type_error (msg, SOUP_TYPE_BYTE_ARRAY, params, 0); - return; - } + child = g_variant_get_child_value (args, 0); + checksum = g_checksum_new (G_CHECKSUM_MD5); - g_checksum_update (checksum, data->data, data->len); + g_checksum_update (checksum, + g_variant_get_data (child), + g_variant_get_size (child)); digest = g_byte_array_new (); g_byte_array_set_size (digest, digest_len); g_checksum_get_digest (checksum, digest->data, &digest_len); g_checksum_free (checksum); - soup_xmlrpc_set_response (msg, SOUP_TYPE_BYTE_ARRAY, digest); + soup_xmlrpc_message_set_response (msg, + g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, + digest->data, digest_len, + TRUE, NULL, NULL), + NULL); g_byte_array_free (digest, TRUE); + g_variant_unref (child); + g_variant_unref (args); } static void -do_dateChange (SoupMessage *msg, GValueArray *params) +do_dateChange (SoupMessage *msg, SoupXMLRPCParams *params) { - GHashTable *arg; + GVariant *args; + GVariant *timestamp; SoupDate *date; + GVariant *arg; int val; + GError *error = NULL; - if (params->n_values != 2) { - args_error (msg, params, 2); + if (!(args = parse_params (msg, params, "(va{si})"))) return; - } - if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_DATE, &date)) { - type_error (msg, SOUP_TYPE_DATE, params, 0); - return; - } - if (!soup_value_array_get_nth (params, 1, G_TYPE_HASH_TABLE, &arg)) { - type_error (msg, G_TYPE_HASH_TABLE, params, 1); - return; + g_variant_get (args, "(v@a{si})", ×tamp, &arg); + + date = soup_xmlrpc_variant_get_datetime (timestamp, &error); + if (!date) { + soup_xmlrpc_message_set_fault (msg, + SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, + "%s", error->message); + g_clear_error (&error); + goto fail; } - if (soup_value_hash_lookup (arg, "tm_year", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_year", "i", &val)) date->year = val + 1900; - if (soup_value_hash_lookup (arg, "tm_mon", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_mon", "i", &val)) date->month = val + 1; - if (soup_value_hash_lookup (arg, "tm_mday", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_mday", "i", &val)) date->day = val; - if (soup_value_hash_lookup (arg, "tm_hour", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_hour", "i", &val)) date->hour = val; - if (soup_value_hash_lookup (arg, "tm_min", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_min", "i", &val)) date->minute = val; - if (soup_value_hash_lookup (arg, "tm_sec", G_TYPE_INT, &val)) + if (g_variant_lookup (arg, "tm_sec", "i", &val)) date->second = val; - soup_xmlrpc_set_response (msg, SOUP_TYPE_DATE, date); + soup_xmlrpc_message_set_response (msg, + soup_xmlrpc_variant_new_datetime (date), + NULL); + + soup_date_free (date); + +fail: + g_variant_unref (args); + g_variant_unref (arg); + g_variant_unref (timestamp); } static void -do_echo (SoupMessage *msg, GValueArray *params) +do_echo (SoupMessage *msg, SoupXMLRPCParams *params) { - int i; - const char *val; - GValueArray *in, *out; + GVariant *args; + GVariant *child; - if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &in)) { - type_error (msg, G_TYPE_VALUE_ARRAY, params, 0); + if (!(args = parse_params (msg, params, "(as)"))) return; - } - out = g_value_array_new (in->n_values); - for (i = 0; i < in->n_values; i++) { - if (!soup_value_array_get_nth (in, i, G_TYPE_STRING, &val)) { - type_error (msg, G_TYPE_STRING, in, i); - return; - } - soup_value_array_append (out, G_TYPE_STRING, val); - } - - soup_xmlrpc_set_response (msg, G_TYPE_VALUE_ARRAY, out); - g_value_array_free (out); + child = g_variant_get_child_value (args, 0); + soup_xmlrpc_message_set_response (msg, child, NULL); + g_variant_unref (args); + g_variant_unref (child); } static void -do_ping (SoupMessage *msg, GValueArray *params) +do_ping (SoupMessage *msg, SoupXMLRPCParams *params) { - if (params->n_values) { - args_error (msg, params, 0); + GVariant *args; + + if (!(args = parse_params (msg, params, "()"))) return; - } - soup_xmlrpc_set_response (msg, G_TYPE_STRING, "pong"); + soup_xmlrpc_message_set_response (msg, g_variant_new_string ("pong"), NULL); + g_variant_unref (args); } static void @@ -199,7 +197,8 @@ server_callback (SoupServer *server, SoupMessage *msg, SoupClientContext *context, gpointer data) { char *method_name; - GValueArray *params; + SoupXMLRPCParams *params; + GError *error = NULL; if (msg->method != SOUP_METHOD_POST) { soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); @@ -208,11 +207,13 @@ server_callback (SoupServer *server, SoupMessage *msg, soup_message_set_status (msg, SOUP_STATUS_OK); - if (!soup_xmlrpc_parse_method_call (msg->request_body->data, - msg->request_body->length, - &method_name, ¶ms)) { - soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED, - "Could not parse method call"); + method_name = soup_xmlrpc_parse_request (msg->request_body->data, + msg->request_body->length, + ¶ms, &error); + if (!method_name) { + soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED, + "Could not parse method call: %s", error->message); + g_clear_error (&error); return; } @@ -229,12 +230,12 @@ server_callback (SoupServer *server, SoupMessage *msg, else if (!strcmp (method_name, "ping")) do_ping (msg, params); else { - soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND, + soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND, "Unknown method %s", method_name); } g_free (method_name); - g_value_array_free (params); + soup_xmlrpc_params_free (params); } static gboolean @@ -310,15 +311,17 @@ int main (int argc, char **argv) { SoupServer *server; + SoupURI *server_uri; int ret; test_init (argc, argv, no_test_entry); - server = soup_test_server_new (run_tests); + server = soup_test_server_new (run_tests ? SOUP_TEST_SERVER_IN_THREAD : SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, "/xmlrpc-server.php", server_callback, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/xmlrpc-server.php", - soup_server_get_port (server)); + server_uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (server_uri, "/xmlrpc-server.php"); + uri = soup_uri_to_string (server_uri, FALSE); if (run_tests) { char *out, **tests, *path; @@ -353,7 +356,7 @@ main (int argc, char **argv) } else { GMainLoop *loop; - g_print ("Listening on port %d\n", soup_server_get_port (server)); + g_print ("Listening on port %d\n", server_uri->port); loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); @@ -363,6 +366,7 @@ main (int argc, char **argv) } soup_test_server_quit_unref (server); + soup_uri_free (server_uri); g_free (uri); if (run_tests) test_cleanup (); diff --git a/tests/xmlrpc-server.php b/tests/xmlrpc-server.php index 66cb2be7..f315b6d0 100644 --- a/tests/xmlrpc-server.php +++ b/tests/xmlrpc-server.php @@ -18,7 +18,7 @@ function sum ($method_name, $params, $app_data) $sum = 0; foreach ($params[0] as $val) { - if (xmlrpc_get_type ($val) != "int") + if (xmlrpc_get_type ($val) != "double") return paramfault(); $sum = $sum + $val; @@ -71,7 +71,7 @@ function echo_ ($method_name, $params, $app_data) function ping ($method_name, $params, $app_data) { - if (count ($params) == 0) + if (is_null ($params) or count ($params) == 0) return "pong"; else return paramfault (); diff --git a/tests/xmlrpc-test.c b/tests/xmlrpc-test.c index f6b20b8f..58d96abc 100644 --- a/tests/xmlrpc-test.c +++ b/tests/xmlrpc-test.c @@ -1,14 +1,11 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2001-2003, Ximian, Inc. + * Copyright 2001-2003, Ximian, Inc. + * Copyright 2015, Collabora ltd. */ #include "test-utils.h" -#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS -G_GNUC_BEGIN_IGNORE_DEPRECATIONS -#endif - static SoupSession *session; static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php"; static const char *uri = NULL; @@ -26,20 +23,8 @@ static gboolean server_test = FALSE; } G_STMT_END #endif -static const char *const value_type[] = { - "BAD", - "int", - "boolean", - "string", - "double", - "datetime", - "base64", - "struct", - "array" -}; - static gboolean -send_xmlrpc (const char *body, GValue *retval) +send_xmlrpc (const char *body, const char *signature, GVariant **retval) { SoupMessage *msg; GError *err = NULL; @@ -51,115 +36,94 @@ send_xmlrpc (const char *body, GValue *retval) soup_test_assert_message_status (msg, SOUP_STATUS_OK); - if (!soup_xmlrpc_parse_method_response (msg->response_body->data, - msg->response_body->length, - retval, &err)) { - if (err) { + *retval = soup_xmlrpc_parse_response (msg->response_body->data, + msg->response_body->length, + signature, &err); + if (!*retval) { + if (err->domain == SOUP_XMLRPC_FAULT) soup_test_assert (FALSE, "FAULT: %d %s\n", err->code, err->message); - g_error_free (err); - } else - soup_test_assert (FALSE, "ERROR: could not parse response\n"); + else + soup_test_assert (FALSE, "ERROR: %s\n", err->message); + g_error_free (err); g_object_unref (msg); return FALSE; } - g_object_unref (msg); return TRUE; } static gboolean -do_xmlrpc (const char *method, GValue *retval, ...) +do_xmlrpc (const char *method, GVariant *args, const char *signature, GVariant **retval) { - va_list args; - GValueArray *params; - char *body; gboolean ret; + char *body; + GError *error = NULL; - va_start (args, retval); - params = soup_value_array_from_args (args); - va_end (args); - - body = soup_xmlrpc_build_method_call (method, params->values, - params->n_values); - g_value_array_free (params); + body = soup_xmlrpc_build_request (method, args, &error); + g_assert_no_error (error); if (!body) return FALSE; - ret = send_xmlrpc (body, retval); + ret = send_xmlrpc (body, signature, retval); g_free (body); return ret; } -static gboolean -check_xmlrpc (GValue *value, GType type, ...) -{ - va_list args; - - if (!G_VALUE_HOLDS (value, type)) { - g_assert_true (G_VALUE_HOLDS (value, type)); - return FALSE; - } - - va_start (args, type); - SOUP_VALUE_GETV (value, type, args); - va_end (args); - return TRUE; -} - static void test_sum (void) { - GValueArray *ints; - int i, val, sum, result; - GValue retval; + GVariantBuilder builder; + int i; + double val, sum, result; + GVariant *retval; gboolean ok; SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; - debug_printf (2, "sum (array of int -> int): "); + debug_printf (2, "sum (array of double -> double): "); - ints = g_value_array_new (10); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("ad")); for (i = sum = 0; i < 10; i++) { - val = g_random_int_range (0, 100); - debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val); - soup_value_array_append (ints, G_TYPE_INT, val); + val = g_random_int_range (0, 400) / 4.0; + debug_printf (2, "%s%.2f", i == 0 ? "[" : ", ", val); + g_variant_builder_add (&builder, "d", val); sum += val; } debug_printf (2, "] -> "); - ok = (do_xmlrpc ("sum", &retval, - G_TYPE_VALUE_ARRAY, ints, - G_TYPE_INVALID) && - check_xmlrpc (&retval, G_TYPE_INT, &result)); - g_value_array_free (ints); + ok = do_xmlrpc ("sum", + g_variant_new ("(@ad)", g_variant_builder_end (&builder)), + "d", &retval); if (!ok) return; - debug_printf (2, "%d\n", result); - g_assert_cmpint (result, ==, sum); + result = g_variant_get_double (retval); + debug_printf (2, "%.2f\n", result); + g_assert_cmpfloat (result, ==, sum); + + g_variant_unref (retval); } static void test_countBools (void) { - GValueArray *bools; + GVariantBuilder builder; int i, trues, falses; - GValue retval; + GVariant *retval; int ret_trues, ret_falses; gboolean val, ok; - GHashTable *result; SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; debug_printf (2, "countBools (array of boolean -> struct of ints): "); - bools = g_value_array_new (10); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("ab")); for (i = trues = falses = 0; i < 10; i++) { val = g_random_boolean (); debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F'); - soup_value_array_append (bools, G_TYPE_BOOLEAN, val); + g_variant_builder_add (&builder, "b", val); if (val) trues++; else @@ -167,18 +131,16 @@ test_countBools (void) } debug_printf (2, "] -> "); - ok = (do_xmlrpc ("countBools", &retval, - G_TYPE_VALUE_ARRAY, bools, - G_TYPE_INVALID) && - check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result)); - g_value_array_free (bools); + ok = do_xmlrpc ("countBools", + g_variant_new ("(@ab)", g_variant_builder_end (&builder)), + "a{si}", &retval); if (!ok) return; - g_assert_true (soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)); - g_assert_true (soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)); - - g_hash_table_destroy (result); + g_assert_true (g_variant_lookup (retval, "true", "i", &ret_trues)); + g_assert_true (g_variant_lookup (retval, "false", "i", &ret_falses)); + g_assert_cmpint (g_variant_n_children (retval), ==, 2); + g_variant_unref (retval); debug_printf (2, "{ true: %d, false: %d }\n", ret_trues, ret_falses); g_assert_cmpint (trues, ==, ret_trues); @@ -188,12 +150,12 @@ test_countBools (void) static void test_md5sum (void) { - GByteArray *data, *result; + GByteArray *data; int i; GChecksum *checksum; guchar digest[16]; gsize digest_len = sizeof (digest); - GValue retval; + GVariant *retval; gboolean ok; SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; @@ -210,27 +172,30 @@ test_md5sum (void) g_checksum_get_digest (checksum, digest, &digest_len); g_checksum_free (checksum); - ok = (do_xmlrpc ("md5sum", &retval, - SOUP_TYPE_BYTE_ARRAY, data, - G_TYPE_INVALID) && - check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result)); + ok = do_xmlrpc ("md5sum", + g_variant_new ("(@ay)", + g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, + data->data, data->len, + TRUE, NULL, NULL)), + "ay", &retval); g_byte_array_free (data, TRUE); if (!ok) return; - soup_assert_cmpmem (result->data, result->len, + soup_assert_cmpmem (g_variant_get_data (retval), g_variant_get_size (retval), digest, digest_len); - g_byte_array_free (result, TRUE); + g_variant_unref (retval); } static void test_dateChange (void) { - GHashTable *structval; + GVariantDict structval; SoupDate *date, *result; char *timestamp; - GValue retval; + GVariant *retval; gboolean ok; + GError *error = NULL; SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; @@ -243,65 +208,69 @@ test_dateChange (void) g_random_int_range (0, 60), g_random_int_range (0, 60)); if (debug_level >= 2) { - timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC); - debug_printf (2, "date: %s, {", timestamp); - g_free (timestamp); + char *tmp; + + tmp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC); + debug_printf (2, "date: %s, {", tmp); + g_free (tmp); } - structval = soup_value_hash_new (); + g_variant_dict_init (&structval, NULL); #define MAYBE (g_random_int_range (0, 3) != 0) if (MAYBE) { date->year = 1970 + (g_random_int_range (0, 50)); debug_printf (2, "tm_year: %d, ", date->year - 1900); - soup_value_hash_insert (structval, "tm_year", - G_TYPE_INT, date->year - 1900); + g_variant_dict_insert (&structval, "tm_year", + "i", date->year - 1900); } if (MAYBE) { date->month = 1 + g_random_int_range (0, 12); debug_printf (2, "tm_mon: %d, ", date->month - 1); - soup_value_hash_insert (structval, "tm_mon", - G_TYPE_INT, date->month - 1); + g_variant_dict_insert (&structval, "tm_mon", + "i", date->month - 1); } if (MAYBE) { date->day = 1 + g_random_int_range (0, 28); debug_printf (2, "tm_mday: %d, ", date->day); - soup_value_hash_insert (structval, "tm_mday", - G_TYPE_INT, date->day); + g_variant_dict_insert (&structval, "tm_mday", + "i", date->day); } if (MAYBE) { date->hour = g_random_int_range (0, 24); debug_printf (2, "tm_hour: %d, ", date->hour); - soup_value_hash_insert (structval, "tm_hour", - G_TYPE_INT, date->hour); + g_variant_dict_insert (&structval, "tm_hour", + "i", date->hour); } if (MAYBE) { date->minute = g_random_int_range (0, 60); debug_printf (2, "tm_min: %d, ", date->minute); - soup_value_hash_insert (structval, "tm_min", - G_TYPE_INT, date->minute); + g_variant_dict_insert (&structval, "tm_min", + "i", date->minute); } if (MAYBE) { date->second = g_random_int_range (0, 60); debug_printf (2, "tm_sec: %d, ", date->second); - soup_value_hash_insert (structval, "tm_sec", - G_TYPE_INT, date->second); + g_variant_dict_insert (&structval, "tm_sec", + "i", date->second); } debug_printf (2, "} -> "); - ok = (do_xmlrpc ("dateChange", &retval, - SOUP_TYPE_DATE, date, - G_TYPE_HASH_TABLE, structval, - G_TYPE_INVALID) && - check_xmlrpc (&retval, SOUP_TYPE_DATE, &result)); - g_hash_table_destroy (structval); + ok = do_xmlrpc ("dateChange", + g_variant_new ("(vv)", + soup_xmlrpc_variant_new_datetime (date), + g_variant_dict_end (&structval)), + NULL, &retval); if (!ok) { soup_date_free (date); return; } + result = soup_xmlrpc_variant_get_datetime (retval, &error); + g_assert_no_error (error); + if (debug_level >= 2) { timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC); debug_printf (2, "%s\n", timestamp); @@ -317,81 +286,58 @@ test_dateChange (void) soup_date_free (date); soup_date_free (result); + g_variant_unref (retval); } static const char *const echo_strings[] = { "This is a test", "& so is this", "and so is <this>", - "& so is <this>" -}; -#define N_ECHO_STRINGS G_N_ELEMENTS (echo_strings) - -static const char *const echo_strings_broken[] = { - "This is a test", - " so is this", - "and so is this", - "amp; so is lt;thisgt;" + "& so is <this>", + NULL }; static void test_echo (void) { - GValueArray *originals, *echoes; - GValue retval; - int i; + GVariant *originals; + GVariant *retval; + char *str; SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; debug_printf (2, "echo (array of string -> array of string):\n"); - originals = g_value_array_new (N_ECHO_STRINGS); - for (i = 0; i < N_ECHO_STRINGS; i++) { - soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]); - debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]); - } - debug_printf (2, "] -> "); + originals = g_variant_new ("^as", echo_strings); + g_variant_ref_sink (originals); + str = g_variant_print (originals, TRUE); + debug_printf (2, "%s -> ", str); + g_free (str); - if (!(do_xmlrpc ("echo", &retval, - G_TYPE_VALUE_ARRAY, originals, - G_TYPE_INVALID) && - check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) { - g_value_array_free (originals); + if (!do_xmlrpc ("echo", + g_variant_new ("(@as)", originals), + "as", &retval)) { + g_variant_unref (originals); return; } - g_value_array_free (originals); - if (debug_level >= 2) { - for (i = 0; i < echoes->n_values; i++) { - debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", - g_value_get_string (&echoes->values[i])); - } - debug_printf (2, "]\n"); - } + str = g_variant_print (retval, TRUE); + debug_printf (2, "%s\n", str); + g_free (str); - g_assert_cmpint (echoes->n_values, ==, N_ECHO_STRINGS); + g_assert_true (g_variant_equal (originals, retval)); - for (i = 0; i < echoes->n_values; i++) { - if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0) { - g_test_skip ("PHP bug"); - g_value_array_free (echoes); - return; - } - - g_assert_cmpstr (echo_strings[i], ==, g_value_get_string (&echoes->values[i])); - } - - g_value_array_free (echoes); + g_variant_unref (originals); + g_variant_unref (retval); } static void test_ping (gconstpointer include_params) { - GValueArray *params; - GValue retval; + GVariant *retval; char *request; - char *out; gboolean ret; + GError *error = NULL; g_test_bug ("671661"); @@ -400,10 +346,8 @@ test_ping (gconstpointer include_params) debug_printf (2, "ping (void (%s) -> string)\n", include_params ? "empty <params>" : "no <params>"); - params = soup_value_array_new (); - request = soup_xmlrpc_build_method_call ("ping", params->values, - params->n_values); - g_value_array_free (params); + request = soup_xmlrpc_build_request ("ping", g_variant_new ("()"), &error); + g_assert_no_error (error); if (!request) return; @@ -419,15 +363,14 @@ test_ping (gconstpointer include_params) memmove (params, end, strlen (end) + 1); } - ret = send_xmlrpc (request, &retval); + ret = send_xmlrpc (request, "s", &retval); g_free (request); - if (!ret || !check_xmlrpc (&retval, G_TYPE_STRING, &out)) + if (!ret) return; - g_assert_cmpstr (out, ==, "pong"); - - g_free (out); + g_assert_cmpstr (g_variant_get_string (retval, NULL), ==, "pong"); + g_variant_unref (retval); } static void @@ -435,7 +378,6 @@ do_bad_xmlrpc (const char *body) { SoupMessage *msg; GError *err = NULL; - GValue retval; msg = soup_message_new ("POST", uri); soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, @@ -444,17 +386,17 @@ do_bad_xmlrpc (const char *body) soup_test_assert_message_status (msg, SOUP_STATUS_OK); - if (!soup_xmlrpc_parse_method_response (msg->response_body->data, - msg->response_body->length, - &retval, &err)) { - if (err) { + if (!soup_xmlrpc_parse_response (msg->response_body->data, + msg->response_body->length, + "()", &err)) { + if (err->domain == SOUP_XMLRPC_FAULT) { debug_printf (1, "FAULT: %d %s (OK!)\n", err->code, err->message); g_error_free (err); g_object_unref (msg); return; } else - soup_test_assert (FALSE, "ERROR: could not parse response\n"); + soup_test_assert (FALSE, "ERROR: could not parse response: %s\n", err->message); } else soup_test_assert (FALSE, "Unexpectedly got successful response!\n"); @@ -485,6 +427,272 @@ test_fault_args (void) do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); } +#define BODY_PREFIX \ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" \ + "<methodCall><methodName>MyMethod</methodName>" +#define BODY_SUFFIX \ + "</methodCall>\n" + +static void +verify_serialization (GVariant *value, + const char *expected_params) +{ + char *debug; + char *body; + char *params; + GError *error = NULL; + + debug = g_variant_print (value, TRUE); + + body = soup_xmlrpc_build_request ("MyMethod", value, &error); + g_assert_no_error (error); + g_assert (g_str_has_prefix (body, BODY_PREFIX)); + g_assert (g_str_has_suffix (body, BODY_SUFFIX)); + + params = g_strndup (body + strlen (BODY_PREFIX), + strlen (body) - strlen (BODY_PREFIX) + - strlen (BODY_SUFFIX)); + + if (!g_str_equal (params, expected_params)) + g_error ("Failed to serialize '%s':\n" + " expected: %s\n" + " got: %s\n", + debug, expected_params, params); + + g_free (params); + g_free (body); + g_free (debug); +} + +static void +verify_serialization_fail (GVariant *value) +{ + char *body; + GError *error = NULL; + + body = soup_xmlrpc_build_request ("MyMethod", value, &error); + g_assert (body == NULL); + g_assert (error != NULL); +} + +static void +test_serializer (void) +{ + SoupDate *date; + + verify_serialization (g_variant_new_parsed ("()"), + "<params/>"); + verify_serialization (g_variant_new_parsed ("(1, 2)"), + "<params>" + "<param><value><int>1</int></value></param>" + "<param><value><int>2</int></value></param>" + "</params>"); + verify_serialization (g_variant_new_parsed ("((1, 2),)"), + "<params><param><value><array><data>" + "<value><int>1</int></value>" + "<value><int>2</int></value>" + "</data></array></value></param></params>"); + verify_serialization (g_variant_new_parsed ("({'one', 1},)"), + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "</struct></value></param></params>"); + verify_serialization (g_variant_new_parsed ("([{'one', 1},{'two', 2}],)"), + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + verify_serialization (g_variant_new ("(^ay)", "bytestring"), + "<params><param>" + "<value><base64>Ynl0ZXN0cmluZwA=</base64></value>" + "</param></params>"); + verify_serialization (g_variant_new ("(y)", 42), + "<params>" + "<param><value><int>42</int></value></param>" + "</params>"); + date = soup_date_new_from_time_t (1434161309); + verify_serialization (g_variant_new ("(v)", soup_xmlrpc_variant_new_datetime (date)), + "<params>" + "<param><value><dateTime.iso8601>20150613T02:08:29</dateTime.iso8601></value></param>" + "</params>"); + soup_date_free (date); + verify_serialization (g_variant_new ("(s)", "<>&"), + "<params>" + "<param><value><string><>&</string></value></param>" + "</params>"); + verify_serialization (g_variant_new ("(u)", 0), + "<params>" + "<param><value><i8>0</i8></value></param>" + "</params>"); + + verify_serialization_fail (g_variant_new_parsed ("({1, 2},)")); + verify_serialization_fail (g_variant_new ("(mi)", NULL)); + verify_serialization_fail (g_variant_new ("(t)", 0)); +} + +static void +verify_deserialization (GVariant *expected_variant, + const char *signature, + const char *params) +{ + char *body; + char *method_name; + SoupXMLRPCParams *out_params = NULL; + GVariant *variant; + GError *error = NULL; + + body = g_strconcat (BODY_PREFIX, params, BODY_SUFFIX, NULL); + method_name = soup_xmlrpc_parse_request (body, strlen (body), + &out_params, + &error); + g_assert_no_error (error); + g_assert_cmpstr (method_name, ==, "MyMethod"); + + variant = soup_xmlrpc_params_parse (out_params, signature, &error); + g_assert_no_error (error); + + if (!g_variant_equal (variant, expected_variant)) { + char *str1, *str2; + + str1 = g_variant_print (expected_variant, TRUE); + str2 = g_variant_print (variant, TRUE); + g_error ("Failed to deserialize '%s':\n" + " expected: %s\n" + " got: %s\n", + params, str1, str2); + g_free (str1); + g_free (str2); + } + + soup_xmlrpc_params_free (out_params); + g_variant_unref (variant); + g_free (method_name); + g_free (body); +} + +static void +verify_deserialization_fail (const char *signature, + const char *params) +{ + char *body; + char *method_name; + SoupXMLRPCParams *out_params = NULL; + GVariant *variant; + GError *error = NULL; + + body = g_strconcat (BODY_PREFIX, params, BODY_SUFFIX, NULL); + method_name = soup_xmlrpc_parse_request (body, strlen (body), + &out_params, + &error); + g_assert_no_error (error); + g_assert_cmpstr (method_name, ==, "MyMethod"); + + variant = soup_xmlrpc_params_parse (out_params, signature, &error); + g_assert_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS); + g_assert (variant == NULL); + + g_free (body); + soup_xmlrpc_params_free (out_params); +} + +static void +test_deserializer (void) +{ + char *tmp; + SoupDate *date; + + verify_deserialization (g_variant_new_parsed ("@av []"), + NULL, + "<params/>"); + verify_deserialization (g_variant_new_parsed ("()"), + "()", + "<params/>"); + verify_deserialization (g_variant_new_parsed ("(@y 1,@n 2)"), + "(yn)", + "<params>" + "<param><value><int>1</int></value></param>" + "<param><value><int>2</int></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("[<[{'one', <1>},{'two', <2>}]>]"), + NULL, + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + verify_deserialization (g_variant_new_parsed ("([{'one', 1},{'two', 2}],)"), + "(a{si})", + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + date = soup_date_new_from_time_t (1434146909); + verify_deserialization (g_variant_new_parsed ("[%v]", soup_xmlrpc_variant_new_datetime (date)), + NULL, + "<params>" + "<param><value><dateTime.iso8601>20150612T22:08:29</dateTime.iso8601></value></param>" + "</params>"); + soup_date_free (date); + verify_deserialization (g_variant_new_parsed ("[<b'bytestring'>]"), + NULL, + "<params>" + "<param><value><base64>Ynl0ZXN0cmluZwA=</base64></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("[<1>]"), + "av", + "<params><param><value><int>1</int></value></param></params>"); + verify_deserialization (g_variant_new_parsed ("[<%s>]", "<>&"), + NULL, + "<params>" + "<param><value><string><>&</string></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("(@y 255,)"), + "(y)", + "<params>" + "<param><value><int>255</int></value></param>" + "</params>"); + + tmp = g_strdup_printf ("<params>" + "<param><value><int>%"G_GUINT64_FORMAT"</int></value></param>" + "</params>", G_MAXUINT64); + verify_deserialization (g_variant_new ("(t)", G_MAXUINT64), + "(t)", tmp); + g_free (tmp); + + verify_deserialization_fail (NULL, + "<params>" + "<param><value><boolean>2</boolean></value></param>" + "</params>"); + verify_deserialization_fail ("(y)", + "<params>" + "<param><value><int>256</int></value></param>" + "</params>"); + verify_deserialization_fail ("(ii)", + "<params>" + "<param><value><int>1</int></value></param>" + "</params>"); + verify_deserialization_fail ("(i)", + "<params>" + "<param><value><int>1</int></value></param>" + "<param><value><int>2</int></value></param>" + "</params>"); +} + +static void +test_fault (void) +{ + char *body; + GVariant *reply; + GError *error = NULL; + + body = soup_xmlrpc_build_fault (1, "error: %s", "failed"); + reply = soup_xmlrpc_parse_response (body, strlen (body), NULL, &error); + g_assert_error (error, SOUP_XMLRPC_FAULT, 1); + g_assert_cmpstr (error->message, ==, "error: failed"); + g_assert (reply == NULL); + + g_free (body); + g_clear_error (&error); +} + static GOptionEntry xmlrpc_entries[] = { { "uri", 'U', 0, G_OPTION_ARG_STRING, &uri, "Alternate URI for server", NULL }, @@ -507,16 +715,19 @@ main (int argc, char **argv) session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - g_test_add_func ("/xmlrpc/sum", test_sum); - g_test_add_func ("/xmlrpc/countBools", test_countBools); - g_test_add_func ("/xmlrpc/md5sum", test_md5sum); - g_test_add_func ("/xmlrpc/dateChange", test_dateChange); - g_test_add_func ("/xmlrpc/echo", test_echo); - g_test_add_data_func ("/xmlrpc/ping/empty-params", GINT_TO_POINTER (TRUE), test_ping); - g_test_add_data_func ("/xmlrpc/ping/no-params", GINT_TO_POINTER (FALSE), test_ping); - g_test_add_func ("/xmlrpc/fault/malformed", test_fault_malformed); - g_test_add_func ("/xmlrpc/fault/method", test_fault_method); - g_test_add_func ("/xmlrpc/fault/args", test_fault_args); + g_test_add_func ("/xmlrpc/variant/serializer", test_serializer); + g_test_add_func ("/xmlrpc/variant/deserializer", test_deserializer); + g_test_add_func ("/xmlrpc/variant/fault", test_fault); + g_test_add_func ("/xmlrpc/variant/sum", test_sum); + g_test_add_func ("/xmlrpc/variant/countBools", test_countBools); + g_test_add_func ("/xmlrpc/variant/md5sum", test_md5sum); + g_test_add_func ("/xmlrpc/variant/dateChange", test_dateChange); + g_test_add_func ("/xmlrpc/variant/echo", test_echo); + g_test_add_data_func ("/xmlrpc/variant/ping/empty-params", GINT_TO_POINTER (TRUE), test_ping); + g_test_add_data_func ("/xmlrpc/variant/ping/no-params", GINT_TO_POINTER (FALSE), test_ping); + g_test_add_func ("/xmlrpc/variant/fault/malformed", test_fault_malformed); + g_test_add_func ("/xmlrpc/variant/fault/method", test_fault_method); + g_test_add_func ("/xmlrpc/variant/fault/args", test_fault_args); ret = g_test_run (); |